雨巷 (rainto.top)

JavaScript异步新写法

笔记 / 

async/await的性能瓶颈 async/await虽然优雅,但它实际上是基于Promise和生成器函数的语法糖。每次使用await关键字时,JavaScript引擎都会创建一个暂停点,保存当前执行上下文,并在异步操作完成后恢复执行。这个过程涉及到上下文切换和状态管理,在高频调用或计算密集型应用中可能导致显著的性能开销。

传统的async/await用法

js
async function fetchData() {
  const result = await fetch('https://api.example.com/data');
  const data = await result.json();
  return data;
}

新一代异步处理方法

1. Promise链式优化

避免不必要的await,改用promise链式调用可以减少上下文切换。这种写法避免了两次await的上下文切换,在高频调用场景下性能提升显著。

js
function fetchData() {
  return fetch('https://api.example.com/data')
    .then(response => response.json())
}

2. 并行执行 Promise.all

当多个异步操作之间没有依赖关系时,使用Promise.all可以并行执行它们。并行执行可以将总执行时间从三个操作的总和减少到最长操作的时间。

js
// 低效写法
async function fetchMultipPaprallel() {
  const [result1, result2] = await Promise.all([
    const data1 = fetch('url1');
    const data2 = fetch('url2');
    const data3 = fetch('url3');
    return [data1, data2, data3];
  ]);
}

// 高效写法
async function fetchMultipPaprallel() {
  return Promise.all([
    fetchData('url1'),
    fetchData('url2'),
    fetchData('url3')
  ]);
}

3. Promise批处理

对于需要处理大量异步操作的场景,使用批处理而非await循环可以显著提高性能。

js
// 低效写法
async function processItems(urls) {
  const results = [];
  for (const url of urls) {
    const data = await fetch(url);
    results.push(data);
  }
  return results;
}

// 高效写法
function processItemsBatched(urls) {
  // const promises = urls.map(url => fetch(url));
  // return Promise.all(promises);
  const batches = [];
  const batchSize = 5; // 每批处理5个请求
  for (let i = 0; i < urls.length; i += batchSize) {
    const batch = urls.slice(i, i + batchSize);
    batches.push(Promise.all(batch.map(url => fetch(url))));
  }
  return Promise.all(batches).then(results => {
    return results.flat();
  });
}

4. Promise池化技术

当需要控制并发数量时,使用Promise池比await循环更高效

js
function promisePool(items, concurrency, iteratorFn) {
  let i = 0;
  const results = [];
  const executing = newSet();
  
  functionenqueue() {
      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);
        returnenqueue();
      });
    }
  
  returnPromise.all(
      Array(Math.min(concurrency, items.length))
        .fill()
        .map(() =>enqueue())
    ).then(() =>Promise.all(results));
  }
  
  // 使用方式
  functionprocessItemsPooled(items) {
  returnpromisePool(items, 5, processItem);
  }