Promise.all的简单实现

Promise.all的简单实现

Promise.all = arr => {
    let aResult = [];    //用于存放每次执行后返回结果
    return new _Promise(function (resolve, reject) {
      let i = 0;
      next();    // 开始逐次执行数组中的函数(重要)
      function next() {
        arr[i].then(function (res) {
          aResult.push(res);    // 存储每次得到的结果
          i++;
          if (i == arr.length) {    // 如果函数数组中的函数都执行完,便resolve
            resolve(aResult);
          } else {
            next();
          }
        })
      }
    })
  };
<think>我们正在讨论JavaScript中的Promise.allPromise.allSettled,并手动实现Promise.all。 根据引用,我们可以总结如下: 1. Promise.all: - 接受一个Promise实例数组(或任何可迭代对象)。 - 返回一个新的Promise,该Promise的状态取决于所有输入的Promise: - 当所有输入的Promise都成功解决(resolved)时,返回的Promise才会被解决(resolved),并且结果是一个数组,按顺序包含每个输入Promise的解决值。 - 如果其中任何一个输入Promise被拒绝(rejected),则返回的Promise会立即被拒绝,并带有第一个被拒绝的Promise的原因(reason)。 - 特性:快速失败(fast-fail),即一个失败立即导致整个Promise.all失败。 2. Promise.allSettled: - 同样接受一个Promise实例数组(或任何可迭代对象)。 - 返回一个新的Promise,该Promise在所有输入的Promise都已敲定(settled,即无论成功或失败)时被解决(resolved)。 - 结果是一个对象数组,每个对象表示对应输入Promise的结果。每个对象都有一个status属性: - 如果状态为'fulfilled',则对象还有value属性,包含解决值。 - 如果状态为'rejected',则对象还有reason属性,包含拒绝原因。 - 特性:不会失败,它总是会等待所有Promise完成,并报告每个Promise的结果。 现在,用户要求手动实现Promise.all。我们将按照以下步骤实现: 手动实现Promise.all的思路: 1. 返回一个新的Promise。 2. 检查输入是否是可迭代对象(如数组),如果不是,可以立即拒绝(但通常我们假设输入是数组)。 3. 初始化一个数组来存储结果,并设置一个计数器来跟踪已解决的Promise数量。 4. 遍历输入数组中的每个Promise: - 使用Promise.resolve()包装每个元素,以确保它是Promise(因为输入可能包含非Promise值)。 - 为每个Promise设置then处理程序: 当某个Promise解决时,将其值存入结果数组的对应位置,然后计数器加1。 如果计数器达到输入数组的长度,说明所有Promise都已完成,则解决返回的Promise(带有结果数组)。 - 如果任何一个Promise被拒绝,立即拒绝返回的Promise(使用第一个拒绝的原因)。 注意:结果数组的顺序必须与输入顺序一致。 实现代码: ```javascript function promiseAll(promises) { return new Promise((resolve, reject) => { // 如果输入为空数组,直接返回一个已解决的Promise,结果为空数组 if (promises.length === 0) { resolve([]); return; } // 初始化结果数组和计数器 const results = new Array(promises.length); let completedCount = 0; promises.forEach((promise, index) => { // 用Promise.resolve包装,确保处理的是Promise Promise.resolve(promise) .then(value => { // 将解决值存入对应位置 results[index] = value; completedCount++; // 当所有Promise都完成时,解决返回的Promise if (completedCount === promises.length) { resolve(results); } }) .catch(reason => { // 一旦有任何一个Promise被拒绝,立即拒绝返回的Promise reject(reason); }); }); }); } ``` 注意:上面的实现中,我们使用Promise.resolve来包装每个元素,这样即使传入非Promise值(如数字、字符串等),也能正常处理。 但是,上面的实现有一个小问题:如果输入数组中包含非Promise值,它们会被Promise.resolve转换为已解决的Promise,因此可以正常存储到结果数组中。 另外,我们使用了一个计数器completedCount,每次有一个Promise完成(无论是解决还是拒绝?)注意:在then里面我们只处理了解决,而拒绝在catch里处理了,并且一旦拒绝就会立即reject整个Promise。所以计数器只在解决时增加,拒绝时不会增加(因为已经reject了,整个流程结束)。 但是,如果我们在拒绝时也增加计数器,那么当有Promise被拒绝时,我们不会等待其他Promise完成,因为我们在拒绝时已经调用了reject。所以不需要在拒绝时增加计数器。 因此,上述实现是符合Promise.all的行为的。 现在,我们也可以简单提一下如何实现Promise.allSettled,但用户主要要求实现Promise.all。 手动实现Promise.allSettled的思路类似,但不会在任何Promise被拒绝时立即拒绝,而是等待所有Promise完成,并且收集每个Promise的结果(包括状态)。 最后,我们生成相关问题。</think>### Promise.allPromise.allSettled 的区别 #### 核心区别总结 | 特性 | Promise.all | Promise.allSettled | |--------------------|--------------------------------------|---------------------------------------| | **结果状态** | 全部成功才成功,任一失败立即失败 | 总是成功,等待所有 Promise 完成 | | **返回值** | 成功时返回值数组 | 返回包含状态和值/原因的对象数组 | | **失败处理** | 立即抛出第一个失败原因 | 收集所有结果(含失败原因) | | **适用场景** | 强依赖关系任务(如数据校验) | 独立任务(如批量请求、日志记录) | #### 关键差异详解 1. **行为差异** - `Promise.all`:实现"快速失败"机制,任一 Promise 被拒绝时立即终止 $$ \text{状态} = \begin{cases} \text{fulfilled} & \text{当 } \forall P_i \in \text{fulfilled} \\ \text{rejected} & \text{当 } \exists P_j \in \text{rejected} \end{cases} $$ - `Promise.allSettled`:实现"全收集"机制,始终等待所有 Promise 完成 $$ \text{状态} \equiv \text{fulfilled} \quad (\forall \text{输入状态}) $$ 2. **返回值结构** ```javascript // Promise.all 成功时 [value1, value2, ...] // Promise.allSettled 始终返回 [ { status: 'fulfilled', value: result }, { status: 'rejected', reason: error } ] ``` 3. **错误处理对比** ```javascript // Promise.all:第一个错误即终止 Promise.all([fetchA(), fetchB()]) .then(console.log) // 全部成功才执行 .catch(err => console.error('首次失败:', err)) // Promise.allSettled:收集所有结果 Promise.allSettled([fetchA(), fetchB()]) .then(results => { results.forEach(r => { if (r.status === 'rejected') console.warn('失败任务:', r.reason) }) }) ``` --- ### 手动实现 Promise.all #### 实现思路 1. 返回新 Promise 作为容器 2. 校验输入是否为可迭代对象 3. 初始化结果数组和完成计数器 4. 遍历处理每个 Promise: - 用 `Promise.resolve()` 包装保证 Promise 类型 - 按顺序存储结果 - 任一失败立即拒绝主 Promise - 全部成功时返回结果数组 #### 完整代码实现 ```javascript function promiseAll(iterable) { return new Promise((resolve, reject) => { // 1. 参数校验 if (!iterable || typeof iterable[Symbol.iterator] !== 'function') { return reject(new TypeError('Argument must be iterable')); } const promises = Array.from(iterable); const results = new Array(promises.length); let completed = 0; // 2. 空数组直接返回 if (promises.length === 0) { return resolve(results); } // 3. 异步处理每个Promise promises.forEach((item, index) => { Promise.resolve(item) // 包装非Promise.then(value => { results[index] = value; // 保持顺序存储 completed++; // 全部完成时返回结果 if (completed === promises.length) { resolve(results); } }) .catch(reject); // 任一失败立即拒绝 }); }); } ``` #### 验证示例 ```javascript // 测试用例 const p1 = Promise.resolve(3); const p2 = 42; // 非Promise值 const p3 = new Promise((_, reject) => setTimeout(() => reject('error'), 100) ); // 成功案例 promiseAll([p1, p2]) .then(console.log); // 输出: [3, 42] // 失败案例 promiseAll([p1, p2, p3]) .catch(console.error); // 输出: 'error' ``` --- ### 关键应用场景对比 | 场景 | 推荐方法 | 原因说明 | |--------------------------|---------------------|------------------------------| | 表单多字段校验 | `Promise.all` | 任一校验失败应中断流程 | | 批量数据上报 | `Promise.allSettled`| 需确保所有上报请求完成 | | 多源数据加载(强依赖) | `Promise.all` | 数据缺一不可 | | 并行独立任务(如日志) | `Promise.allSettled`| 单任务失败不影响整体 | > 引用说明:`Promise.allSettled` 会返回包含每个 Promise 状态的对象数组,无论成功或失败[^3],而 `Promise.all` 在任一 Promise 被拒绝时会立即终止[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值