# async/await 和 generator
# 示例
异步请求
const getData = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve("good"), 1000);
});
2
3
4
# async/await
async function asyncTestG () {
console.log('start');
const data1 = await getData();
console.log('data1', data1)
const data2 = await getData();
console.log('data2', data2)
return 'end'
}
asyncTestG().then( res => console.log(res))
打印结果:
> start
> data1 good
> data2 good
> end
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Generator
function* testG() {
console.log("start"); // ①
const data1 = yield getData(); // ②
console.log("data1", data1); // ③
const data2 = yield getData(); // ④
console.log("data2", data2); // ⑤
return "end"; // ⑥
}
2
3
4
5
6
7
8
调用 Generator 函数,返回一个可迭代的对象,注意,此时还没有执行 testG 里面的代码
const gen = testG();
第一次调用 next 方法,开始执行 Generator 函数,直到遇到第一个 yield 表达式停止所以停在 ② 处
const g1 = gen.next()
继续执行 next 方法,从 ② 处继续往下执行,碰到下一个 yield 时停止,此时停在 ④ 处
const g2 = gen.next()
继续执行,碰到 return 语句,函数执行结束
const g3 = gen.next()
next 函数调用后返回一个对象 {value: any, done: boolean} value 是 yield 后面表达式的返回值,这里也就是一个 Promise 对象, done 表示生成器后续是否还有 yield 语句,即生成器函数是否已经执行完毕并返回
如果在调用 next 函数的时候传入了参数,那么这个参数会赋值给上一条执行的 yield 语句左边定义的变量,比如 const g2 = gen.next('hello') ,此时 data1 = 'hello' 当生成器执行遇到 return 时,会导致生成器立刻变为完成状态,即调用 next 函数返回对象中 done 为 true,value 则是 return 的值, 如果执行到最后没有 return,则 value 为 undefined
现在知道了 Generator 函数的调用原理,如果想打印出和 async/await 一样的结果,如下:
const g1 = gen.next();
g1.value.then((res) => {
const g2 = gen.next(res);
g2.value.then((r) => {
const g3 = gen.next(r);
console.log(g3.value);
});
});
2
3
4
5
6
7
8
# 通过 Generator 来实现 async 的用法
根据 async/await
的用法,它返回的是一个 Promise 对象,而且我们需要传入 生成器函数作为参数,因此 asyncToGenerator
函数大体结构如下:
function asyncToGenerator(genFunc) {
return function () {
return new Promise((resolve, reject) => {});
};
}
2
3
4
5
下面是完整代码:
function asyncToGenerator(genFunc) {
return function () {
// 调用 Generator 函数,生成迭代器对象
const gen = genFunc.apply(this, arguments);
return new Promise((resolve, reject) => {
/**
* 递归执行迭代器对象的 next 方法
* @param key 迭代器的方法名 next or throw
* @param arg 执行next 或者 throw 方法时传入的参数
*/
function step(key, arg) {
let genResult;
try {
genResult = gen[key](arg);
} catch (err) {
return reject(err);
}
const { value, done } = genResult;
if (done) {
// 只有当迭代器执行完毕,最后一次调用next方法的时候
// done 才会为 true,而此时的 value 也就是 生成器函数的返回值,
// 若没有return,则是 undefined, 因此直接 resolve 返回
return resolve(value);
} else {
// 此处的 value 一般来说是 Promise 对象
// 但如果 yield 后面是一个常量 '你好世界'
// 此时 value 就是一个字符串
// 所以用 Promise.resolve 包一下,解析value的值
// 如果报错通过catch捕获,执行 gen.throw
return Promise.resolve(value)
.then((val) => {
step("next", val);
})
.catch((err) => {
step("throw", err);
});
}
}
step("next");
});
};
}
const func = asyncToGenerator(generatorFunc);
func()
.then((res) => console.log(res))
.catch((e) => console.log("e", e));
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