阮老师曾经在2015年的时候,就对这个方法做好详细介绍,现在2019年了,咱们再来详细的学习下。
从最早期的回调函数,到 Promise 对象, 再到 Generator 函数,每次都有所改进。
``
异步I/O不就是读取一个文件,干嘛搞的这么复杂,
然而异步编程的最高境界,就是不关心它是不是异步。
async 函数就是隧道尽头之光
要跑这段脚本也容易,去node上进行调试就OK了。
var fs = require('fs');
var readFile = function (fileName){
return new Promise(function (resolve, reject){
fs.readFile(fileName, function(error, data){
if (error) reject(error);
resolve(data);
});
});
};
var gen = function* (){
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
var asyncReadFile = async function (){
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
比较后会发现,async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已。
async 函数对 Generator 函数的改进,提现在以下三点。
var result = asyncReadFile();
3.更广的适用性。co 函数库约定,yield 命令后面只能是 Thunk 函数 或 Promise 对象,而 async 函数的 await命令后面,可以跟
Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
async 函数的实现
async 函数的实现,就是将Generator函数和自动执行器,包装在一个函数里。
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function*() {
})
}
所有的 async 函数都可以写成上面的第二种形式,其中的 spawn 函数就是自动执行器。
下面给出 spawn 函数的实现,基本就是前文自动执行器的翻版。
/**
* 模拟 async 函数的实现,该段代码取自 Secrets of the JavaScript Ninja (Second Edition),p159
*/
// 接收生成器作为参数,建议先移到后面,看下生成器中的代码
var myAsync = generator => {
// 注意 iterator.next() 返回对象的 value 是 promiseAjax(),一个 promise
const iterator = generator();
// console.log('--->', iterator)
// handle 函数控制 async 函数的 挂起-执行
const handle = iteratorResult => {
// console.log('---11>', iteratorResult) // 如果是true的话它就对代码进行阻断了
if (iteratorResult.done) return;
// console.log('--->', iteratorResult) // 如果是promise是会挂起的
const iteratorValue = iteratorResult.value;
// 只考虑异步请求返回值是 promise 的情况
if (iteratorValue instanceof Promise) {
// 递归调用 handle,promise 兑现后再调用 iterator.next() 使生成器继续执行
// ps.原书then最后少了半个括号 ')'
iteratorValue
.then(result => handle(iterator.next(result)))
.catch(e => iterator.throw(e));
}
};
try {
handle(iterator.next());
} catch (e) {
console.log(e);
}
};
myAsync(function*() {
try {
const a = yield Promise.resolve(1);
const b = yield Promise.resolve(a + 10);
const c = yield Promise.resolve(b + 100);
console.log(a, b, c); // 输出 1,11,111
} catch (e) {
console.log("出错了:", e);
}
})
我没有看阮老师的代码,从这段代码来看,async 函数 只是构造器函数的封装也就是 generator 函数的封装。 iteratorResult.done一直是false,直到 iteratorResult.done 为 true,函数这个时候才阻断执行。
我就粘贴下掘金原作者的话,然后大家看一眼。
1)首先调用generator()生成它的控制器,即迭代器iterator,此时,生成器处于挂起状态;
2)第一次调用handle函数,并传入iterator.next(),这样就完成生成器的第一次调用的;
3)执行生成器,遇到yield生成器再次挂起,同时把yield后表达式的结果(未完成的 promise)传给 handle;
4)生成器挂起的同时,异步请求还在进行,异步请求完成(promise 兑现)后,会调用handle函数中的iteratorValue.then();
5)iteratorValue.then()执行时内部递归调用handle,同时把异步请求回的数据传给生成器(iterator.next(result)),生成器更新数据再次执行。如果出错直接结束;
6)3、4、5 步重复执行,直到生成器结束,即iteratorResult.done === true,myAsync 结束调用。
https://juejin.im/post/5b56837c6fb9a04fe0181555
普通函数使用 function 声明,生成器函数用 function*声明
普通函数使用 return 返回值,生成器函数使用 yield 返回值
普通函数是 run to completion 模式,即普通函数开始执行后,会一直执行到该函数所有语句完成,在此期间别的代码语句是不会被执行的;生成器函数是 run-pause-run 模式,即生成器函数可以在函数运行中被暂停一次或多次,并且在后面再恢复执行,在暂停期间允许其他代码语句被执行
对于 Generators 的使用,本文不再多做介绍,如需了解更多内容推荐阅读下面系列文章,《ES6 Generators: Complete Series》或者 《深入掌握 ECMAScript 6 异步编程》系列文章
http://www.alloyteam.com/2016/02/generators-in-depth/
以上是对 async 函数的简单理解
文章采用 知识共享署名 4.0 国际许可协议 进行许可,转载时请注明原文链接。