不要在循环await啦,异步操作的六个最佳实践!

开发 前端
前两天有个同学在面试中被问到了一个问题:“如果在请求多个不同的接口,那么应该如何去做?” 该同学回答说:“我们可以把这些接口放到一个数组中,然后通过 for 循环来循环请求!”

前两天有个同学在面试中被问到了一个问题:“如果在请求多个不同的接口,那么应该如何去做?” 该同学回答说:“我们可以把这些接口放到一个数组中,然后通过 for 循环来循环请求!”

嗯...这确是是一个方式,不过这并不好。再加上异步问题现在已经成了面试中的常见问题,所以,今天咱们就来说一下 异步请求的最佳实践,帮助大家解决异步编程,以及面试问题。

1.不使用 await 的循环请求

我们不应该在循环内使用 await。 而是可以利用 promise.all 方法:

// ❌async function fn(reqs) {  const results = [];  for (const req of reqs) {    // 每次循环迭代都会延迟到整个异步操作完成    results.push(await req);  }  return results;}

// ✅
async function fn(reqs) {
  // 存储所有异步操作的 Promise
  const promises = reqs.map((req) => req); // 所有异步操作都已经开始,现在等待它们全部完成
  const results = await Promise.all(promises);
  return results;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

2.不要在 promise 中执行返回操作

不要在 Promise 构造函数中返回值。 从那里返回的值是无用的。 它们也不影响 Promise 的状态。

  • 正确的方法是使用 resolve 传递值。
  • 如果有错误,则使用 reject 传递错误。
// ❌
new Promise((resolve, reject) => {
    // 返回没有意义
    if (b) {
        return result;
    }
});

// ✅
new Promise((resolve, reject) => {
    // 利用 resolve 传递
    if (b) {
        resolve(result);
        return;
    }
});
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

3.避免竞态问题

看以下代码,你认为最终打印会是多少?

// ❌
let totalPosts = 0;

async function getPosts(userId) {
  // 模拟获取用户的帖子数量
  const users = [
    { id: 1, posts: 5 },
    { id: 2, posts: 3 },
  ]; // 模拟异步延迟
  await sleep(Math.random() * 1000); // 返回对应用户的帖子数量
  return users.find((user) => user.id === userId).posts;
}

async function sleep(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
}

async function addPosts(userId) {
  // 将用户的帖子数量加到总帖子数上
  totalPosts += await getPosts(userId);
}

// 并行地获取两个用户的帖子数量,并在全部获取完毕后输出总帖子数
await Promise.all([addPosts(1), addPosts(2)]);
console.log("帖子数量:", totalPosts);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

执行以上代码,你可能会打印 3 或 5,而不是 8。

这个原因就是因为 竞态条件 问题而导致的。当在单独的函数调用中更新值时,更新不会反映在当前函数作用域中。 因此,这两个函数都将其结果添加到初始的 TotalPosts 值 0 中。

以下是避免竞态条件的方式:

// ✅
let totalPosts = 0;

async function getPosts(userId) {
  const users = [
    { id: 1, posts: 5 },
    { id: 2, posts: 3 },
  ];
  await sleep(Math.random() * 1000);
  return users.find((user) => user.id === userId).posts;
}

async function sleep(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
}

async function addPosts(userId) {
  const posts = await getPosts(userId);
  totalPosts += posts; // 变量被读取并立即更新
}

await Promise.all([addPosts(1), addPosts(2)]);
console.log("帖子数量:", totalPosts);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

4.避免回调地狱

这个问题大家应该很好理解,直接看代码即可

// ❌
async1((err, result1) => {
  async2(result1, (err, result2) => {
    async3(result2, (err, result3) => {
      async4(result3, (err, result4) => {
        console.log(result4);
      });
    });
  });
});

// ✅
const result1 = await asyncPromise1();
const result2 = await asyncPromise2(result1);
const result3 = await asyncPromise3(result2);
const result4 = await asyncPromise4(result3);
console.log(result4);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

5.避免直接返回 await

尽量避免直接返回 await ,特别是在需要 try..catch 的时候:

// ❌
async () => {
  try {
    return await getUser(userId);
  } catch (error) {
    // 输出错误
  }
};

// ✅
async () => {
  try {
    const user = await getUser(userId);
    return user;
  } catch (error) {
    // 输出错误
  }
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

6.reject 最好配合 Error 使用

// ❌
Promise.reject('这是一个错误');

// ✅
Promise.reject(new Error('这是一个错误'));
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.


责任编辑:武晓燕 来源: 程序员Sunday
相关推荐

2024-04-15 12:27:00

await面试接口

2013-03-06 09:26:20

云服务云实践精准管理

2022-05-26 11:11:19

Kubernetes容器云安全

2013-03-06 10:54:03

云服务实践关键步骤

2025-03-13 00:00:05

2011-10-28 16:21:02

数据虚拟化服务器

2021-07-19 10:06:30

数据治理数字化转型CIO

2021-10-08 08:00:00

Java开发功能

2017-06-19 09:12:08

JavaScriptPromiseAsync

2018-05-11 09:25:46

全闪存阵列实践

2009-07-08 11:27:05

敏捷方法

2020-07-24 00:41:18

物联网项目物联网IOT

2023-07-18 15:11:01

2023-04-14 08:10:59

asyncawait

2021-08-07 09:32:23

数据治理数字化转型CIO

2020-03-16 08:00:00

物联网项目物联网IOT

2023-09-13 08:00:00

JavaScript循环语句

2017-06-06 15:10:42

框架APP设计

2022-11-15 16:54:54

2022-01-17 11:25:46

代码Pythonfor
点赞
收藏

51CTO技术栈公众号