最近遇到一个关于async函数使用的Bug,因代码涉及太多业务,所以模拟了代码, 如下:
let testArr = [1, 2, 3]
let flag = false
const func = (res) => {
return new Promise((resolve, reject) => {
if (res) {
resolve(res)
}
})
}
testArr.forEach(async (item) => {
await func(item).then(res => {
flag = true
console.log('res', res, flag)
})
})
console.log('flag', flag)
当时写代码的人的目的很简单,就是要让异步函数变成同步来执行,按如下输出:
res 1 true
res 2 true
res 3 true
flag true
但实际输出的是:
flag false
res 1 true
res 2 true
res 3 true
当时我也觉得奇怪的,为什么await没有生效?真的没有生效?
于是我在await 后面加了一个console.log(‘inside’, flag), 代码如下
let testArr = [1, 2, 3]
let flag = false
const func = (res) => {
return new Promise((resolve, reject) => {
if (res) {
resolve(res)
}
})
}
testArr.map(async (item) => {
await func(item).then(res => {
flag = true
console.log('res', res, flag)
})
console.log('inside', flag)
})
console.log('flag', flag)
输出如下
flag false
res 1 true
res 2 true
res 3 true
inside true
inside true
inside true
也就是说,其实在函数里面await是生效了?那为是什么外面就没有生效?
很多人以为await会一直等待之后的表达式执行完之后才会继续执行后面的代码,实际上await是一个让出线程的标志。
await后面的函数会先执行一遍,然后就会跳出整个async函数来执行后面js栈的代码。
等本轮事件循环执行完了之后又会跳回到async函数中等待await后面表达式的返回值。
如果返回值为非promise,则继续执行async函数后面的代码,
否则将返回的promise,放入promise队列(Promise的Job Queue), 然后等待promise任务队列执行完之后,再执行await后面的代码
所以,如果要让flag变成true,需要再用一个async函数,修改的代码如下:
let testArr = [1, 2, 3]
let flag = false
const func = (res) => {
return new Promise((resolve, reject) => {
if (res) {
resolve(res)
}
})
}
async function container () {
await testArr.map(async (item) => {
await func(item).then(res => {
flag = true
console.log('res', res, flag)
})
console.log('inside', flag)
})
console.log('flag', flag)
}
container()
输出:
res 1 true
res 2 true
res 3 true
flag true
inside true
inside true
inside true
从其他博主看到这样一段代码,我觉得非常经典:
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();//关键点1
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");});//关键点2
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
调整了一下代码顺序
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 v2 = await testAsync();
console.log(v2);
const v1 = await testSometing();//关键点1
console.log(v1);
console.log(v1, v2);
}
test();
var promise = new Promise((resolve)=> { console.log("promise start.."); resolve("promise");});//关键点2
promise.then((val)=> console.log(val));
console.log("test end...")
输出:
test start...
执行testAsync
promise start..
test end...
promise //第五位
hello async
执行testSometing
testSometing
testSometing hello async
区别主要是’promise’的出现的位置,所以:
如果返回值为非promise,则继续执行async函数后面的代码,哪怕外面已经有任务队列在排队
否则将返回的promise,放入promise队列(Promise的Job Queue), 然后等待promise任务队列执行完之后,再执行await后面的代码