Generator+Promise实现Async&Await

我们在之前讲解async时提到了由Promise转变至async所经历的一系列过程。

由于Promise只能通过catch捕获错误,而在内部.then链中嵌套的一系列Promise调用所产生的err是无法被外层catch所捕获的,而其后添加catch则又破坏了代码的格式和一致性。

同时,Promise中的catch函数中的异常堆栈不够完整,依旧难以追寻真正发生错误的位置(因为内部调用大都是匿名函数。而不用匿名函数则又丧失了箭头函数的简洁,让开发者很苦恼)。

所以,Async语法应运而生。他弥补了上述Promise调用所出现的不足之处,而其具体实现则是通过GeneratorPromise协同的语法糖。下面我们一起看看如何通过GeneratorPromise实现一个Async-Await吧~

Generator

  • Generator是一个函数,通过yield关键字控制其执行
  • 执行该函数时到第一个yield时停止执行,直到调用其返回值的.next()才会执行yield后面的部分,返回一个带有valuedone属性的对象,其机制类似于return,到此为止,返回结果
  • 我们可以在调用next()时传递一个参数,在上次yield前接收到这个参数
  • 同时,Generator内部仍可以嵌套Generator函数,具体实现就是yield*,在此不再赘述。

在此只对其做一个粗浅的概述,若各位有兴趣可以看相关的博客: 《ES6中的Generator》

Promise

  • 每次返回更新自己的状态(fullfied || rejected),返回值仍为一个Promise

实现

实现之前我们需要明确:

  • generator生成器返回值是一个对象({value: XXX, done: true}
  • async-await返回的是一个Promise

所以我们若想实现功能,必须将Promise塞进field之中去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function run (gen) {
gen = gen()
return next(gen.next()) // 自动调用yeild

function next ({done, value}) {
return new Promise(resolve => { // 返回Promise,与async返回类型相同
if (done) { // finish
resolve(value)
} else { // not yet
value.then(data => {
next(gen.next(data)).then(resolve) // 递归调用yield
})
}
})
}
}

function getRandom () {
return new Promise(resolve => {
setTimeout(_ => resolve(Math.random() * 10 | 0), 1000)
})
}

function * main () {
let num1 = yield getRandom()
let num2 = yield getRandom()

return num1 + num2
}

run(main).then(data => {
console.log(`got data: ${data}`);
})

由代码可知我们通过generator——main函数以近似同步的方式执行异步代码。但这同时也需要一个外部函数帮助我们执行这个main

而其对应的async/await函数实现上方代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function getRandom () {
return new Promise(resolve => {
setTimeout(_ => resolve(Math.random() * 10 | 0), 1000)
})
}

async function main () {
let num1 = await getRandom()
let num2 = await getRandom()

return num1 + num2
}

console.log(`got data: ${await main()}`)

大体上看我们只需要将*改为async,将yield改成await,且免去了外部调用的main函数。

事实上Generator作为一个生成器,用以完成异步代码的近同步化确实有些不务正业。不过的确有相关的库(co)用以将PromiseGenerator实现其功能,有兴趣的boy可以查询相关文档。

总结

Generator生成器返回的对象是一个类似于{value: XXX, done: true}结构的Object。而Async则始终返回一个Promise,使用await.then()获取返回值。

相较于Generator + coAsync生来就是为了处理异步编程,对此更加专业,所以还是让Generator干他自己的老本行去8~