前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手写async,await 理解内部原理

手写async,await 理解内部原理

作者头像
心念
发布2023-01-11 21:14:26
7920
发布2023-01-11 21:14:26
举报
文章被收录于专栏:前端心念

前言

众所周知,async,await本质就是Generator函数的语法糖。

何为糖,吃起来比较甜的。

何为语法糖,用起来比较爽的。

async await 底层并不是新东西,只是用起来比Generator函数更舒服的api...

await在等待什么

我们先看看下面这代码,这是async await的最简单使用,await后面返回的是一个Promise对象:

代码语言:javascript
复制
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()前,就一直在等待。那这个是怎么实现的呢?

原理实现

先看下面代码,输出什么?

代码语言:javascript
复制
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函数专门来实现这个效果

大聪明写法:

代码语言:javascript
复制
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之后再执行下一个

改进版:

代码语言:javascript
复制
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,就要自己写多少个嵌套吧,所以还是需要封装一个函数,显然,递归实现最简单

代码语言:javascript
复制
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了,不再让它继续递归

所以可以改成这样

最终版

代码语言:javascript
复制
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的等待执行,啊真香

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • await在等待什么
  • 原理实现
    • 最终版
    • 结论
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档