async/await函数的执行顺序的理解

本文深入探讨了async函数和Promise的工作机制,通过实例演示了await关键字如何影响代码执行流程,解释了为何await不会使外部变量立即更新,并提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近遇到一个关于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后面的代码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值