20几个你不得不知道的Promise高级用法

在JavaScript中,Promise是一种解决异步编程问题的重要方式。一个Promise对象代表了一个将要在本次操作完成后立即、稍后或从未实现的返回值。以下是23个高级用法的探讨,每一个都在JavaScript的海洋中航行,让开发者们能够以更高效、优雅的方式处理异步操作。

1. 并发控制

使用Promise.all可以并行执行多个Promise,但当需要控制并发的请求数量时,可以通过实现一个并发控制函数来控制同时执行的Promise数量。

javascript 复制代码
const concurrentPromises = (promises, limit) => {
  return new Promise((resolve, reject) => {
    let i = 0;
    let result = [];
    const executor = () => {
      if (i >= promises.length) {
        return resolve(result);
      }
      const promise = promises[i++];
      Promise.resolve(promise)
        .then(value => {
          result.push(value);
          if (i < promises.length) {
            executor();
          } else {
            resolve(result);
          }
        })
        .catch(reject);
    };
    for (let j = 0; j < limit && j < promises.length; j++) {
      executor();
    }
  });
};

2. Promise超时

有时候,我们希望Promise在一定时间内如果没有得到解决就自动reject。这可以用下面的方式实现。

javascript 复制代码
const promiseWithTimeout = (promise, ms) =>
  Promise.race([
    promise,
    new Promise((resolve, reject) =>
      setTimeout(() => reject(new Error('Timeout after ' + ms + 'ms')), ms)
    )
  ]);

3. Promise的取消

JavaScript原生的Promise是无法取消的,但我们可以通过引入一个可控的中断逻辑来模拟取消Promise。

javascript 复制代码
const cancellablePromise = promise => {
  let isCanceled = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then(
      value => (isCanceled ? reject({ isCanceled, value }) : resolve(value)),
      error => (isCanceled ? reject({ isCanceled, error }) : reject(error))
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      isCanceled = true;
    }
  };
};

4. 检测Promise状态

原生的Promise不允许直接查询状态。但可以通过一定的技巧来了解当前Promise是否已解决、被拒绝或尚未解决。

javascript 复制代码
const reflectPromise = promise =>
  promise.then(
    value => ({ status: 'fulfilled', value }),
    error => ({ status: 'rejected', error })
  );

5. 顺序执行Promise数组

有时候我们需要按顺序执行一组Promise,以确保前一个异步操作完成后再开始下一个。

javascript 复制代码
const sequencePromises = promises =>
  promises.reduce(
    (prev, next) => prev.then(() => next()),
    Promise.resolve()
  );

6. 基于条件的Promise链

在某些场合下,需要根据条件判断是否执行下一个Promise。

javascript 复制代码
const conditionalPromise = (conditionFn, promise) =>
  conditionFn() ? promise : Promise.resolve();

7. Promise的重试逻辑

当Promise因为某些暂时性的错误被拒绝时,可能希望能够重试执行。

javascript 复制代码
const retryPromise = (promiseFn, maxAttempts, interval) => {
  return new Promise((resolve, reject) => {
    const attempt = attemptNumber => {
      if (attemptNumber === maxAttempts) {
        reject(new Error('Max attempts reached'));
        return;
      }
      promiseFn().then(resolve).catch(() => {
        setTimeout(() => {
          attempt(attemptNumber + 1);
        }, interval);
      });
    };
    attempt(0);
  });
};

8. 确保Promise只解决一次

在某些情况下,可能希望确保Promise只会解决一次,即使可能会被多次调用resolve

javascript 复制代码
const onceResolvedPromise = executor => {
  let isResolved = false;
  return new Promise((resolve, reject) => {
    executor(
      value => {
        if (!isResolved) {
          isResolved = true;
          resolve(value);
        }
      },
      reject
    );
  });
};

9. 使用Promise.allSettled处理多个异步操作

Promise.all不同的是,Promise.allSettled会等到所有的prromise都结束后才完成,无论每个promise结束后是fulfilled还是rejected。

javascript 复制代码
const promises = [fetch('/api/endpoint1'), fetch('/api/endpoint2')];
Promise.allSettled(promises).then(results => {
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      console.log(`Promise ${index + 1} succeeded with value ${result.value}`);
    } else {
      console.error(`Promise ${index + 1} failed with reason ${result.reason}`);
    }
  });
});

10. 处理多个Promises的最快响应

当处理多个异步请求,而只关心最快回应的结果时,可以使用Promise.race来实现。

javascript 复制代码
const promises = [fetch('/api/endpointA'), fetch('/api/endpointB')];
Promise.race(promises)
  .then(value => {
    // 处理最快的响应
  })
  .catch(reason => {
    // 处理最早的拒绝
  });

