再有人问Promise,我就用这 9 个高级用法闪瞎他的眼!

AI 概述
常见方法基础用法1.  Promise.all([])2. Promise.allSettled([])3. Promise.any([])4. Promise.race([])高级用法1. 并发控制2.Promise 超时3. 取消 Promise4. 顺序执行 Promise 数组5. Promise 的重试逻辑6. 确保 Promise 只解析一次7. 使用 Promise 代替回调函数8. 动态生成 Promise 链9. 使用 Pr...
目录
文章目录隐藏
  1. 概述
  2. 常见方法
  3. 基础用法
  4. 高级用法
  5. 结论

再有人问 Promise,我就用这 9 个高级用法闪瞎他的眼!

概述

想象一下你点外卖:下单后,你并不知道外卖什么时候送达,也不知道会不会出问题。  Promise 就类似于这个外卖订单的“状态记录器”。它代表着一个异步操作(比如网络请求、文件读取)的最终结果。

这个“状态记录器”总共有三种状态:

  • 等待中 (Pending):  就像你刚下单,外卖还在准备中,不知道最终结果如何。
  • 已完成 (Fulfilled): 外卖送到了,你成功拿到餐食了!
  • 已拒绝 (Rejected): 外卖出问题了,比如商家缺货、配送员出了意外,你没拿到外卖。

传统的回调函数处理异步操作就像你一直盯着手机等外卖,还要处理各种可能的情况(例如:超时、商家联系不上等等)。而 Promise 就像一个更高级的“外卖监控系统”,它让你不用一直盯着,系统会自动通知你外卖的状态变化:

  • 它会在事件循环完成后才通知你结果(也就是你不会一直刷新页面查看状态)。
  • 即使外卖送达(无论成功或失败),你之前添加的“监控” then() 仍然会收到通知。
  • 你可以添加多个“监控”,它们会按照添加顺序依次收到通知。

Promise 最强大的功能是链式调用,就像你可以把外卖送达后“吃午餐”,“洗碗”这些步骤串联起来一样,一个步骤完成后自动执行下一个步骤,让整个流程变得清晰流畅。

常见方法

Promise 提供了几个常用的静态方法来方便地创建和操作多个 Promise:

  • Promise.all(promises):  同时等待多个 Promise 完成,只有全部完成才返回结果,任何一个失败则整个操作失败。  就像同时点了几家外卖,只有全部送达你才能开始吃饭。
  • Promise.allSettled(promises):  等待所有 Promise 完成,无论成功还是失败都返回结果,就像你点了几家外卖,无论哪家出了问题,你都会知道结果。
  • Promise.any(promises):  等待多个 Promise 中的任意一个完成,只要有一个成功就返回结果,全部失败才算失败。  就像你同时点了几家外卖,只要有一家送达,你就可以先吃。
  • Promise.race(promises):  等待多个 Promise 中最先完成的一个,无论成功还是失败。  就像你同时点了几家外卖,先到的一家决定你的用餐时间。
  • Promise.resolve(value): 创建一个已完成的 Promise,直接返回 value。就像外卖已经提前送到。
  • Promise.reject(reason): 创建一个已拒绝的 Promise,直接返回 reason。就像外卖送餐途中出事了。

此外,Promise 实例本身也有一些重要的方法,例如 then() 用于添加成功回调,catch() 用于添加失败回调,finally()  无论成功失败都会执行的回调。  这些方法共同构成了 Promise 的核心功能,提供了处理异步操作的强大机制。

基础用法

1.  Promise.all([])

当数组中所有 Promise 实例都成功时,它返回一个包含成功结果的数组,顺序与请求顺序相同。如果任何一个 Promise 失败,则进入失败回调。

const p1 = new Promise((resolve) => {
    resolve(1);
});
const p2 = new Promise((resolve) => {
    resolve(1);
});
const p3 = Promise.resolve('ok');

// 如果所有 Promise 都成功,result 将是一个包含 3 个结果的数组。
const result = Promise.all([p1, p2, p3]);
// 如果其中一个失败,result 将是失败 Promise 的值。

