如何将generator函数转为类async函数?

如何将generator函数转为类async函数?

我们先来想想generator和async有什么区别?
唯一的区别就是async会自动执行,而generator每次都要调用next函数。
所以问题变为,如何让generator自动执行next函数?
回忆一下generator的知识:每次执行generator的next函数时,它会返回一个对象:

1
{ value: xxx, done: false }

返回这个对象后,如果能再次执行next,就可以达到自动执行的目的了。
看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function * gen(){
yield new Promise((resolve,reject){
//异步函数1
if(成功){
resolve()
}else{
reject();
}
});

yield new Promise((resolve,reject){
//异步函数2
if(成功){
resolve()
}else{
reject();
}
})
}
let g = gen();
let ret = g.next();

此时ret = { value: Promise实例; done: false};value已经拿到了Promise对象,那就可以自己定义成功/失败的回调函数了。如:

1
ret.value.then(g.next)

现在就大功告成啦。我们只要找到一个合适的方法让g.next()一直持续下去就可以自动执行了。
所以问题的关键在于yield的value必须是一个Promise。那么我们来看看co是如何把这些东西都转化为Promise的:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
function co(gen) {
var ctx = this; // 把上下文转换为当前调用co的对象
var args = slice.call(arguments, 1) // 获取参数

// we wrap everything in a promise to avoid promise chaining,
// 不管你的gen是什么,都先用Promise包裹起来
return new Promise(function(resolve, reject) {
// 如果gen是函数,则修改gen的this为co中的this对象并执行gen
if (typeof gen === 'function') gen = gen.apply(ctx, args);

// 因为执行了gen,所以gen现在是一个有next和value的对象,如果gen不存在、或者不是函数则直接返回gen
if (!gen || typeof gen.next !== 'function') return resolve(gen);

// 执行类似上面示例g.next()的代码
onFulfilled();


function onFulfilled(res) {
var ret;
try {
ret = gen.next(res); // 执行每一个gen.next()
} catch (e) {
return reject(e);
}
next(ret); //把执行得到的返回值传入到next函数中,next函数是自动执行的关键
}


function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}

/**
* Get the next value in the generator,
* return a promise.
*/
function next(ret) {
// 如果ret.done=true说明迭代已经完毕,返回最后一次迭代的value
if (ret.done) return resolve(ret.value);

// 无论ret.value是什么,都转换为Promise,并且把上下文指向ctx
var value = toPromise.call(ctx, ret.value);

// 如果value是一个Promise,则继续在then中调用onFulfilled。相当于从头开始!!
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);

return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
});
}

本文只作为我学习的一个笔记,这是该文的出处:可能是目前最全的koa源码解析指南