9 个Promises的必知高级用法

科技   2024-11-21 10:10   上海  


概述

Promise 对象表示异步操作的最终完成(或失败)及其结果值。

Promise 始终处于以下状态之一:

  • 待处理:初始状态,既未实现也未拒绝。

  • 已实现:操作已成功完成。

  • 已拒绝:操作失败。

与“旧式”回调不同,使用 Promises 具有以下约定:

  • 在当前事件循环完成之前,不会调用回调函数。

  • 即使异步操作完成(成功或失败),之后通过 then() 添加的回调仍将被调用。

  • 可以通过多次调用 then() 来添加多个回调,它们将按照添加的顺序执行。

Promises 的典型特征是链接。

一般用法

1.Promise.all([])

当数组中的所有 Promise 实例都成功时,它会按请求的顺序返回成功结果数组。如果任何 Promise 失败,它将进入失败回调。

const p1 = new Promise((resolve) => {    resolve(1);});const p2 = new Promise((resolve) => {    resolve(1);});const p3 = Promise.resolve('ok');
// If all promises succeed, result will be an array of 3 results.const result = Promise.all([p1, p2, p3]); // If one fails, the result is the failed promise's value.

2. Promise.allSettled([])

执行不会失败;它返回与输入数组中每个 Promise 实例的状态相对应的数组。

const p1 = Promise.resolve(1);const p2 = Promise.reject(-1);Promise.allSettled([p1, p2]).then(res => {    console.log(res);});// Output: /*   [    { status: 'fulfilled', value: 1 },    { status: 'rejected', reason: -1 }   ] */

3. Promise.any([])

如果输入数组中的任何 Promise 满足条件,则返回的实例将变为满足条件并返回第一个满足条件的 Promise 的值。如果所有 Promise 均被拒绝,则将变为拒绝条件。

const p1 = new Promise((resolve, reject) => {    reject(1);});const p2 = new Promise((resolve, reject) => {    reject(2);});const p3 = Promise.resolve("ok");
Promise.any([p1, p2, p3]).then( (r) => console.log(r), // Outputs 'ok' (e) => console.log(e));

4. Promise.race([])

只要数组中的任何 Promise 改变状态,race 方法的状态就会随之改变;第一个改变的 Promise 的值将传递给 race 方法的回调。

const p1 = new Promise((resolve) => {    setTimeout(() => {        resolve(10);    }, 3000);});const p2 = new Promise((resolve, reject) => {    setTimeout(() => {        throw new Error("I encountered an error");    }, 2000);});
Promise.race([p1, p2]).then( (v) => console.log(v), // Outputs 10 (e) => console.log(e));

抛出异常不会改变竞争状态;它仍然由 p1 决定。

高级用法

以下是 9 种高级用法,可帮助开发人员更高效、更优雅地处理异步操作。

01.并发控制

使用 Promise.all 允许并行执行多个 Promises,但要控制同时请求的数量,您可以实现并发控制功能。

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();        }    });};

02. Promise 超时

有时,可能希望 Promise 在特定时间范围内未解析时自动拒绝。这可以按如下方式实现。

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

03. 取消 Promises

原生 JavaScript Promises 无法取消,但你可以通过引入可控中断逻辑来模拟取消。

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;        }    };};

04. Promise 数组的顺序执行

有时您需要按顺序执行一系列 Promise,确保前一个异步操作完成后再开始下一个操作。

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

05. 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);    });};

06. 确保 Promise 仅解析一次

在某些情况下,您可能希望确保 Promise 仅解析一次,即使多次调用 resolve。

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

07. 使用 Promises 代替回调

Promises 通过替代回调函数,提供了一种更标准化、更方便的方式来处理异步操作。

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

08. 动态生成 Promise 链

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

const tasks = [task1, task2, task3]; // Array of asynchronous tasks
const promiseChain = tasks.reduce((chain, currentTask) => { return chain.then(currentTask);}, Promise.resolve());

09. 使用 Promises 实现简单的异步锁

在多线程环境中,可以使用 Promises实现简单的异步锁,确保每次只有一个任务可以访问共享资源。

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

这段代码不断创建和解析 Promise,实现了一个简单的 FIFO 队列,确保只有一个任务可以访问共享资源。

lock 变量表示当前是否有任务正在执行,始终指向正在进行的任务的 Promise。

acquireLock 函数请求执行权限并创建一个新的 Promise 以等待当前任务完成。

结论

Promise 是现代 JavaScript 异步编程不可或缺的一部分。

掌握它们的高级技巧将大大提高开发效率和代码质量。通过上述各种方法,开发人员可以更自信地处理复杂的异步场景,编写更易读、更优雅、更健壮的代码。

最后,感谢你的阅读,祝编程愉快!



学习更多技能

请点击下方公众号



web前端开发
一个专业而懂你的前端开发技术研究学习平台,每天与你分享【web前端开发】相关的技术文章与案例,互联网资讯,编程图书与课程,前端开发工具等学习资源,坚持每天学习一点点,每天进步一点点,关注我们,一起学习成长进步。
 最新文章