我们一直使用async和await,来让我们以同步的方式来调用异步代码,它实际上就是promise语法糖,书写起来也比较方便。一直都是使用,但是并不知道它的实现原理是什么,今天再看es6文档的时候看到了,就简单来记录一下。
引导
其实这个问题看似很难,但是其实是我们应该知道的一个问题,比如说我们在学习es6的时候有一个generator生成器函数,随后我们就开始学习了async和await,我记得当时我看视频中老师说了把generator的*换成async把yield换成await,就是async和await 由此我们可见,async和await的实现和生成器函数密不可分
生成器函数详解
因为我们平常用生成器函数比较少,所以可能有些遗忘,所以我这里来简单的回顾一下
- 函数声明
// 注意,生成器函数的星号不受两侧空格的影响
function* generatorFn() {}
- 使用
声明生成器之后,我们需要调用生成器函数来产生一个生成器对象,然后控制整个函数的调度,另外需要注意的一点是:生成器一开始处于暂停执行,我们需要先调用,才能执行(这里也就牵涉到为什么next第一次传入的值没有效果,是因为是为了开启调度的),与生成器相似的是,生成器对象也实现了iterator接口,会有next()方法(该方法用于调度到下一个),同时返回值会有一个done(ture表示调度结束了)和value
//直接使用
function* generatorFn() {
return 'foo';
}
let generatorObject = generatorFn();
console.log(generatorObject); // generatorFn {<suspended>}
console.log(generatorObject.next()); // { done: true, value: 'foo' }
// 使用yield中断
function* generatorFn() {
yield 'foo';
yield 'bar';
return 'baz';
}
let generatorObject = generatorFn();
console.log(generatorObject.next()); // { done: false, value: 'foo' }
console.log(generatorObject.next()); // { done: false, value: 'bar' }
console.log(generatorObject.next()); // { done: true, value: 'baz' }
// 使用next传值
function* generatorFn(initial) {
console.log(initial);
console.log(yield);
console.log(yield);
}
// 生成一个生成器对象
let generatorObject = generatorFn('foo');
// 开启调度,执行第一个输出,输出传入的initial
generatorObject.next('bar'); // foo
// 调度下一个,next传入值会作为上一个yield的返回值(即第二个next传入的值会作为第一个yield的返回值)
generatorObject.next('baz'); // baz
// 输出qux,解释同上一个
generatorObject.next('qux'); // qux
/*
这里可能比较绕一点,我们只需要记住第一次调用next传入的值是无效的
因为第一次的next是启动调度,之后调用的next传入的值会作为yield的返回值
比如我们第二次调用next('baz')会输出yield
*/
实现async和await
function spawn(genF) {
return new Promise(function (resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch (e) {
return reject(e);
}
// done是false表示已经没有后续的代码要运行了
if (next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function (v) {
// 开始调度,这里next传递的值会作为上一次yeild的返回值(即第二次执行next函数会作为第一个yield的返回值因为第一个没有用)
step(function () { return gen.next(v); });
}, function (e) {
step(function () { return gen.throw(e); });
});
}
// 开始调度,第一次next传递的值没有效果,用于开始调度
step(function () { return gen.next(undefined); });
});
}
以上就是async和await的实现原理,我们在调用的时候直接传入一个生成器函数就可以