JavaScript中你不得不知道的Promise高级用法分享

 更新时间:2023年12月03日 16:58:14   作者:慕仲卿  
在JavaScript中,Promise是一种解决异步编程问题的重要方式,本文主要来和大家探讨一下23个Promise的高级用法,每一个都在JavaScript的海洋中航行,让开发者们能够以更高效、优雅的方式处理异步操作,希望对大就有所帮助

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

1. 并发控制

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

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。这可以用下面的方式实现。

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。

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是否已解决、被拒绝或尚未解决。

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

5. 顺序执行Promise数组

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

const sequencePromises = promises =>
  promises.reduce(
    (prev, next) => prev.then(() => next()),
    Promise.resolve()
  );

6. 基于条件的Promise链

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

const conditionalPromise = (conditionFn, promise) =>
  conditionFn() ? promise : Promise.resolve();

7. Promise的重试逻辑

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

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

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。

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来实现。

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

11. 使用async/await简化Promise

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

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

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

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

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

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

14. 使用Generators管理流程

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

function* asyncGenerator() {
  const result1 = yield aPromise1;
  const result2 = yield aPromise2(result1);
  // ...
}

15. 使用Promises替代回调

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

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

16. 流式处理大型数据集

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

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

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

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

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可以依次执行一系列异步操作。

const promisePipe = (...fns) => value => fns.reduce((p, f) => p.then(f), Promise.resolve(value));

19. 使用promise实现一个延时

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

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

20. 动态生成Promise链

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

const tasks = [task1, task2, task3]; // Array of asynchronous tasks

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

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

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

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操作合并为一个函数,通过函数复用减少冗余代码。

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

23. 处理可选的异步操作

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

async function optionallyAsyncTask(condition, asyncOperation, fallbackValue) {
  if (condition) {
    return await asyncOperation;
  } else {
    return fallbackValue;
  }
}

结语

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

以上就是JavaScript中你不得不知道的Promise高级用法分享的详细内容,更多关于JavaScript Promise的资料请关注脚本之家其它相关文章!

相关文章

  • 读JavaScript DOM编程艺术笔记

    读JavaScript DOM编程艺术笔记

    读JavaScript DOM编程艺术笔记,需要的朋友可以参考下。
    2011-11-11
  • 详解微信小程序支付流程与梳理

    详解微信小程序支付流程与梳理

    这篇文章主要介绍了详解微信小程序支付流程与梳理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • Use Word to Search for Files

    Use Word to Search for Files

    Use Word to Search for Files...
    2007-06-06
  • 纯javascript实现自动发送邮件

    纯javascript实现自动发送邮件

    当我们发送邮件时,可以自定义邮件发送的时间,那么使用代码是如何实现的呢?下面通过本篇文章给大家介绍使用纯javascript实现自动发送邮件,感兴趣的朋友可以参考下
    2015-10-10
  • layui 监听表格复选框选中值的方法

    layui 监听表格复选框选中值的方法

    今天小编就为大家分享一篇layui 监听表格复选框选中值的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • js日期相关函数总结分享

    js日期相关函数总结分享

    最近写了一个倒计时程序,因为经常要在手机端访问,所以没有引用jquery,对于用习惯jquery的我还真不习惯。下面简单说明js日期相关函数,并说明实现倒计时的原理
    2013-10-10
  • uniapp在微信小程序中图片宽度显示问题示例代码

    uniapp在微信小程序中图片宽度显示问题示例代码

    在uniapp中,如果你的富文本图片显示宽度不正常,你可以通过设置图片的宽高属性来解决这个问题,这篇文章主要介绍了uniapp在微信小程序中图片宽度显示问题,需要的朋友可以参考下
    2023-02-02
  • javascript for循环性能测试示例

    javascript for循环性能测试示例

    这篇文章主要介绍了javascript for循环性能测试,结合实例形式分析了javascript使用for循环遍历数组的三种常用方法及对应的时间消耗,总结javascript使用for循环遍历数组的相关操作技巧,需要的朋友可以参考下
    2019-08-08
  • JavaScript实现三阶幻方算法谜题解答

    JavaScript实现三阶幻方算法谜题解答

    这篇文章主要介绍了JavaScript实现三阶幻方算法谜题解答,三阶幻方是指试将1~9这9个不同整数填入一个3×3的表格,使得每行、每列以及每条对角线上的数字之和相同,需要的朋友可以参考下
    2014-12-12
  • Javascript中匿名函数的多种调用方式总结

    Javascript中匿名函数的多种调用方式总结

    这篇文章主要是对Javascript中匿名函数的多种调用方式进行了详细的总结介绍。需要的朋友可以过来参考下,希望对大家有所帮助
    2013-12-12

最新评论