10月阅读周·JavaScript异步编程设计快速响应的网络应用:Async.js的任务组织技术
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读九个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》。
当前阅读周书籍:《JavaScript异步编程设计快速响应的网络应用》。
Async.js的任务组织技术
异步函数序列的运行
假设我们希望某一组异步函数能依次运行。在不使用工具函数的情况下,可能会编写出类似这样的代码:
funcs[0](function () {
funcs[1](function () {
funcs[2](onComplete);
});
});
幸好我们还有 async.series 和 async.waterfall。这两个方法均接受一组函数(即任务列表)作为参数并按顺序运行它们,二者给任务列表中的每个函数均传递一个 Node 风格的回调。async.series与 async.waterfall 之间的差别是,前者提供给各个任务的只有回调,而后者还会提供任务列表中前一任务的结果。(所谓“结果”,指的是各个任务传递给其回调的非错误的值。)
我们来看一个用延时实现的简单例子。
Asyncjs/seriesTimers.js
var async = require('async');
var start = new Date();
async.series(
[
function (callback) {
setTimeout(callback, 100);
},
function (callback) {
setTimeout(callback, 300);
},
function (callback) {
setTimeout(callback, 200);
},
],
function (err, results) {
// 显示自start 而流逝的时间
console.log('Completed in ' + (new Date() - start) + 'ms');
},
);
(将 async.series 替换为 async.waterfall 不会对这个例子造成任何影响,因为这里各个任务的回调在运行时均不带参数。)
因为任务列表中的各个任务会按顺序完成,所以会在 600 毫秒之后(实际上比 600毫秒稍长一些)运行完工回调(即因完成整个工作流事件而调用的回调,又称完工事件处理器)。Async.js传递给任务列表中每个函数的回调好像在说:“出错了吗(回调的首参数是否为错误)?如果没出错,我就要收集结果(回调的次参数)并运行下一个任务了。”
下次再遇到一组需要按顺序运行的异步函数时,请试试async.series或async.waterfall吧。这二者中的一个很可能是最适合完成工作的工具。
异步函数的并行运行
Async.js提供了async.series的并行版本,即async.parallel。就像 async.series 一样,async.parallel 也接受一组形为function(callback) {……}的函数作为参数,但会再加上一个(可选的)在触发最末回调后运行的完工事件处理器。
前面那个延时例子重写如下。
Asyncjs/parallelTimers.js
var async = require('async');
var start = new Date();
async.parallel(
[
function (callback) {
setTimeout(callback, 100);
},
function (callback) {
setTimeout(callback, 300);
},
function (callback) {
setTimeout(callback, 200);
},
],
function (err, results) {
console.log('Completed in ' + (new Date() - start) + 'ms');
},
);
async.series完成工作流需要用掉3次延时的总和(约600毫秒),而async.parallel的用时只是最长的那次延时(约300毫秒)。
总结
更为便利的是,Async.js按照任务列表的次序向完工事件处理器传递结果,而不是按照生成这些结果的次序。这样,我们既拥有了并行机制的性能优势,又没有失去结果的可预知性。
async.series、async.waterfall、async.parallel 与那些数据收集方法一起,诠释了Async.js的内核与灵魂:为最常见的异步情景提供简单又省时的工具函数。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)