JS 的异步遍历

通常前端一看到要遍历数组,就会用 forEach。

// 有一个异步方法
function sleepPromise(msg, t) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`promise-${msg} ${t}s`);
    }, t * 1000);
  })
}

const tasks = [
    ['a', 3],
    ['b', 1],
    ['c', 2],
  ];

tasks.forEach(async task => {
  const [ msg, t ] = task;
  const m = await sleepPromise(msg, t);
  console.log(m);
})
// 输出结果是:
// promise-b 1s
// promise-c 2s
// promise-a 3s

这种写法并不对,其实是将遍历写成了同步。 问题处在forEach 并不支持异步。你在 forEach 方法的前面加不加 await 关键字都是无效的,因为它的内部没有处理异步的逻辑。

forEach 是es5 的api,promise 是es6 的方法,forEach以后也不会支持异步的。

试试用串联写法

// 串行写法
for (const task of tasks) {
  const [ msg, t ] = task;
  const m = await sleepPromise(msg, t);
  console.log(m);
}
// 输出的结果
// promise-a 3s
// promise-b 1s
// promise-c 2s

使用普通的 for 循环写法,await 的外层函数就仍就是 loopAysnc 方法,就能正确保存阻塞代码。

可以处理异步,但是串联写法会慢很多,耗时久。如果我们的这些请求是有顺序的依赖关系的,这样写是没问题。

如果没有顺序依赖,这样写就不合理,那我们需要写一个并行的异步。并且还要保证所有异步都执行完后才执行下一步。我们可以用 Promise.all() 

// 并行写法
const taskPromises = tasks.map(task => {
  const [ msg, t ] = task;
  return sleepPromise(msg, t).then(m => {
    console.log(m);
  });
});
await Promise.all(taskPromises);

首先,我们需要根据 tasks 数组生成对应的 promise 对象数组,然后传入到 Promise.all 方法中执行。

这样,这些异步方法就会同时执行。当所有异步都执行完毕后,代码才往下执行。

速度很快,很强大。

回到forEach , 前面说到 forEach 底层并没有实现异步的处理,才导致阻塞失效,那么我们其实不妨实现支持异步的简易 forEach。

async function forEach(arr, fn) {
  const fns = [];
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    fns.push(fn(item, i, arr));
  }
  await Promise.all(fns);
}

串行实现:

async function forEach(arr, fn) {
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    await fn(item, i, arr);
  }
}

用法

await forEach(tasks, async task => {
  const [ msg, t ] = task;
  const m = await sleepPromise(msg, t);
  console.log(m);
})

总结:

一般来说,我们更常用 Promise.all 的并行执行异步的方法,常见于数据库查找一些 id 对应的数据的场景

for 循环的串行写法适用于多个异步有依赖的情况,比如找最终推荐人。

forEach 则是纯粹的错误写法,除非是不需要使用 async/await 的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值