手写Async await核心原理,再也不怕面试官问我Async await原理

开发 前端
什么会等到返回的promise的对象的状态为非pending的时候才会继续往下执行,也就是resolve执行之后,才会继续执行。

前言

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

generator函数

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

await在等待什么

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

async function getResult() {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
console.log(1);
}, 1000);
})
}

getResult()

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

async function getResult() {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
console.log(1);
}, 1000);
})
console.log(2);
}

getResult()

图片

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

原理实现

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

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函数专门来实现这个效果呢?

我一开始这样来实现:

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之再执行下一个,所以要这么实现。

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();
});
});

图片

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

优化

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

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了,不再让它继续递归。

所以可以改成这样:

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的原理揭秘就到此为止了,再讲下去就不礼貌了。

责任编辑:武晓燕 来源: 前端阳光
相关推荐

2021-08-10 18:36:02

Express原理面试

2020-10-20 09:12:57

axios核心原理

2020-11-24 07:48:32

React

2020-10-23 09:26:57

React-Redux

2022-01-05 09:55:26

asynawait前端

2021-05-08 07:53:33

面试线程池系统

2022-04-01 07:52:42

JavaScript防抖节流

2022-10-31 11:10:49

Javavolatile变量

2024-03-12 08:37:32

asyncawaitJavaScript

2014-07-15 10:31:07

asyncawait

2016-11-22 11:08:34

asyncjavascript

2021-07-20 10:26:12

JavaScriptasyncawait

2012-07-22 15:59:42

Silverlight

2023-10-08 10:21:11

JavaScriptAsync

2023-07-28 07:31:52

JavaScriptasyncawait

2021-06-28 07:27:43

AwaitAsync语法

2023-11-28 17:49:51

watch​computed​性能

2022-06-13 07:36:47

useEffectHooks

2021-06-15 05:36:45

Gulpawaitasync

2020-10-15 12:52:46

SpringbootJava编程语言
点赞
收藏

51CTO技术栈公众号