Async详解之一:流程控制

2023-11-11

转载:http://freewind.me/blog/20120515/917.html

为了适应异步编程,减少回调的嵌套,我尝试了很多库。最终觉得还是async最靠谱。

地址:https://github.com/caolan/async

Async的内容分为三部分:

  1. 流程控制:简化十种常见流程的处理
  2. 集合处理:如何使用异步操作处理集合中的数据
  3. 工具类:几个常用的工具类

本文介绍其中最简单最常用的流程控制部分。

由于nodejs是异步编程模型,有一些在同步编程中很容易做到的事情,现在却变得很麻烦。Async的流程控制就是为了简化这些操作。

1. series(tasks, [callback]) (多个函数依次执行,之间没有数据交换)

有多个异步函数需要依次调用,一个完成之后才能执行下一个。各函数之间没有数据的交换,仅仅需要保证其执行顺序。这时可使用series。

纯js代码:

step1(function(err, v1) {
  step2(function(err, v2) {
    step3(function(err, v3) {
       // do somethig with the err or values v1/v2/v3
    }
  }
});

从中可以看到这嵌套还是比较多深的,如果再多几步,会更深。在代码中忽略对了每一层err的处理,否则还都等加上 if(err) return callback(err),那就更麻烦了。

对于这种情况,使用async来处理,就是这样的:

var async = require('async')
async.series([
   step1, step2, step3
], function(err, values) {
   // do somethig with the err or values v1/v2/v3
});

可以看到代码简洁了很多,而且自动处理每个回调中的错误。当然,这里只给出来最最简单的例子,在实际中,我们常会在每个step中执行一些操作,这时可写成:

var async = require('async')
async.series([
  function(cb) { step1(function(err,v1) {
     // do something with v1
     cb(err, v1);
  }),
  function(cb) { step2(...) },
  function(cb) { step3(...) }
], function(err, values) {
// do somethig with the err or values v1/v2/v3
});

该函数的详细解释为:

  1. 依次执行一个函数数组中的每个函数,每一个函数执行完成之后才能执行下一个函数。
  2. 如果任何一个函数向它的回调函数中传了一个error,则后面的函数都不会被执行,并且将会立刻会将该error以及已经执行了的函数的结果,传给series中最后那个callback。
  3. 当所有的函数执行完后(没有出错),则会把每个函数传给其回调函数的结果合并为一个数组,传给series最后的那个callback。
  4. 还可以json的形式来提供tasks。每一个属性都会被当作函数来执行,并且结果也会以json形式传给series最后的那个callback。这种方式可读性更高一些。

具体例子可参考:https://github.com/freewind/async_demo/blob/master/series.js

其代码中还包含了:

  1. 如果中间某个函数出错,series函数如何处理
  2. 如果某个函数传给回调的值为undefined, null, {}, []等,series如何处理

另外还需要注意的是:多个series调用之间是不分先后的,因为series本身也是异步调用。

2. parallel(tasks, [callback]) (多个函数并行执行)

并行执行多个函数,每个函数都是立即执行,不需要等待其它函数先执行。传给最终callback的数组中的数据按照tasks中声明的顺序,而不是执行完成的顺序。

如果某个函数出错,则立刻将err和已经执行完的函数的结果值传给parallel最终的callback。其它未执行完的函数的值不会传到最终数据,但要占个位置。

同时支持json形式的tasks,其最终callback的结果也为json形式。

示例代码:

async.parallel([
    function(cb) { t.fire('a400', cb, 400) },
    function(cb) { t.fire('a200', cb, 200) },
    function(cb) { t.fire('a300', cb, 300) }
], function (err, results) {
    log('1.1 err: ', err); // -> undefined
    log('1.1 results: ', results); // ->[ 'a400', 'a200', 'a300' ]
});

中途出错的示例:

async.parallel([
    function(cb) { log('1.2.1: ', 'start'); t.fire('a400', cb, 400) }, // 该函数的值不会传给最终callback,但要占个位置
    function(cb) { log('1.2.2: ', 'start'); t.err('e200', cb, 200) },
    function(cb) { log('1.2.3: ', 'start'); t.fire('a100', cb, 100) }
], function(err, results) {
    log('1.2 err: ', err); // -> e200
    log('1.2 results: ', results); // -> [ , undefined, 'a100' ]
});

以json形式传入tasks

async.parallel({
    a: function(cb) { t.fire('a400', cb, 400) },
    b: function(cb) { t.fire('c300', cb, 300) }
}, function(err, results) {
    log('1.3 err: ', err); // -> undefined
    log('1.3 results: ', results); // -> { b: 'c300', a: 'a400' }
});

更详细示例参见:https://github.com/freewind/async_demo/blob/master/parallel.js

3. waterfall(tasks, [callback]) (多个函数依次执行,且前一个的输出为后一个的输入)

与seires相似,按顺序依次执行多个函数。不同之处,每一个函数产生的值,都将传给下一个函数。如果中途出错,后面的函数将不会被执行。错误信息以及之前产生的结果,将传给waterfall最终的callback。

这个函数名为waterfall(瀑布),可以想像瀑布从上到下,中途冲过一层层突起的石头。注意,该函数不支持json格式的tasks。

async.waterfall([
    function(cb) { log('1.1.1: ', 'start'); cb(null, 3); },
    function(n, cb) { log('1.1.2: ',n); t.inc(n, cb); },
    function(n, cb) { log('1.1.3: ',n); t.fire(n*n, cb); }
], function (err, result) {
    log('1.1 err: ', err); // -> null
    log('1.1 result: ', result); // -> 16
});

更详细示例参见:https://github.com/freewind/async_demo/blob/master/waterfall.js

4. auto(tasks, [callback]) (多个函数有依赖关系,有的并行执行,有的依次执行)

用来处理有依赖关系的多个任务的执行。比如某些任务之间彼此独立,可以并行执行;但某些任务依赖于其它某些任务,只能等那些任务完成后才能执行。

虽然我们可以使用async.parallel和async.series结合起来实现该功能,但如果任务之间关系复杂,则代码会相当复杂,以后如果想添加一个新任务,也会很麻烦。这时使用async.auto,则会事半功倍。

如果有任务中途出错,则会把该错误传给最终callback,所有任务(包括已经执行完的)产生的数据将被忽略。

这里假设我要写一个程序,它要完成以下几件事:

  1. 从某处取得数据
  2. 在硬盘上建立一个新的目录
  3. 将数据写入到目录下某文件
  4. 发送邮件,将文件以附件形式发送给其它人。

分析该任务,可以知道1与2可以并行执行,3需要等1和2完成,4要等3完成。

async.auto({
    getData: function (callback) {
        setTimeout(function(){
            console.log('1.1: got data');
            callback();
        }, 300);
    },
    makeFolder: function (callback) {
        setTimeout(function(){
            console.log('1.1: made folder');
            callback();
        }, 200);
    },
    writeFile: ['getData', 'makeFolder', function(callback) {
        setTimeout(function(){
            console.log('1.1: wrote file');
            callback(null, 'myfile');
        }, 300);
    }],
    emailFiles: ['writeFile', function(callback, results) {
        log('1.1: emailed file: ', results.writeFile); // -> myfile
        callback(null, results.writeFile);
    }]
}, function(err, results) {
    log('1.1: err: ', err); // -> null
    log('1.1: results: ', results); // -> { makeFolder: undefined,
                                    //      getData: undefined,
                                    //      writeFile: 'myfile',
                                    //      emailFiles: 'myfile' }
});

更多详细示例参见:https://github.com/freewind/async_demo/blob/master/auto.js

5. whilst(test, fn, callback)(用可于异步调用的while)

相当于while,但其中的异步调用将在完成后才会进行下一次循环。举例如下:

var count1 = 0;
async.whilst(
    function() { return count1 < 3 },
    function(cb) {
        log('1.1 count: ', count1);
        count1++;
        setTimeout(cb, 1000);
    },
    function(err) {
        // 3s have passed
        log('1.1 err: ', err); // -> undefined
    }
);

它相当于:

try {
  whilst(test) {
    fn();
  }
  callback();
} catch (err) {
  callback(err);
}

该函数的功能比较简单,条件变量通常定义在外面,可供每个函数访问。在循环中,异步调用时产生的值实际上被丢弃了,因为最后那个callback只能传入错误信息。

另外,第二个函数fn需要能接受一个函数cb,这个cb最终必须被执行,用于表示出错或正常结束。

更详细示例参见:https://github.com/freewind/async_demo/blob/master/whilst_until.js

6. until(test, fn, callback) (与while相似,但判断条件相反)

var count4 = 0;
async.until(
    function() { return count4>3 },
    function(cb) {
        log('1.4 count: ', count4);
        count4++;
        setTimeout(cb, 200);
    },
    function(err) {
        // 4s have passed
        log('1.4 err: ',err); // -> undefined
    }
);

当第一个函数条件为false时,继续执行第二个函数,否则跳出。

7. queue (可设定worker数量的队列)

queue相当于一个加强版的parallel,主要是限制了worker数量,不再一次性全部执行。当worker数量不够用时,新加入的任务将会排队等候,直到有新的worker可用。

该函数有多个点可供回调,如worker用完时、无等候任务时、全部执行完时等。

定义一个queue,其worker数量为2,并在任务执行时,记录一下日志:

var q = async.queue(function(task, callback) {
    log('worker is processing task: ', task.name);
    task.run(callback);
}, 2);

worker数量将用完时,会调用saturated函数:

q.saturated = function() {
    log('all workers to be used');
}

当最后一个任务交给worker执行时,会调用empty函数

q.empty = function() {
    log('no more tasks wating');
}

当所有任务都执行完时,会调用drain函数

q.drain = function() {
    console.log('all tasks have been processed');
}

放入多个任务,可一次放一个,或一次放多个

q.push({name:'t1', run: function(cb){
    log('t1 is running, waiting tasks: ', q.length());
    t.fire('t1', cb, 400); // 400ms后执行
}}, function(err) {
    log('t1 executed');
});
q.push([{name:'t3', run: function(cb){
    log('t3 is running, waiting tasks: ', q.length());
    t.fire('t3', cb, 300); // 300ms后执行
}},{name:'t4', run: function(cb){
    log('t4 is running, waiting tasks: ', q.length());
    t.fire('t4', cb, 500); // 500ms后执行
}}], function(err) {
    log('t3/4 executed');
});

更多详细示例参见:https://github.com/freewind/async_demo/blob/master/queue.js

8. iterator(tasks) (将几个函数包装为iterator)

将一组函数包装成为一个iterator,可通过next()得到以下一个函数为起点的新的iterator。该函数通常由async在内部使用,但如果需要时,也可在我们的代码中使用它。

var iter = async.iterator([
    function() { console.log('111') },
    function() { console.log('222') },
    function() { console.log('333') }
]);
console.log(iter());
console.log(iter.next());

直接调用(),会执行当前函数,并返回一个由下个函数为起点的新的iterator。调用next(),不会执行当前函数,直接返回由下个函数为起点的新iterator。

对于同一个iterator,多次调用next(),不会影响自己。如果只剩下一个元素,调用next()会返回null。

更详细示例参见:https://github.com/freewind/async_demo/blob/master/iterator.js

9. apply(function, arguments..) (给函数预绑定参数)

apply是一个非常好用的函数,可以让我们给一个函数预绑定多个参数并生成一个可直接调用的新函数,简化代码。

对于函数:

function(callback) { t.inc(3, callback); }

可以用apply改写为:

async.apply(t.inc, 3);

还可以给某些函数预设值,得到一个新函数:

var log = async.apply(console.log, ">");
log('hello');
// > hello

更详细代码参见:https://github.com/freewind/async_demo/blob/master/apply.js

10. nextTick(callback) (在nodejs与浏览器两边行为一致)

nextTick的作用与nodejs的nextTick一样,都是把某个函数调用放在队列的尾部。但在浏览器端,只能使用setTimeout(callback,0),但这个方法有时候会让其它高优先级的任务插到前面去。

所以提供了这个nextTick,让同样的代码在服务器端和浏览器端表现一致。

var calls = [];
async.nextTick(function() {
    calls.push('two');
});
calls.push('one');
async.nextTick(function() {
    console.log(calls); // -> [ 'one', 'two' ]
});

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Async详解之一:流程控制 的相关文章

  • nodejs笔记之:事件驱动,线程池,非阻塞,异常处理等

    nodejs笔记之 事件驱动 线程池 非阻塞 异常处理等 2016年05月01日 14 44 42 Johnny丶me 阅读数 4112更多 分类专栏 NodeJs Nodejs 硬实战 全栈工程师笔记 版权声明 本文为博主原创文章 遵循
  • node.js HTTP模块 URL模块 supervisor工具

    在vscode 上安装一个插件 Node Snippets 创建一个js文件 直接输入node 就能创建一个服务器 node http server var http require http 表示引入一个HTTP模块 request 表示
  • 【node】10、express模块搭建服务

    express模块是一个外部引入模块 不是node内部自身的模块 所以需要下载express模块才能引入 下载express之前需要初始化项目文件 npm init y 初始化后安装express npm install express 安
  • Node.js中的断言处理

    在Node js中 可以利用assert模块进行断言处理 即书写一些判断用测试代码 如果判断结果为假 则抛出AssertionError异常 判断两个值是否相等 assert equal actual expected message ac
  • 如何在 MacOS 上安装 Node.js

    Node js 是一个强大的 JavaScript 运行时环境 允许您在服务器端执行 JavaScript 它广泛用于开发可扩展的 Web 应用程序 命令行工具等 在本文中 我们将介绍在 macOS 系统上安装 Node js 的过程 先决
  • Node处理csv文件(利用csv-parse读取指定字符串内的文件片段)

    需求 读取一个csv文件 需要从指定字符串开始读取至指定字符串结束 由于csv文件可能使用不同环境打开编辑过 末尾包含很多多余的分隔逗号 去除这些逗号 使用 Stream const parse require csv parse cons
  • node.js和npm的关系

    一 npm是什么 当一个网站依赖的js代码越来越多 程序员发现这是一件很麻烦的事情 去 jQuery 官网下载 jQuery 去 BootStrap 官网下载 BootStrap 去 Underscore 官网下载 Underscore 有
  • nodejs,zip压缩版安装与配置

    下载zip包 下载 Node js 解压 将文件解压到要安装的位置 并新建两个目录node global npm全局安装位置node cache npm 缓存路径 配置环境变量 NODE PATH D Program Files node
  • node写可选参数接口

    个人网站 紫陌 笔记分享网 想寻找共同学习交流 共同成长的伙伴 请点击 前端学习交流群 今天写项目接口看到接口文档要求带四个参数两个参数必选两个可选 当时在想可选参数要怎么做 毕竟自己也没有写过 然后想了一天终于想出一个感觉不是最佳的方案
  • mongodb 关于 整数类型 和 字符串类型 索引的比较

    想看看到底是 整数类型的索引快呢 还是字符串类型的索引快 到底快多少呢 内存分别占多少呢 今天就来测试一下 配置 华硕飞行堡垒6 500G 的 SSD 准备数据 a 是 for 循环的变量 id a name abcdefg a 2千万的数
  • Node.js搭建WEB服务器

    Node js搭建WEB服务器 1 安装Node和nodemon插件 2 引入http模块 3 创建服务监听端口 4 解析接口地址 5 解析get参数 6 解析post参数 1 安装Node和nodemon插件 全局安装nodemon插件
  • 前端页面生成PDF方案之puppetter

    1 新建一个文件夹 例如 test 2 新建一个js文件 例如test js 3 进入test文件夹 在该目录下运行命令行 并执行下面命令 npm init 4 运行命令安装 npm i puppetter 5 编辑test js cons
  • 在node中使用es7

    今天学写了体验异步的终极解决方案 ES7的Async Await这篇文章 发现作者是用 es7 的语法写 node 所以顺便学习了一下如何在 node 中使用 es7 的语法 记录一下 首先安装 babel cli yarn add bab
  • Angular --官方文档使用 Angular CLI创建项目

    1 在node环境下 标配 node v npm v 指令在命令窗口查看两个的版本号 node高于6 9 3 npm版本高于3 0 0 如果你是新安装的话 就不要考虑这个问题了 2 全局安装 Angular CLI npm install
  • Nginx常用命令以及升级(window)

    nginx Windows作为标准控制台应用程序 不是服务 运行 可以使用以下命令对其进行管理 start nginx 启动Nginx nginx s stop fast shutdown 快速停止 nginx s quit gracefu
  • node连接mysql实现带分页列表多条件模糊查询效果-新手教程

    前言 使用node连接mysql 这里是模糊查询方法完整流程 第一 安装我们的express脚手架 入口 第二 在根目录下创建model文件夹 里面放两个文件 1 mysql config js 这里放的是我们的数据库配置 配置链接数据库参
  • Express初学之入门

    1 Express简介 基于Node的极简MVC框架 提供灵活的路由功能 提供强大的中间件机制 内核极小 扩展性很好 使用广泛 2 路由机制 Express概念 定义了Api的地址 请求 响应方式 方法 天然支持HTTP Method 中的
  • Node之使用dns模块解析域名

    引 在网络编程中 开发者更倾向于使用域名 而不是IP地址来指定网络连接的目标地址 在Node js中 提供dns模块 以实现域名查找及域名解析的处理 在dns模块中 提供了三个主方法及一系列便捷方法 其中三个主方法分别为用于将一个域名解析为
  • Node.js爬虫实战:搜狗图片爬取

    说在前面 当我们在网上寻找图片时 经常会遇到需要批量下载搜索结果中的图片的情况 而搜狗作为中国颇具影响力的搜索引擎之一 其图片搜索功能提供了丰富多样的图片资源 在这种情况下 我们希望能够通过编程的方式 批量下载搜狗图片搜索结果中的图片 以便
  • Node.js 工作线程与子进程:应该使用哪一个

    Node js 工作线程与子进程 应该使用哪一个 并行处理在计算密集型应用程序中起着至关重要的作用 例如 考虑一个确定给定数字是否为素数的应用程序 如果我们熟悉素数 我们就会知道必须从 1 遍历到该数的平方根才能确定它是否是素数 而这通常非

随机推荐

  • Windows7不再卡五叶草,更新了bootx64.efi和bootmgfw.efi文件,支持安全启动,不用关闭安全启动和打开csm支持.

    如题 微软最近为Windows7的64位系统更新了bootx64 efi和bootmgfw efi文件 然后Windows7的64位系统就支持了更新的平台 而且支持安全启动 不用关闭安全启动和打开csm支持 已在Intel10代和12代平台
  • Gitlab----设置页面语言为简体中文

    原文链接 1 简介 gitlab为每个用户提供了个性化设置 即用户登录之后设置页面的语言之后 并不影响其他人的页面的语言 如此就解决了有的人喜欢看中文界面 有的人喜欢看英文界面 当然如果会其他国语言也可以同样设置自己喜欢的语言 2 用户登录
  • Unity动画控制

    1 Trigger控制动画状态重复播放
  • 判断一个对象是否存在某个属性

    一 OBJ hasOwnProperty 属性 返回一个布尔值 只判断该对象自身的属性 不会去查找原型链上的属性 二 OBJ 或者 OBJ 去访问该属性 如果不存在就会返回undefined 这个方法可以查找原型链上的属性 这里有一个特殊情
  • ssh免密登录,各种权限设置都无效的解决办法!

    如果失败 有可能是以下原因 1 权限问题 这个还得试试 有可能你的权限真的不对 ssh目录 以及 home 当前用户 需要700权限 参考以下操作调整 sudo chmod 700 ssh sudo chmod 700 home 当前用户
  • Python打乱数据集

    在随机划分训练集和测试集时 需要随机划分 使用这种方法 一 打乱索引 import random random seed seed index i for i in range len x train random shuffle inde
  • linux定时删除指定文件shell脚本

    vi delete sh 输入以下内容 bin bash data path home test logs expire time 1 function deletefiles for file in find 1 name Default
  • vscode安装node-snippets

    vscode安装node snippets扩展后没有代码提示问题 安装好扩展后 输入node后仍没有代码提示 需要观察自己的代码所在文件路径是否全是英文路径 然后大多都以了 如果还有不行 就尝试在设置里面输入prevent 取消下面的选中
  • windows下如何安装高版本的jaxlib

    https zhuanlan zhihu com p 498404068
  • Vue2手捏Vuex插件

    一 展示数据 state 1 plugins Vuex js let Vue class Store constructor options 1 保存数据 通过 vue 中的 data 是响应式 this vm new Vue data s
  • C语言百日刷题第九天

    前言 今天是刷题第9天 放弃不难 但坚持一定很酷 快来跟我一起刷题吧 加油 一起努力进步 C语言百日刷题第九天 前言 76 计算偶数的所有质因子 77 提取不重复的整数 78 二进制中1的个数 79 猴子分桃 80 百钱买百鸡 76 计算偶
  • Typora-Markdown 文本编辑器

    图片 md test png img src md test png height 300 时序图 实线 gt gt gt 虚线 gt gt gt 背景 rect rgb 244 244 255 end 列子 mermaid sequenc
  • SSM商城项目实战:客户管理

    SSM商城项目实战 客户管理 1 项目背景和需求分析 在一个电商平台中 客户管理是非常重要的一环 通过对客户信息的管理 可以更好地了解客户需求 提供个性化的服务 增强客户满意度 本项目旨在实现一个简单的客户管理功能 包括客户信息的添加 修改
  • react组件权限控制+路由权限+路由懒加载

    组件控制 permission js import React useEffect useState useContext from react const PermissionContext React createContext per
  • [~/vulhub]/log4j/CVE-2021-44228-20221225

    vulhub log4j CVE 2021 44228 kwkl kwkl vulhub cd log4j CVE 2021 44228 kwkl kwkl vulhub log4j CVE 2021 44228
  • flutter 对象模型的理解

    如果要传值 传一个int值时就很简单了 类型就是int 如果我们要传好多个值呢 里面有int 也有String 也会有列表 对象 当然我们也可以一个个传 问题是这样子很麻烦 我们可以将这些不同的类型数据堆到一个模型里面 然后传这个模型 也就
  • 为了搞清楚CDN的原理,我头都秃了...

    点击上方 前端Q 关注公众号 回复加群 加入前端Q技术交流群 作者 winty的帆宝宝 原文地址 https juejin cn post 6944420222757503006 今天下班前忽然发现维护的项目网站打不开了 打开控制台一看 原
  • C++--运算符

    运算符是一种告诉编译器执行特定的数学或逻辑操作的符号 C 内置了丰富的运算符 并提供了以下类型的运算符 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 杂项运算符 本章将逐一介绍算术运算符 关系运算符 逻辑运算符 位运算符 赋值运
  • Python的turtle库(教师节祝福,用turtle画一只熊猫和一个太阳)

    目录 一 基本功能 1 turtle库的官方文档 2 开始绘制 3 绘制结果 4 常用的turtle库的方法 一 基本功能 1 turtle库的官方文档 官方文档 2 开始绘制 import turtle as t import math
  • Async详解之一:流程控制

    转载 http freewind me blog 20120515 917 html 为了适应异步编程 减少回调的嵌套 我尝试了很多库 最终觉得还是async最靠谱 地址 https github com caolan async Asyn