Promise与Promise.all的基本使用,手写Promise

在现代 JavaScript 编程中,异步操作已经成为开发中不可或缺的一部分,而 Promise 则是处理异步操作的核心工具。随着异步编程需求的增加,JavaScript 提供了多种处理方式,其中 PromisePromise.all 是两个非常重要且常用的异步操作方法。在本文中,我们将深入探讨 Promise 及其进阶用法 Promise.all,并讨论它们的使用场景和最佳实践。

1. Promise 的基本概念

Promise 是一种用于处理异步操作的对象。它代表着一个在未来可能完成或失败的操作,并且能够以一种线性、直观的方式来处理异步逻辑。Promise 对象有三种状态:

  • Pending(待定):初始状态,操作尚未完成。
  • Fulfilled(已完成):操作成功完成。
  • Rejected(已拒绝):操作失败。

一个 Promise 的典型使用方式如下:

new Promise((resolve, reject) => {
    // 执行一些异步操作
    setTimeout(() => {
        let success = true;
        if (success) {
            resolve("操作成功!");
        } else {
            reject("操作失败!");
        }
    }, 1000);
}).then(result => {
    console.log(result); // "操作成功!"
}).catch(error => {
    console.log(error); // "操作失败!"
});

在这个例子中,Promise 被创建后,它会立即执行传入的函数,并在操作完成时调用 resolvereject,然后通过 then 方法处理成功的结果,通过 catch 方法处理失败的情况。

2. Promise.all 的使用场景

Promise.allPromise 的一个高级用法,适用于当我们有多个异步操作需要并行执行,并且希望在所有操作都成功后继续执行后续逻辑时。它接收一个包含多个 Promise 对象的数组,并返回一个新的 Promise 对象。

Promise.all 的工作机制如下:

  • 当数组中的所有 Promise 对象都 Fulfilled 时,Promise.all 返回的 Promise 状态变为 Fulfilled,并返回一个包含所有 Promise 返回值的数组。
  • 如果数组中的任意一个 Promise 进入 Rejected 状态,Promise.all 返回的 Promise 状态立即变为 Rejected,并返回第一个 Rejected 的错误。

下面是一个 Promise.all 的简单示例:

let promise1 = Promise.resolve(3);
let promise2 = 42;
let promise3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3])
    .then(values => {
        console.log(values); // [3, 42, "foo"]
    })
    .catch(error => {
        console.log(error);
    });

在这个例子中,Promise.all 接收了三个 Promise,它们都成功了,因此 then 回调函数会接收到一个数组,包含三个 Promise 返回的值。

3. 使用 Promise.all 处理并行异步操作

在实际开发中,我们常常需要同时处理多个异步操作,例如从多个 API 获取数据、并行处理多个文件或数据库查询等。这种情况下,Promise.all 可以有效地提高代码的执行效率,因为它可以同时发起所有请求,而不是一个接一个地等待前一个请求完成。

举个例子,假设我们需要从三个不同的 API 获取数据,然后将这些数据进行处理:

function fetchData(url) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(`Data from ${url}`);
        }, Math.random() * 1000);
    });
}

Promise.all([
    fetchData('https://api1.com'),
    fetchData('https://api2.com'),
    fetchData('https://api3.com')
]).then(results => {
    console.log(results); 
}).catch(error => {
    console.log(error);
});

在这个例子中,三个 fetchData 请求会同时开始,并且 Promise.all 会等待所有请求都完成后再继续处理结果。

4. Promise.all 的注意事项

虽然 Promise.all 提供了一种强大的并行处理方式,但也有一些需要注意的地方:

  1. 单点失败问题:如果任意一个 Promise 被拒绝(Rejected),Promise.all 返回的 Promise 也会立即 Rejected,这意味着即使其他 Promise 已经成功完成,整个 Promise.all 也会失败。这种行为在某些情况下可能并不理想。

  2. 顺序不可控Promise.all 返回结果的顺序与传入 Promise 的顺序一致,而不是实际完成的顺序。如果你需要根据完成时间顺序处理 Promise,可能需要其他方法,例如 Promise.race

  3. 并发限制:在某些情况下,过多的并发请求可能导致性能问题或服务器负载过高。可以考虑使用 Promise.all 的变种,如 p-limit 库来限制并发数。

5. 手写Promise