11. 使用async/await简化Promise

asyncawait关键字可以让异步代码看起来更像同步代码,使得逻辑更清晰。

javascript 复制代码
async function asyncFunction() {
  try {
    const result = await aPromise;
    // Do something with result
  } catch (error) {
    // Handle error
  }
}

12. 连续获取不确定数量的数据页

当获取分页数据时,我们可能不知道一共有多少页,可以采取递归的方式直到取完所有页。

javascript 复制代码
async function fetchPages(apiEndpoint, page = 1, allResults = []) {
  const response = await fetch(`${apiEndpoint}?page=${page}`);
  const data = await response.json();
  if (data.nextPage) {
    return fetchPages(apiEndpoint, page + 1, allResults.concat(data.results));
  } else {
    return allResults.concat(data.results);
  }
}

13. 映射并发Promises并处理结果数组

当需要并发执行异步函数并处理所有结果时,可以使用Promise.all

javascript 复制代码
const fetchUrls = urls => {
  const fetchPromises = urls.map(url => fetch(url).then(response => response.json()));
  return Promise.all(fetchPromises);
};

14. 使用Generators管理流程

通过将async/await与Generators配合,可以创建一个可控制的异步流程管理器。

javascript 复制代码
function* asyncGenerator() {
  const result1 = yield aPromise1;
  const result2 = yield aPromise2(result1);
  // ...
}

15. 使用Promises替代回调

Promise提供了一种更标准和便捷的方式来处理异步操作,将回调函数替换为Promise。

javascript 复制代码
const callbackToPromise = (fn, ...args) => {
  return new Promise((resolve, reject) => {
    fn(...args, (error, result) => {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });
};

16. 流式处理大型数据集

使用Promise处理大型数据集时,最好是流式地获取和处理这些数据,以避免内存过载。

javascript 复制代码
async function processLargeDataSet(dataSet) {
  for (const dataChunk of dataSet) {
    const processedChunk = await process(dataChunk); // Returns a Promise
    await save(processedChunk); // Another async operation
  }
}

17. 同时执行多个异步任务并处理中途的失败

有时即便其中一个异步任务失败了,也希望其他任务能够顺利完成。

javascript 复制代码
const promises = [promise1, promise2, promise3];
Promise.all(promises.map(reflectPromise)).then(results => {
  results.forEach(result => {
    if (result.status === 'fulfilled') {
      // Do something with result.value
    } else {
      // Handle result.error
    }
  });
});

18. Promise-pipeline

通过管道化promise可以依次执行一系列异步操作。

javascript 复制代码
const promisePipe = (...fns) => value => fns.reduce((p, f) => p.then(f), Promise.resolve(value));

19. 使用promise实现一个延时

可以使用Promise结合setTimeout来实现一个异步的延时函数。

javascript 复制代码
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

20. 动态生成Promise链

在一些情况下,可能需要根据不同条件动态生成一系列的Promise链。

javascript 复制代码
const tasks = [task1, task2, task3]; // Array of asynchronous tasks

const promiseChain = tasks.reduce((chain, currentTask) => {
  return chain.then(currentTask);
}, Promise.resolve());

21. 使用Promise实现简易的异步锁

在多线程环境中,可以使用Promise来实现一个简易的异步锁。

javascript 复制代码
let lock = Promise.resolve();

const acquireLock = () => {
  let release;
  const waitLock = new Promise(resolve => {
    release = resolve;
  });
  const tryAcquireLock = lock.then(() => release);
  lock = waitLock;
  return tryAcquireLock;
};

22. 组合多个Promise操作为一个函数

可以将多个Promise操作合并为一个函数,通过函数复用减少冗余代码。

javascript 复制代码
const fetchDataAndProcess = async url => {
  const data = await fetch(url).then(resp => resp.json());
  return processData(data);
};

23. 处理可选的异步操作

有些场合下,一个异步操作是可选的,可以使用下面的方式来处理。

javascript 复制代码
async function optionallyAsyncTask(condition, asyncOperation, fallbackValue) {
  if (condition) {
    return await asyncOperation;
  } else {
    return fallbackValue;
  }
}

结语

Promise是现代JavaScript异步编程不可或缺的一部分,精通其高级技巧将大大提升开发效率和代码质量。通过上面介绍的多种用法,开发者们可以更自信地处理各种复杂的异步场景,并能够写出更可读、更优雅、更健壮的代码。

相关推荐
前端大卫21 分钟前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘36 分钟前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare37 分钟前
浅浅看一下设计模式
前端
Lee川41 分钟前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix1 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人1 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人1 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼1 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端