众所周知,async,await本质就是Generator函数的语法糖。
何为糖,吃起来比较甜的。
何为语法糖,用起来比较爽的。
async await 底层并不是新东西,只是用起来比Generator函数更舒服的api...
我们先看看下面这代码,这是async await的最简单使用,await后面返回的是一个Promise对象:
1async function getResult() {
2 await new Promise((resolve, reject) => {
3 setTimeout(() => {
4 resolve(1);
5 console.log(1);
6 }, 3000);
7 })
8
9 console.log(2);
10}
11
12getResult()
上面代码3秒后打印结果为:
1
2
也就是说明,await后面的promise对象,没resolve()前,就一直在等待。那这个是怎么实现的呢?
先看下面代码,输出什么?
1async function getResult() {
2 await new Promise((resolve, reject) => {
3 setTimeout(() => {
4 resolve(1);
5 console.log(1);
6 }, 1000);
7 })
8
9
10 await new Promise((resolve, reject) => {
11 setTimeout(() => {
12 resolve(2);
13 console.log(2);
14 }, 500);
15 })
16
17 await new Promise((resolve, reject) => {
18 setTimeout(() => {
19 resolve(3);
20 console.log(3);
21 }, 100);
22 })
23
24}
25
26getResult()
没错,是输出:1,2,3
现在改用generator函数专门来实现这个效果
大聪明写法:
1function* getResult(params) {
2
3 yield new Promise((resolve, reject) => {
4 setTimeout(() => {
5 resolve(1);
6 console.log(1);
7 }, 1000);
8 })
9
10 yield new Promise((resolve, reject) => {
11 setTimeout(() => {
12 resolve(2);
13 console.log(2);
14 }, 500);
15 })
16
17 yield new Promise((resolve, reject) => {
18 setTimeout(() => {
19 resolve(3);
20 console.log(3);
21 }, 100);
22 })
23}
24const gen = getResult()
25
26gen.next();
27gen.next();
28gen.next();
最终输出:3,2,1 明显不对
因为三个promise实例同时执行了,才会出现这种问题,所以需要等第一个promise,resolve之后再执行下一个
改进版:
1
2function* getResult(params) {
3
4 yield new Promise((resolve, reject) => {
5 setTimeout(() => {
6 resolve(1);
7 console.log(1);
8 }, 1000);
9 })
10
11 yield new Promise((resolve, reject) => {
12 setTimeout(() => {
13 resolve(2);
14 console.log(2);
15 }, 500);
16 })
17
18 yield new Promise((resolve, reject) => {
19 setTimeout(() => {
20 resolve(3);
21 console.log(3);
22 }, 100);
23 })
24}
25const gen = getResult()
26
27gen.next().value.then(() => {
28 gen.next().value.then(() => {
29 gen.next();
30 });
31});
打印结果:1,2,3 这次对了
但是呢,总不能有多少个await,就要自己写多少个嵌套吧,所以还是需要封装一个函数,显然,递归实现最简单
1function* getResult(params) {
2
3 yield new Promise((resolve, reject) => {
4 setTimeout(() => {
5 resolve(1);
6 console.log(1);
7 }, 1000);
8 })
9
10 yield new Promise((resolve, reject) => {
11 setTimeout(() => {
12 resolve(2);
13 console.log(2);
14 }, 500);
15 })
16
17 yield new Promise((resolve, reject) => {
18 setTimeout(() => {
19 resolve(3);
20 console.log(3);
21 }, 100);
22 })
23}
24const gen = getResult()
25
26function co(g) {
27 g.next().value.then(()=>{
28 co(g)
29 })
30}
31
32co(gen)
再来看看打印结果,发现了一个报错
可以发现成功执行了,但是为什么报错了?
这是因为generator方法会返回四次,最后一次的value是undefined。
而实际上返回第三次就表示已经返回done,代表结束了,所以,我们需要判断是否是已经done了,不再让它继续递归
所以可以改成这样
1function* getResult(params) {
2
3 yield new Promise((resolve, reject) => {
4 setTimeout(() => {
5 resolve(1);
6 console.log(1);
7 }, 1000);
8 })
9
10 yield new Promise((resolve, reject) => {
11 setTimeout(() => {
12 resolve(2);
13 console.log(2);
14 }, 500);
15 })
16
17 yield new Promise((resolve, reject) => {
18 setTimeout(() => {
19 resolve(3);
20 console.log(3);
21 }, 100);
22 })
23}
24const gen = getResult()
25
26function co(g) {
27 const nextObj = g.next();
28 if (nextObj.done) {
29 return;
30 }
31 nextObj.value.then(()=>{
32 co(g)
33 })
34}
35
36co(gen)
可以看到这样就实现了。
完美,这个co其实也是大名鼎鼎的co函数的简单写法
async内部就是帮我们执行了co函数,根据resolve时机自动调用Generator.next(),实现await的等待执行,啊真香