forEach或map循环遍历使用async/await执行顺序问题

博客围绕JavaScript异步循环执行顺序问题展开。业务中循环调用接口后执行后续操作时,不同写法会导致执行顺序不同。文中分析了forEach+async/await、for await of、Promise.all+map+async/await三种写法的执行顺序及原理,为处理异步任务提供参考。

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

出现原因

         出现这个问题主要是业务当中碰到了,基于这个问题研究了一下问题出现的原因以及解决的方法。

本质是想先进行循环调用接口,然后执行下面的操作,但是发现不同的写法会导致执行顺序有问题。

forEach+async/await例子

    function example1() {
      let promises = [
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3),
      ];
      promises.forEach(async (promise) => {
        let result = await promise;
        console.log(result);
      });
      console.log(4);
      console.log(5);
    }

    example1();

        先执行console.log(4),这是因为JavaScript的异步处理机制。在你的代码中,forEach循环中的每个Promise都被标记为async,这意味着它们将在后台运行,而主线程将继续执行后面的代码。因此,console.log(4)和console.log(5)将首先执行。

        当你在JavaScript中使用async/await时,你实际上是在创建一个新的Promise。这个Promise在后台运行,而主线程继续执行其他代码。当Promise完成时,它的结果将被返回,但这可能发生在主线程已经执行了其他代码之后。

        所以在例子中,forEach循环中的Promise可能还在运行,而主线程已经执行了console.log(4)和console.log(5)。这就是为什么你看到4和5首先被打印出来,然后是1、2和3。

    function example1() {
      let promises = [
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3),
      ];
      promises.forEach(async (promise) => {
        // let result = await promise;
        console.log(promise);
        //console.log(await promise);//4 5 1 2 3
      });
      console.log(4);
      console.log(5);
    }

    example1();

        Promise的构造函数是同步的,Promise.then是异步的,加上await,实际上是调用了then方法,所以会先执行同步任务,后执行Promise.then这个微任务。所以打印await promise是4 5 1 2 3

for await of例子

async function example() {
      let promises = [
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3),
      ];
      for await (let result of promises) {
        console.log(result);
      }
      console.log(4);
      console.log(5);
    }
    example();

        在这个例子中,使用了for await...of循环,这是一种特殊的循环,用于处理异步操作,特别是Promise。

        for await...of循环会等待每个Promise解析,然后打印结果。这意味着它会按照顺序打印1,2,3。只有当所有的Promise都解析完毕,循环才会结束,然后才会执行并打印4和5。

        所以,执行顺序将会是:1,2,3,4,5。

        第二个例子与第一个例子不同,因为在第一个例子中,使用的是forEach循环和async函数,这会导致Promise在后台运行,而主线程继续执行其他代码。但在这个例子中,for await...of循环确保了在继续执行其他代码之前,所有的Promise都已经解析完毕。

两者的区别就在写法上面,如果把第二种情况做一个改写,也会出现第一种情况的结果。

    function example() {
      let promises = [
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3),
      ];

      for (let result of promises) {
        async function log() {
          console.log(await result);
        }
        log();
      }
      console.log(4);
      console.log(5);
    }
    example();

Promise.all+map+async/await写法

    function example1() {
      let promises = [
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3),
      ];

      Promise.all(
        promises.map(async (promise) => {
          let result = await promise;
          console.log(result);
        })
      ).then(() => {
        console.log(4);
        console.log(5);
      });
    }

    example1();

reduce写法 


async  function example1() {
    let promises = [
      Promise.resolve(1),
      Promise.resolve(2),
      Promise.resolve(3),
    ];
   await promises.reduce( (l,item) => {
     return l.then(()=>{
        item.then(res=>{
            console.log(res);
        })
        return item
     })
    },Promise.resolve());
    console.log(4);
    console.log(5);
  }

  example1();

在 JavaScript 中,reduce 函数用于数组的累积操作,而传递给 reduce 的第二个参数是初始值。如果你传递了 Promise.resolve() 作为初始值,那么 reduce 就可以用于在异步操作中进行累积。

在上面的例子中,reduce 中的初始值是 Promise.resolve()。每次迭代都返回一个 Promise,该 Promise 在异步操作完成后调用 resolve。这确保了数组中的每个元素都在前一个异步操作完成后处理。

这种方式可以确保异步操作按顺序执行,并且是 reduce 的一种常见用法,特别是当你需要对异步操作的结果进行累积时。这种写法适用于需要按照一定顺序处理一组异步任务的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值