Open
Description
我们要如何实现一个async/await
呢,首先我们要知道,async/await
实际上是对Generator
(生成器)的封装,是一个语法糖。
因此我们先来看看Generator
的用法:
ES6 新引入了Generator
函数,可以通过yield
关键字,把函数的执行流挂起,通过next()
方法可以切换到下一个状态,为改变执行流程提供了可能,从而为异步编程提供解决方案。
function* myGenerator() {
yield '1'
yield '2'
return '3'
}
const gen = myGenerator(); // 获取迭代器
gen.next() //{value: "1", done: false}
gen.next() //{value: "2", done: false}
gen.next() //{value: "3", done: true} 如果上面不是return '3',而是yield '3'则返回{value: undefined, done: true}
也可以通过给next()
传参, 让yield
具有返回值
function* myGenerator() {
console.log(yield '1') //test1
console.log(yield '2') //test2
console.log(yield '3') //test3
}
// 获取迭代器
const gen = myGenerator();
gen.next()
gen.next('test1')
gen.next('test2')
gen.next('test3')
我们看到Generator的用法,应该️会感到很熟悉,*/yield
和async/await
看起来其实已经很相似了,它们都提供了暂停执行的功能,但二者又有三点不同:
async/await
自带执行器,不需要手动调用next()
就能自动执行下一步async
函数返回值是Promise
对象,而Generator
返回的是生成器对象
await
能够返回Promise
的resolve/reject
的值
我们对async/await的实现,其实也就是对应以上三点封装Generator。
function run(gen) {
var g = gen() //由于每次gen()获取到的都是最新的迭代器,因此获取迭代器操作要放在_next()之前,否则会进入死循环
function _next(val) { //封装一个方法, 递归执行g.next()
let {value, done} = g.next(val) //获取迭代器对象,并返回resolve的值
if(done) return value //递归终止条件
value.then(val => { //Promise的then方法是实现自动迭代的前提
_next(val) //等待Promise完成就自动执行下一个next,并传入resolve的值
})
}
_next() //第一次执行
}
对于我们之前的例子,我们就能这样执行:
function* myGenerator() {
console.log(yield Promise.resolve(1)) //1
console.log(yield Promise.resolve(2)) //2
console.log(yield Promise.resolve(3)) //3
}
run(myGenerator)
返回Promise & 异常处理
虽然我们实现了Generator的自动执行以及让yield
返回resolve
的值,但上边的代码还存在着几点问题:
- 返回值是Promise:
async/await
的返回值是一个Promise
,我们这里也需要保持一致,给返回值包一个Promise
- 需要兼容基本类型:这段代码能自动执行的前提是
yield
后面跟Promise
,为了兼容后面跟着基本类型值
的情况,我们需要把yield
跟的内容都用Promise.resolve()
转化一遍 - 缺少错误处理:上边代码里的
Promise
如果执行失败,就会导致后续执行直接中断,我们需要通过调用Generator.prototype.throw()
,把错误抛出来,才能被外层的try-catch
捕获到
我们改造一下run方法:
function run(genetator){
return new Promise((resolve, reject) => {
let interator = genetator();
function _next(val){
try {
var {value, done} = interator.next(val)
} catch (error) {
return reject(error);
}
if(done){
return resolve("{ value: undefined, done: true }");
}
Promise.resolve(value).then((v) => {
_next(v);
}, (error) => {
interator.throw(error);
})
}
_next();
})
}
然后我们可以测试一下:
function* myGenerator() {
let res1 = yield 1;
console.log(res1) //1
let res2 = yield 2;
console.log(res2) //2
let res3 = yield Promise.resolve(3);
console.log(res3) //3
}
let res = run(myGenerator)
res.then((d) => {
console.log(d); // { value: undefined, done: true }
})