async、await
async / await是ES7的重要特性之一,也是目前社区里公认的优秀异步解决方案。目前,async / await这个特性已经是stage 3的建议。
(1)Promise、generator、yield是ES6中的异步解决方案,使用Promise之后,可以很好的减少嵌套的层数。
function fn(p) { return new Promise((resolve, reject) => { console.log(p); setTimeout(() => { console.log('test:',p); resolve(); }, 2000); }); } function handlePromise() { fn("aaa") .then(() => { return fn("bbb"); }) .then(() => { return fn("ccc"); }) .catch(reason => console.log(`catch reason: ${reason}`)); } 执行:handlePromise()
Promise仍然存在缺陷,它只是减少了嵌套,并不能完全消除嵌套。对于多个promise串行执行的情况,第一个promise的逻辑执行完之后,我们需要在它的then函数里面去执行第二个promise,这个时候会产生一层嵌套。另外,采用Promise的代码看起来依然是异步的!
(2)在Node.js中对于回调的处理,我们经常用的TJ / Co就是使用generator结合promise来实现的,co是coroutine的简称。
const co = require('co'); const request = require('request'); const options = { url: 'https://api.github.com/repos/cpselvis/zhihu-crawler', headers: { 'User-Agent': 'request' } }; // yield后面是一个生成器 generator const getRepoData = function* () { return new Promise((resolve, reject) => { request(options, (err, res, body) => { if (err) { reject(err); } resolve(body); }); }); }; co(function* () { const result = yield getRepoData; // ... 如果有多个异步流程,可以放在这里,比如 // const r1 = yield getR1; // const r2 = yield getR2; // const r3 = yield getR3; // 每个yield相当于暂停,执行yield之后会等待它后面的generator返回值之后再执行后面其它的yield逻辑。 return result; }).then(function (value) { console.log(value); }, function (err) { console.error(err.stack); });
(3)虽然co是社区里面的优秀异步解决方案,但是并不是语言标准,只是一个过渡方案。
ES7语言层面提供async / await去解决语言层面的难题。目前async / await 在 IE edge中已经可以直接使用了,但是chrome和Node.js还没有支持。
幸运的是,babel已经支持async的transform了,所以我们使用的时候引入babel就行。
在开始之前我们需要引入以下的package,preset-stage-3里就有我们需要的async/await的编译文件。
无论是在Browser还是Node.js端都需要安装下面的包。
$ npm install babel-core --save $ npm install babel-preset-es2015 --save $ npm install babel-preset-stage-3 --save
function fn(p) { return new Promise((resolve, reject) => { console.log(p); setTimeout(() => { console.log('test:',p); resolve(); }, 2000); }); } // 注意点: // 1.async用来申明里面包裹的内容可以进行同步的方式执行,await则是进行执行顺序控制, // 每次执行一个await,程序都会暂停等待await返回值,然后再执行之后的await。 // 2.await后面调用的函数需要返回一个promise,另外这个函数是一个普通的函数即可,而不是generator。 // 3.await只能用在async函数之中,用在普通函数中会报错。 // 4.await命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。 // 5.其实,async / await的用法和co差不多,await和yield都是表示暂停, // 外面包裹一层async 或者 co来表示里面的代码可以采用同步的方式进行处理。 // 不过async / await里面的await后面跟着的函数不需要额外处理,co是需要将它写成一个generator的。 async function asyncFun() { try { const result_a = await fn('aaa'); const result_b = await fn('bbb'); const result_c = await fn('ccc'); const result_d = await fn('ddd'); // 每个await相当于暂停,执行await之后会等待它后面的函数(不是generator)返回值之后再执行后面其它的await逻辑。 return result_d; } catch (err) { console.log(err); } } 执行:asyncFun().then(result => console.log(`result: ${result}`)).catch(err => console.error(err));
async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。
async 函数返回的是一个 Promise 对象。从文档中也可以得到这个信息。async 函数(包含函数语句、函数表达式、Lambda表达式)会返回一个 Promise 对象,如果在函数中 return
一个直接量,async 会把这个直接量通过 Promise.resolve()
封装成 Promise 对象。如果 async 函数没有返回值,又该如何?很容易想到,它会返回 Promise.resolve(undefined)
。注意,函数fn(p)
没有申明为 async
。实际上fn(p)
本身就是返回的 Promise 对象,加不加 async
结果都一样。
Promise 方案 参数传递太麻烦,async、await方案便于传参,例子参考:https://segmentfault.com/a/1190000007535316
参考:
http://www.cnblogs.com/cpselvis/p/6344122.html
https://segmentfault.com/a/1190000007535316
http://caibaojian.com/es6/generator.html