2. Promise.allSettled([])

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

const p1 = Promise.resolve(1);
const p2 = Promise.reject(-1);
Promise.allSettled([p1, p2]).then(res => {
    console.log(res);
});
// 输出:
/*
   [
    { 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), // 输出 '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), // 输出 10
    (e) => console.log(e)
);

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

高级用法

以下列出了 9 种高级用法,可以帮助开发者更高效、更优雅地处理异步操作。  每个用法都配有代码示例和详细解释。

1. 并发控制

使用 Promise.all 可以并行执行多个 Promise,但为了控制同时请求的数量,可以实现一个并发控制函数。  例如,限制同时进行的网络请求数量,避免服务器过载。

const concurrentPromises = async (promises, limit) => {
    const results = [];
    let running = 0;
    const next = () => {
        if (promises.length === 0) return;
        const p = promises.shift();
        running++;
        p().then(res => {
            results.push(res);
            running--;
            next();
        }).catch(err => {
            console.error("Error in concurrent promise:", err);
            running--;
            next();
        });
    };
    for (let i = 0; i < limit; i++) {
        next();
    }
    return new Promise(resolve => {
        const check = () => {
            if (running === 0 && promises.length === 0) {
                resolve(results);
            } else {
                setTimeout(check, 10); //检查是否所有 Promise 都执行完成
            }
        };
        check();
    });
};

const promises = [
    () => new Promise(resolve => setTimeout(() => resolve('promise1'), 1000)),
    () => new Promise(resolve => setTimeout(() => resolve('promise2'), 2000)),
    () => new Promise(resolve => setTimeout(() => resolve('promise3'), 500)),
    () => new Promise(resolve => setTimeout(() => resolve('promise4'), 1500)),
    () => new Promise(resolve => setTimeout(() => resolve('promise5'), 800))
];

concurrentPromises(promises, 2).then(results => console.log(results));
//输出结果顺序可能因并发执行而改变

2.Promise 超时

有时,您可能希望 Promise 在一定时间内未完成则自动拒绝。  这可以用于防止长时间等待无响应的请求。

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

const myPromise = new Promise(resolve => setTimeout(() => resolve('success'), 3000));

promiseWithTimeout(myPromise, 2000)
  .then(result => console.log(result)) // 可能输出 'success',也可能超时
  .catch(error => console.error(error)); // 可能输出超时错误

3. 取消 Promise

原生 JavaScript Promise 无法取消,但可以通过引入可控中断逻辑来模拟取消。  这对于需要在中途停止操作的情况非常有用。

let isCanceled = false;
const cancellablePromise = (promise) => {
  return new Promise((resolve, reject) => {
    promise.then(value => {
      if (isCanceled) {
        reject({ isCanceled: true, value });
      } else {
        resolve(value);
      }
    }).catch(error => {
      if (isCanceled) {
        reject({ isCanceled: true, error });
      } else {
        reject(error);
      }
    });
  })
};
//测试案例
const p = cancellablePromise(new Promise(res => setTimeout(() => res('OK'), 3000)));
setTimeout(() => isCanceled = true, 1000);
p.then(console.log).catch(console.error) // 输出错误信息和 isCanceled

4. 顺序执行 Promise 数组

有时您需要按顺序执行一系列 Promise,确保先前的异步操作完成之后才能开始下一个操作。  这对于依赖关系明确的任务非常重要。

const sequencePromises = async (promises) => {
    let result = null;
    for (const promise of promises) {
        result = await promise();
    }
    return result;
};

const promises = [
    () => new Promise(resolve => setTimeout(() => resolve('promise1'), 1000)),
    () => new Promise(resolve => setTimeout(() => resolve('promise2'), 500)),
    () => new Promise(resolve => setTimeout(() => resolve('promise3'), 2000))
];

sequencePromises(promises).then(result => console.log(result)); // 输出 promise3

5. Promise 的重试逻辑

当 Promise 因临时错误而被拒绝时,您可能希望重试其执行。  这对于网络请求等易受干扰的操作非常有用。

const retryPromise = async (promiseFn, maxAttempts, interval) => {
    let attempts = 0;
    while (attempts < maxAttempts) {
        try {
            const result = await promiseFn();
            return result;
        } catch (error) {
            console.error(`Attempt ${attempts + 1} failed:`, error);
            attempts++;
            await new Promise(resolve => setTimeout(resolve, interval));
        }
    }
    throw new Error('Max attempts reached');
};

const unreliablePromise = () => new Promise((resolve, reject) => {
    if (Math.random() < 0.5) {
        resolve('Success!');
    } else {
        reject(new Error('Failed!'));
    }
});

retryPromise(unreliablePromise, 3, 1000)
    .then(result => console.log(result))
    .catch(error => console.error(error));

6. 确保 Promise 只解析一次

在某些情况下,您可能希望确保 Promise 只解析一次,即使 resolve 被多次调用。  这可以防止意外的重复操作。

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


const myPromise = onceResolvedPromise((resolve) => {
    resolve('Success!');
    resolve('Another Success!'); //这个 resolve 不会被执行
});

myPromise.then(result => console.log(result)); // 输出 Success!

7. 使用 Promise 代替回调函数

Promise 提供了一种更标准化、更方便的方式来处理异步操作,取代回调函数,使代码更易读和维护。

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

// 模拟一个使用回调函数的异步操作
const asyncOperation = (callback) => {
    setTimeout(() => {
        const result = Math.random() > 0.5 ? 'Success' : 'Failure';
        callback(result === 'Failure' ? new Error('Operation failed') : null, result);
    }, 1000);
};

callbackToPromise(asyncOperation)
    .then(result => console.log('Success:', result))
    .catch(error => console.error('Error:', error));

8. 动态生成 Promise 链

在某些情况下,您可能需要根据不同的条件动态创建一系列 Promise 链。  这对于需要根据运行时数据决定执行流程的场景非常有用。

const tasks = [
    () => new Promise(resolve => setTimeout(() => resolve('Task 1'), 1000)),
    () => new Promise(resolve => setTimeout(() => resolve('Task 2'), 500)),
    () => new Promise(resolve => setTimeout(() => resolve('Task 3'), 2000))
];


const dynamicPromiseChain = async (tasks, condition) => {
    let result = null;
    for(const task of tasks){
        if(!condition){
            break;
        }
        result = await task();
    }
    return result;
};


dynamicPromiseChain(tasks, true)
  .then(result => console.log('Result:', result))
  .catch(error => console.error('Error:', error));

dynamicPromiseChain(tasks, false)
  .then(result => console.log('Result:', result))
  .catch(error => console.error('Error:', error));

9. 使用 Promise 实现简单的异步锁

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

let lock = Promise.resolve();

const acquireLock = async () => {
    await lock;
    return new Promise(resolve => {
        lock = new Promise(r => setTimeout(() => {r(); lock = Promise.resolve(); resolve();},1000));
    });
};

const accessResource = async () => {
    await acquireLock();
    console.log('Accessing resource...');
    await new Promise(resolve => setTimeout(resolve, 2000)); // 模拟访问资源
    console.log('Finished accessing resource.');
};

const runTask = async () => {
    await accessResource();
}

const run = async ()=>{
    await runTask();
    await runTask();
}

run();

这些示例展示了 Promise 高级用法的具体实现,可以帮助开发者更好地理解和应用这些技巧。  请注意,实际应用中可能需要根据具体情况进行调整。

结论

Promise 是现代 JavaScript 异步编程中不可或缺的一部分。掌握其高级技巧将极大地提高开发效率和代码质量。通过上述各种方法,开发者可以更自信地处理复杂的异步场景,编写更易读、更优雅、更健壮的代码。

来源公众号:前端宝哥

以上关于再有人问Promise,我就用这 9 个高级用法闪瞎他的眼!的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。

「点点赞赏,手留余香」

1

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 再有人问Promise,我就用这 9 个高级用法闪瞎他的眼!

发表回复