深圳幻海软件技术有限公司 欢迎您!

记录--通过手写,分析async await核心原理

2023-03-02

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助前言asyncawait语法是ES7出现的,是基于ES6的promise和generator实现的generator函数在之前我专门讲个generator的使用与原理实现,大家没了解过的可以先看那个手写generator核心原理,再也不怕面

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

前言

async await 语法是 ES7出现的,是基于ES6的 promise和generator实现的

generator函数

在之前我专门讲个generator的使用与原理实现,大家没了解过的可以先看那个手写generator核心原理,再也不怕面试官问我generator原理

这里就不再赘述generator,专门的文章讲专门的内容。

await在等待什么

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

?
1
2
3
4
5
6
7
8
9
10
async function getResult() {
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
}
 
getResult()

但不知你有没有想过一个问题,为什么会等到返回的promise的对象的状态为非pending的时候才会继续往下执行,也就是resolve执行之后,才会继续执行,就像下面的代码一样

?
1
2
3
4
5
6
7
8
9
10
11
async function getResult() {
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
    console.log(2);
}
 
getResult()

可以看到运行结果是先打印了1,再打印2了,也就是说明在返回的promise对象没执行resolve()前,就一直在await,等它执行。然后再执行下面的程序,那这个是怎么实现的呢?

原理实现

我们看一下下面的代码,输出顺序是什么?

?
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
async function getResult() {
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
     
 
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })
 
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })
 
}
 
getResult()

没错是 1,2,3.

那用generator函数专门来实现这个效果呢

我一开始这样来实现:

?
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
function* getResult(params) {
     
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })
}
const gen = getResult()
 
gen.next();
gen.next();
gen.next();

但是发现打印顺序是 3,2,1.明显不对。

这里的问题主要是三个 new Promise几乎是同一时刻执行了。才会出现这种问题,所以需要等第一个promise执行完resolve之再执行下一个,所以要这么实现

?
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
function* getResult(params) {
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })
}
const gen = getResult()
 
gen.next().value.then(() => {
    gen.next().value.then(() => {
        gen.next();
    });
});

可以看到这样就打印正常了。

特别 需要解释下。gen.next().value 就是返回的promise对象,不理解的可以看看文首介绍的那篇generator的 文章。手写generator核心原理,再也不怕面试官问我generator原理

优化

但是呢,总不能有多少个await,就要自己写多少个嵌套吧,所以还是需要封装一个函数,显然,递归实现最简单

?
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
function* getResult(params) {
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })
}
const gen = getResult()
 
function co(g) {
    g.next().value.then(()=>{
        co(g)
    })
}
 
co(gen)

再来看看打印结果

可以发现成功执行了,但是为什么报错了?

这是因为generator方法会返回四次,最后一次的value是undefined。

而实际上返回第三次就表示已经返回done,代表结束了,所以,我们需要判断是否是已经done了,不再让它继续递归

所以可以改成这样

?
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
function* getResult(params) {
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })
 
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })
}
const gen = getResult()
 
function co(g) {
    const nextObj = g.next();
    if (nextObj.done) {
        return;
    }
    nextObj.value.then(()=>{
        co(g)
    })
}
 
co(gen)

可以看到这样就实现了。

完美,这个co其实也是大名鼎鼎的co函数的简单写法

本篇文章关于async 和 await的原理揭秘就到此为止了,再讲下去就不礼貌了。

本文转载于:

https://juejin.cn/post/7136424542238408718

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 


__EOF__

  • 本文作者: 林恒
  • 本文链接: https://www.cnblogs.com/smileZAZ/p/16720415.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。