之前看koa文档是koa是洋葱模型,里面用到的await,next。话不多说,直接贴图吧
实际await就是这样的执行逻辑。
先看一段代码:
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
await async3()
}
async function async3() {
await async4()
console.log('async3')
}
async function async4() {
await async5()
console.log('async4')
}
async function async5() {
console.log('async5')
}
async1();
console.log('start')
// 执行结果
// async1 start
// async2
// async5
// start
// async4
// async3
// async1 end
分析下为何是这样的:
前面执行结果其实都很容易写出来
async1 start
async2
遇见await async2,那么紧跟在await async2 后面的那些代码就会延迟执行了。
有人说,因为await是promise的语法糖,所以实际紧跟await后面的代码可以理解成
new Promise().then(的回调函数来执行)
鹅,,,可能是这样的,或许这样也好记好理解一些。
还是按照洋葱模型来理解执行顺序吧。
第一个await async2 后面的代码先放着,一会再回来执行
ps:什么时候再回来执行呢?
答曰:等到没有遇到新的await后,并且非await的同步代码也执行完了后。
所以执行顺序是:
await async2 --> await async3 --> await async4 --> await async5
-->执行非await的同步代码
-->反过来执行await async5紧跟后面的被阻塞的代码
...
-->反过来执行await async2紧跟后面的被阻塞的代码
其实这个例子,是await 函数不停的嵌套await,所以用这个例子,理解洋葱模型最好了。
再看一段结合setTimeout异步的代码
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
setTimeout(() => {
console.log('timer1')
}, 0)
}
async function async2() {
setTimeout(() => {
console.log('timer2')
}, 0)
console.log("async2");
}
async1();
setTimeout(() => {
console.log('timer3')
}, 0)
console.log("start")
// 执行结果
// 'async1 start'
// 'async2'
// 'start'
// 'async1 end'
// 'timer2'
// 'timer3'
// 'timer1'
这里setTimeout都延迟了0秒,所以按照eventLoop的宏任务,按照宏队列循环的先后来执行。
执行await async2()时,内部有一个timer2的setTimeout,所以列为宏1,
执行同步代码时候,遇见一个timer3的setTimeout,列为宏2,
按照await紧跟在后面的代码的洋葱执行逻辑,timer1,列为宏3
所以setTimeout的执行顺序就是:
time2-->timer3-->timer1
再看一个await的例子:
async function testSometing() {
console.log("执行testSometing");
return "testSometing";
}
async function testAsync() {
console.log("执行testAsync");
return Promise.resolve("hello async");
}
async function test() {
console.log("test start...");
const v1 = await testSometing();
console.log(v1);
const v2 = await testAsync();
console.log(v2);
console.log(v1, v2);
}
test();
var promise = new Promise(resolve => {
console.log("promise start...");
resolve("promise");
});
promise.then(val => console.log(val));
console.log("test end...");
// 打印结果
// test start...
// 执行testSometing
// promise start...
// test end...
// testSometing
// 执行testAsync
// promise
// hello async
// testSometing hello async
上面例子一开始我答案写错了,我用第一道题的await 嵌套去解题了。
后来又去做了一次,还是做错了一部分。
在promise.then 和console.log(v2) 和 console.log(v1,v2)顺序上出错了。
后来搜索了相关文章,类似promise.then 和await后的代码执行前后顺序问题,不同的node版本,以及不同的chrome浏览器表现并不相同。
有打印出认为await后的同步任务先执行
也有打印出promise.then后面的微任务先执行
其实回过头想,只要记住,函数内部紧跟await后面的语句都是阻塞的就可以了。
再看一个例子:
async testAwait () {
async function async1 () {
console.log('async1 start');
await new Promise((resolve, reject) => {
console.log('promise1')
resolve(2)
})
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
await async1()
console.log('srcipt end')
}
// 打印结果
// srcipt start
// async1 start
// promise1
// async1 success
// srcipt end
但如果去掉Promise 里的resolve,执行结果如下
// srcipt start
// async1 start
// promise1
为何呢?
因为await async1(),如果async1内部没有被resolve,或内部发生了报错,await async1 后面的代码都会被阻塞掉。
(内部报错,或者reject的话,编译后的await函数会内部报错,可以看打印结果的)
如果把await async1()换成
async1().then(res => console.log(res))
那么同步代码
console.log('srcipt end')
不会被影响,因为async1()返回了一个Promise,但状态一直是pendding,所以then无法进去。
所以日常在写业务代码的时候,务必保证调用的await 函数,他内部状态,不然会影响整个js同步进程的执行,造成阻塞。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。