How AsyncAwait Can Slow Down Your Node.js App
在 Node.js 世界里,async/await 已经成为了处理异步操作的标准。它干净、易读,感觉像同步一样。虽然听起来很棒,错误使用 async/await 可能会导致性能瓶颈,从而使应用变慢。
尽管使用了 async/await,你的应用程序仍然感觉卡顿,这篇文章就是为你写的。
为什麼使用 Async/Await 感觉好慢底层实现中,async/await 并没有做什么魔法 — 它只是将你的异步操作包裹成一个 Promise。如果使用不当,async/await 可能会引入 不必要的阻塞操作 和 顺序处理 ,这会削弱 Node.js 的非阻塞和异步特性带来的优势。
这里有一些常见的场景,可能会拖慢你的应用程序,当使用异步/await时。
1. 顺序执行,而不是并行执行异步/等待会一次只执行一个函数,除非你明确地将它们并行执行,使表达更贴合编程背景。举个例子:
async function fetchUserData() {
const user = await getUser(); // 第一个获取请求
const orders = await getOrders(user.id); // 第二个获取请求
const profile = await getUserProfile(user.id); // 第三个获取请求
return { user, orders, profile: 个人资料 };
}
在这里,getUser
、getOrders
和 getUserProfile
的调用是按顺序执行的。每个调用都会等待前一个结束,从而增加了整体执行时间。
修复:使用Promise.all并行执行异步函数
为了不按顺序执行,我们可以把这些承诺并行处理:
async function 获取用户数据() {
const [用户, 订单, 资料] = await Promise.all([
获取用户(),
获取订单(用户ID),
获取用户资料(用户ID),
]);
return { 用户, 订单, 资料 };
}
使用 Promise.all
,这三个请求会同时执行,大大缩短了执行时间。
在循环中获取数据时,这种情况很常见。
async function fetchOrderDetails(orders) {
const details = [];
for (const order of orders) {
const detail = await getOrderDetail(order.id); // 等待每个
details.push(detail);
}
return details;
}
每个 getOrderDetail
调用都等待前一个完成。如果你需要处理数百个订单,这种方法可能会非常慢,速度会让人感到痛苦。
使用**Promise.all**
在循环中处理多个异步操作
而不是一个接一个地等待,同时发起所有请求:
async function 获取订单详情(订单) {
const 详情 = 等待Promise.all(
订单.map(订单 => 获取订单详情(订单.id))
);
返回 详情;
}
这会大大提高性能,因为会并发运行这些承诺。
3. 拦截长尾操作如果你将长时间运行的异步操作任务与关键代码混合,异步/等待的阻塞特性可能会延迟重要任务的执行。
async function processRequest(req) {
const user = await getUser(req.userId); // 必须的
const analytics = await updateAnalytics(req); // 虽然不关键,但会阻塞用户获取
return { user, status: '已处理' };
}
虽然updateAnalytics
是非关键性的操作,但还是拖慢了整体响应速度。
处理:把不太重要的任务单独处理一下
把不太重要的操作从主要流程里挪出去:
async function processRequest(req) {
const user = await getUser(req.userId);
// 更新分析数据(无需等待结果)
updateAnalytics(req).catch(err => console.error('错误信息:', err));
return { user, status: '已处理' };
}
现在,updateAnalytics
在后台运行,不会影响到响应速度。
- 批量处理承诺们
使用Promise.all
来分组可以并行运行的承诺们。 - 限制并发数
如果需要处理大量并行请求,可以使用[p-limit](https://www.npmjs.com/package/p-limit)
这样的库来控制并发。
const pLimit = require('p-limit');
const limit = pLimit(5); // 限制并发请求最多5个
const results = await Promise.all(
tasks.map(task => limit(() => asyncTask(task))) // 对每个任务执行异步任务,限制并发数
);
- 避免将已经同步的代码转换为异步/等待。
保持异步工作流程独立且高效。 - 按需调用昂贵的API。
使用条件逻辑来决定何时调用它们。
const profile = condition ? await fetchProfile(user.id) : null;
// 如果 condition 为真,则获取用户档案,否则 profile 为 null。
- 使用工具进行调试
工具如Clinic.js和Node.js自带的性能分析工具可以帮助识别异步问题。
异步/await是一种提升可读性和可维护性的绝佳工具,但也容易被误用。
记得,Node.js 擅长处理非阻塞的并发。明智地使用 async/await 函数,并在适当情况下利用并行处理(例如使用 Promise.all
)。
你可能也会喜欢:
2)使用Artillery(负载测试工具)进行压力测试:为Node.js应用准备应对高峰流量期
4)[ 高级JavaScript Promise面试问题]( https://blog.arunangshudas.com/can-you-answer-this-senior-level-javascript-promise-interview-question/)
5)数据库索引是什么,为什么它很重要? ,了解数据库索引及其重要性。
8)基于令牌的身份验证方法:在 JWT 和 Paseto 之间做选择以适用于现代应用
9)针对高流量 API 的 Node.js 中的 API 滥用预防和速率限制策略
更多相关博客请点击这里这里
在评论中分享一下你的经历,并让我们一起讨论如何解决这些!
关注我领英