Node.js异步控制流:回调、事件、Promise和async/await
来源:互联网 发布:解放军土鳖 知乎 编辑:程序博客网 时间:2024/05/21 08:02
前言
对于Node.js的异步控制流,目前共计有四种常用的方式。较为经典的为callback
和EventEmitter
;在ES6中,加入了Promise
;在ES7中加入了async/await
。下面就逐个分析一下这四种常用的异步控制。
callback形式的异步控制
对于callback
形式,即采用回调函数。在理解上,就是函数将任务分配出去,当任务完成之后,然后根据执行结果来进行相应的回调。可以看下面一个例子:
function sleep(ms, callback){ setTimeout(function(){ callback("finish") //执行完之后,返回‘finish’。 }, ms);}sleep(1000, function(val){ console.log(val);});//输出结果:finish。
这种形式十分容易理解,但是能够解决大部分的问题。但是却存在着致命的缺点。可以想象,如果需要在一段代码中调用多次sleep()
函数,将会出现下面的情况:
var i = 0;//记录sleep()函数调用的次数function sleep(ms, callback){ setTimeout(function(){ if(i < 2){ i++; callback("finish", null); }else{ callback(null, new Error('i大于2')); } }, ms);}//第一次调用sleep(1000, function (val, err) { if (err) console.log(err.message); else { console.log(val); //第二次调用 sleep(1000, function (val, err) { if (err) console.log(err.message); else { console.log(val); //第三次调用 sleep(1000, function (val, err) { if (err) console.log(err.message); else { console.log(val); } }); } }); }});//输出结果分别为:finish,finish,i大于2。
由上面的例子可以看出,假如需要多次调用sleep()
函数时,就会进行多次的嵌套。这种嵌套虽然可以使用,但是在代码的阅读、维护和美观上面,都是反人类出现的。所以,在新标准ES6和ES7发布之后,将会逐渐摒弃这种写法。
事件监听形式的异步控制
对于callback
的改进就是使用事件监听的形式进行操作:每次调用异步函数都会返回一个EvetEmitter
对象。在函数内部,可以根据需求来触发不同的事件。对不同的事件进行监听,然后做出相应的处理。具体代码如下:
var i = 0;var events = require('events');var emitter = new events.EventEmitter();//创建事件监听器的一个对象function sleep(ms) { setTimeout(function () { i++; if (i < 2) { emitter.emit('done', 'finish');//触发事件'done' } else { emitter.emit('error', new Error('i大于2'));//触发事件'error' } }, ms);}var emit = sleep(1000);//监听事件'done'emitter.on('done', function(val){ console.log(val);});//监听事件'error'emitter.on('error', function(val){ console.log(val);});
通过事件监听的形式进行异步处理,可以更加直观和多样性。但是和callback
类似,并没有解决嵌套的问题。例如需要调用多次sleep()
函数,依然需要在函数emitter.on('done', function(val))
中进行嵌套。
Promise对象进行异步控制处理
比较Promise和EventEmitter,可以发现两个十分明显的区别。对于EventEmitter,我们可以根据自己的需求来定义多种监听事件,然后对不同的事件进行不同的处理;对于promise,则可以对将异步操作的结果进行返回,然后根据返回值来进行相应的操作。对于promise的详细学习,可以通过在MDN查阅详细且最新的资料。
代码如下:
let num = 1function sleep(ms) { //返回一个Promise对象 return new Promise(function (resolev, reject) { setTimeout(function () { if (num < 1) { num++ //如果成功,对resolve处理 resolev(new Error('finish')) } else { //如果失败,对reject操作 reject('i大于2') } }, ms) })}sleep(300).then(value => { console.log(value) return sleep(300)}).then(value => { console.log(value) return sleep(300)}).then(value => { console.log(value) return sleep(300)}).catch(function (error) { console.log(error)})
和callback与EventEmmitter相比,Promise将异步处理的嵌套函数进行了展开,更利于阅读和理解。当promise链中的任意一个函数出错都会直接抛出到链的最底部,所以我们统一用了一个catch
去捕获,每次promise的回调返回一个promise,这个promise把下一个then
当作自己的回调函数,并在resolve
之后执行,或在reject
后被catch
出来。
async/await解决异步控制
虽然Promise将嵌套函数进行了展开,但是依然是链式函数,并没有解决回调本身。因此在最新的ES7中,引入了async/await
函数,来解决这个问题。async
函数返回一个 Promise 对象,可以使用then
方法添加回调函数。当函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
代码如下:
let num = 1function sleep(ms) { //返回一个Promise对象 return new Promise(function (resolev, reject) { setTimeout(function () { if (num < 3) { num++ //如果成功,对resolve处理 resolev('finish') } else { //如果失败,对reject操作 reject(new Error('i大于2')) } }, ms) })}//用关键词async修饰函数async function asyncSleep() { try { let value //用await来修饰函数 value = await sleep(300) console.log(value) value = await sleep(300) console.log(value) value = await sleep(300) console.log(value) } catch (error) { console.log(error) }}//调用函数asyncSleep()
在上面的函数中,我们进行逐步分析
* 我们首先将setTimeout
函数进行封装,让其返回Promise对象。
* 然后声明一个用async
修饰的函数,在里面调用sleep()
函数。
* 对于每次sleep()
函数,我们用await关键词进行修饰,表示下面的操作需要进行异步处理。
* 声明一个value变量来接受sleep()
函数返回的Promise对象。
* 最后用catch来对异常进行处理。
总的来说async/await
是promise的语法糖,但它能将原本异步的代码写成同步的形式,try...catch
也是比较友好的捕获异常的方式所以在今后写node的时候尽量多用promise或者async/await
,对于回调就不要使用了,大量嵌套真的很反人类。
参考资料
node.js异步控制流程 回调,事件,promise和async/await
ECMAScript 6 入门, 阮一峰
- Node.js异步控制流:回调、事件、Promise和async/await
- promise 和async await
- Async/await和promise
- Promise,同步异步,Async/await
- 前端的异步解决方案之Promise和Await-Async
- 前端的异步解决方案之Promise和Await-Async
- node.js 7 async / await
- node.js 异步流程控制async
- async 和await的结合promise用法
- Async和await异步编程
- async和await异步操作
- 解决js异步问题的方法--async和await(ES7)
- 使用Promise和async-await实现的一个异步遍历+同步执行任务的实例
- async/await && promise
- 关于Promise,Generator,async / await 对异步的处理
- Node.js Async Await in ES7
- 在node.js 中使用async await
- node js 异步执行流程控制模块Async介绍
- 最简单网络编程之客户端往服务器端送东西03
- React Native 'boost/iterator/iterator_adaptor.hpp' file not found’解决方案
- 69道Spring面试题和答案
- 华为自带浏览器绕坑大法!
- vector<vector<int> >array 理解
- Node.js异步控制流:回调、事件、Promise和async/await
- CSDN转载博客
- Json数据导出Excel(IE10支持)
- bootstrap-table固定表头,同时适配高度,固定列解决方案
- 查询语句中select from where group by having order by的执行顺序
- JDK环境配置
- 如何在eclipse上简历maven项目
- GLSL 基础量定义
- Sift中尺度空间、高斯金字塔、差分金字塔(DOG金字塔)、图像金字塔