class NewPromise {
  constructor(func) {
    //判断当前执行到第几个then和catch的回调函数
    this.currentIndex = 0;
    //resolve函数,执行当前索引后面最近的then的回调函数
    this.res = (arg) => {
      //找到最近的一个then的回调函数
      for (let i = this.currentIndex; i < this.funcArr.length; i++) {
        if (this.funcArr[i].type === "then") {
          this.currentIndex++;
          break;
        }
      }
      //记录返回值
      const resReturn = this.funcArr[this.currentIndex - 1].func(arg);
      //判断返回值是否存在,如不存在则执行finally,如存在则判断类型是否为NewPromise也就是此类
      if (resReturn) {
        if (resReturn instanceof NewPromise) {
          resReturn.currentIndex = this.currentIndex;
          resReturn.funcArr = this.funcArr;
          resReturn.finallyFunc = this.finallyFunc;
        } else {
          this.res(resReturn);
        }
      } else {
        if (this.finallyFunc) {
          this.finallyFunc();
        }
      }
    };
    //reject函数,执行当前索引后面最近的catch的回调函数
    this.rej = (arg) => {
      //找到最近的一个catch的回调函数
      for (let i = this.currentIndex; i < this.funcArr.length; i++) {
        if (this.funcArr[i].type === "catch") {
          this.currentIndex++;
          break;
        }
      }
      const rejReturn = this.funcArr[this.currentIndex - 1].func(arg);
      //判断返回值是否存在,如不存在则执行finally,如存在则判断类型是否为NewPromise也就是此类
      if (rejReturn) {
        if (resReturn instanceof NewPromise) {
          resReturn.currentIndex = this.currentIndex;
          resReturn.funcArr = this.funcArr;
          resReturn.finallyFunc = this.finallyFunc;
        } else {
          this.rej(resReturn);
        }
      } else {
        if (this.finallyFunc) {
          this.finallyFunc();
        }
      }
    };
    //预备数组,记录全部回调函数,并以对象形式储存,其中包括type判断是then还是catch,以及对应的函数指针
    this.funcArr = [];
    //保存finally回调函数
    this.finallyFunc;
    //执行newpromise的参数函数,并将写好的reslove和reject函数传入
    func(this.res, this.rej);
  }
  //then,将传入的函数放入预备数组
  then(func) {
    this.funcArr.push({
      type: "then",
      func,
    });
    return this;
  }
  //catch,将传入的函数防御预备数组
  catch(func) {
    this.funcArr.push({
      type: "catch",
      func,
    });
    return this;
  }
  //finally,最终必须执行的函数
  finally(func) {
    this.finallyFunc = func;
  }
}
new NewPromise((res, rej) => {
  setTimeout(() => {
    res(6);
  }, 1000);
})
  .then((res) => {
    console.log(res);
  })
  .catch((rej) => {
    console.log(rej);
  });

手写实现 `Promise.all` 方法,可以按照以下步骤进行操作: 1. 创建一个新的 Promise 对象,并返回它。 2. 在内部创建一个数组 `results`,用于存储每个传入的 Promise 对象的结果。 3. 使用 `Array.from` 方法将传入的参数(可以是数组或类数组对象)转换为一个真正的数组。 4. 遍历数组中的每个 Promise 对象,使用 `Promise.resolve` 方法将其转换为一个 Promise 对象。 5. 对于每个 Promise 对象,使用 `.then` 方法来处理其结果。 - 如果 Promise 对象 resolved,将结果存储到 `results` 数组中。 - 如果 Promise 对象 rejected,直接将整个 `Promise.all` 的 Promise 对象 rejected,并传递该错误给它。 6. 在所有 Promise 对象都处理完毕后,如果没有任何错误,则将 `results` 数组作为参数解析传递给 `Promise.all` 的 Promise 对象,并使其 resolved。 以下是一个示例代码,展示了如何手写实现 `Promise.all` 方法: ```javascript function myPromiseAll(promises) { return new Promise((resolve, reject) => { const results = []; const promisesCount = promises.length; let resolvedCount = 0; if (promisesCount === 0) { resolve(results); return; } function handleResolve(index, value) { results[index] = value; resolvedCount++; if (resolvedCount === promisesCount) { resolve(results); } } function handleReject(error) { reject(error); } for (let i = 0; i < promisesCount; i++) { Promise.resolve(promises[i]) .then((value) => handleResolve(i, value)) .catch(handleReject); } }); } // 示例 Promise 对象 const promise1 = Promise.resolve(1); const promise2 = Promise.resolve(2); const promise3 = new Promise((resolve) => setTimeout(resolve, 1000, 3)); // 使用自定义的 Promise.all 方法 myPromiseAll([promise1, promise2, promise3]) .then((results) => { console.log(results); // 输出: [1, 2, 3] }) .catch((error) => { console.log(error); }); ``` 通过以上代码,你可以手动实现 JavaScript 中的 `Promise.all` 方法。请注意,这只是一个简单的示例,并没有处理所有可能的情况,如传入的参数不是一个数组等。在实际使用中,建议使用 JavaScript 原生的 `Promise.all` 方法来处理多个 Promise 对象的并行操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不止会JS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值