【JavaScript】await 性能太差?新写法性能提升80%
异步编程早已成为JavaScript的核心能力,从早期令人头疼的回调地狱,到Promise链式调用的优化,再到async/await带来的“同步化”语法体验,JavaScript异步处理方式持续迭代升级。但即便async/await让代码可读性大幅提升,在部分场景下仍会产生不必要的性能损耗。本文将分享一种新一代异步编程范式,在特定场景下能实现最高80%的性能提升。
async/await虽然语法优雅,但其本质是基于Promise和生成器函数的语法糖。每次使用await关键字时,JavaScript引擎都会创建执行暂停点,保存当前的执行上下文,待异步操作完成后再恢复执行流程。这一过程涉及上下文切换与状态管理,在高频调用或计算密集型场景中,会造成明显的性能开销。
// 传统async/await写法
async function fetchData() {
const result = await fetch('https://api.example.com/data');
const data = await result.json();
return data;
}
减少不必要的await使用,改用Promise链式调用,可有效降低上下文切换的频次:
这种写法规避了两次await带来的上下文切换成本,在高频调用场景下能带来显著的性能提升。
当多个异步操作彼此无依赖关系时,使用Promise.all实现并行执行:
并行执行可将原本三个异步操作的总耗时,从“逐个执行的时间总和”缩短为“耗时最长的单个操作时间”。
针对需要处理大批量异步操作的场景,采用批处理而非await循环的方式,能大幅提升执行效率:
当需要限制异步操作并发数量时,Promise池化技术比单纯的await循环更高效:
function promisePool(items, concurrency, iteratorFn) {
let i = 0;
const results = [];
const executing = newSet();
function enqueue() {
if (i === items.length) returnPromise.resolve();
const item = items[i++];
const promise = Promise.resolve(iteratorFn(item, i - 1));
results.push(promise);
executing.add(promise);
return promise.finally(() => {
executing.delete(promise);
return enqueue();
});
}
returnPromise.all(
Array(Math.min(concurrency, items.length))
.fill()
.map(() => enqueue())
).then(() =>Promise.all(results));
}
// 池化调用示例
function processItemsPooled(items) {
return promisePool(items, 5, processItem);
}
我们针对上述优化方案在不同场景下开展了性能测试,结果显示:
阅读原文:原文链接