浅谈Promise

一、首先什么是Promise?

Promise 是 Es6 中用于处理异步操作的对象。在代码层面,Promise 解决了异步操作的 "回调地狱" 问题。

什么是回调地狱呢? 回调地狱是指,在一个异步操作执行完成后,执行下一条异步操作时,出现回调函数嵌套回调函数的情况。如果嵌套层级过多,会导致代码可读性变差,下面是代码演示情况。

js 复制代码
 Setup.prototype.run = function () {
            this.step1(function () {
                this.step2(function () {
                    this.step3(function () {
                        // ······
                    })
                })
            })
        }

从上述代码可以看出,step1()方法的参数是一个回调函数,该函数不会立即执行,而是等到step1()方法内部调用了这个函数时才会执行,其执行的时机是未知的,若step2()必须等待setp1()的回调函数执行完成后才能执行,就必须将其加入到step1()回调函数的末尾,导致代码的嵌套又多了一层,这样的代码不仅可读性差,对错误的处理也会带来麻烦。此时,我们可以通过Promise来解决这种问题。

js 复制代码
Setup.prototype.run = function () {
    return this.step1() 
        .then(() => this.step2()) 
        .then(() => this.step3()) 
        .then(() => { 
        // 执行完 step3 后的操作 
        }); 
}

在这个修改后的代码中,每个步骤都返回一个 Promise 对象,然后使用 .then() 方法来实现链式调用。当前一个步骤完成时,会执行下一个步骤。如果任何一步出现错误,可以通过 Promise 的错误处理机制来捕获错误。

二、基本用法

js 复制代码
// 创建一个 Promise 对象
        const myPromise = new Promise((resolve, reject) => {
            // 模拟异步操作
            setTimeout(() => {
                const randomNumber = Math.random();
                if (randomNumber > 0.5) {
                    resolve(randomNumber); // 异步操作成功时调用 resolve
                } else {
                    reject('Error: Random number is too small'); // 异步操作失败时调用 reject
                }
            }, 1000);
        });

        // 处理 Promise 的成功和失败情况
       myPromise.then((result) => {
                console.log('Success:', result); // 成功时输出结果
            }, (err) => {
                console.log(err)                 // 失败时输出错误信息
            }).then((result) => {
                console.log('Success:', result); // 成功时输出结果
            }).catch((error) => {
                console.error('Failure:', error); // 失败时输出错误信息
        });

1.Promise 构造函数接受一个函数作为参数,并且这个函数具有两个参数------resolverejectresolve 函数用于将 Promise 对象的状态从 "pending"(进行中)转变为 "fulfilled"(已完成),在异步操作成功时调用并将异步操作的结果作为参数传递出去。而 reject 函数用于将 Promise 对象的状态从 "pending"(进行中)转变为 "rejected"(已拒绝),在异步操作失败时调用并将异步操作报错的错误作为参数传递出去。

2.then 方法可以接受两个回调函数作为参数。第一个回调函数会在 Promise 对象的状态变为 "fulfilled"(已完成)时被调用,而第二个回调函数则会在 Promise 对象的状态变为 "rejected"(已拒绝)时被调用。需要注意的是,第二个回调函数是可选的,你可以选择只提供第一个回调函数。这两个回调函数都会接收 Promise 对象传递出来的值作为参数。Promise 的 then 方法返回的是一个新的 Promise 实例,而不是原来那个 Promise 实例。这也就是为什么我们可以采用链式写法,即在一个 then 方法后再调用另一个 then 方法。通过链式调用 then 方法,我们可以依次处理异步操作的结果,每个 then 方法接收上一个 then 方法返回的结果,并返回一个新的 Promise 对象,从而形成一个 Promise 调用链。

3..catch() 方法用于处理 Promise 对象状态变为 rejected 时的回调函数。.catch() 方法是.then(null, onRejected) 方法的一种简便写法,用于捕获 Promise 对象链中任何一个 Promise 的错误,并进行统一的错误处理。

js 复制代码
  somePromiseFunction()
            .then((result) => {
                console.log('成功:', result);
                return result * 2;
            })
            .catch((error) => {
                console.error('捕获到错误:', error);
            });

在这个示例中,如果 somePromiseFunction() 返回的 Promise 对象状态变为 rejected,那么错误会被 .catch() 方法捕获并处理。这样可以确保统一地处理 Promise 链中的错误情况。

Promise 构造函数的作用是创建一个 Promise 对象,并通过 resolvereject 函数来控制该对象的状态变化。而 then 方法则用于注册对 Promise 对象状态变化的监听,并在状态变为 "fulfilled"(已完成)或 "rejected"(已拒绝)时执行相应的回调函数。

三、PromiseState => Promise的三种状态

Promise 表示一个异步操作的最终完成或失败其结果值。它有三种状态:pending、fulfilled、rejected。当异步操作完成,Promise将从pending状态转变为fulfilled或者rejected。

1. pending (初始状态)

pending是Promise的最初始的状态。在初始状态下,Promise 可以转变为成功时的 fulfilled(成功时)或者 rejected(失败时)的状态。无论转变为哪个状态都是不可逆的,只能改变一次。

js 复制代码
const p = new Promise((resolve, reject) => {})
console.log(p)

2. fulfilled (已完成)

在 Promise 中,fulfilled 表示 Promise 的状态已经成功完成。当 Promise 表示的异步操作顺利执行并返回了一个值时,它会从 pending(进行中)状态转变为 fulfilled(已完成)状态。

一旦 Promise 进入了 fulfilled 状态,意味着异步操作成功完成,并且可以通过 Promise 的 then 方法来处理和获取操作的结果值。在 then 方法中,你可以编写处理已完成状态的代码逻辑,以便在异步操作成功时执行相应的操作。

js 复制代码
 // 模拟一个异步操作
        function asyncOperation() {
            return new Promise((resolve, reject) => {
                // 模拟异步操作,在2秒后将 Promise 状态改为已完成
                setTimeout(() => {
                    resolve("Operation completed successfully");
                }, 2000);
            });
        }

        // 调用异步操作并处理 Promise 状态改变
        let result = asyncOperation().then((result) => {
            console.log(result); // 在Promise状态变为fulfilled时输出结果
        }).catch((error) => {
            console.error(error); // 在Promise状态变为rejected时输出错误信息
        });
        console.log(result)

3.rejected (失败时)

在 Promise 中,rejected 是指一个 Promise 的状态,表示异步操作失败或被拒绝。当 Promise 表示的异步操作无法成功完成时,它会从 pending(进行中)状态转变为 rejected(已拒绝)状态。

一旦 Promise 进入了 rejected 状态,意味着异步操作出现错误或异常情况,这时可以通过 Promise 的 catch 方法或者 then 方法的第二个参数来处理这个错误状态。在 catch 方法中,你可以编写处理错误的代码逻辑,以便在异步操作出现问题时执行相应的错误处理操作。

js 复制代码
   let promise = new Promise((resolve, reject) => {
            // 模拟异步操作出现问题
            setTimeout(() => {
                reject(new Error("Something went wrong"));
            }, 1000);
        });

        let result = promise.then(value => {
            console.log(value)
        }).catch(error => {
            throw '出错了'
        });

四、Promise 的其他方法

1. Promise.all

Promise.all 是一个 Promise 方法,用于将多个 Promise 实例包装成一个新的 Promise 实例。这个新的 Promise 实例在传入的所有 Promise 实例都成功完成时才会成功,一旦有任何一个 Promise 实例失败(rejected),那么整个 Promise.all 实例就会立即失败。

通过 Promise.all,可以同时处理多个异步操作,并等待它们全部完成后再执行后续操作。这在需要同时请求多个资源或执行多个独立的异步操作时非常有用。

下面是一个简单的示例,演示了如何使用 Promise.all

js 复制代码
let promise1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('Promise 1 resolved');
            }, 1000);
        });

let promise2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('Promise 2 resolved');
            }, 1500);
        });

let promise3 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('Promise 3 resolved');
            }, 2000);
        });

Promise.all([promise1, promise2, promise3])
            .then(values => {
                console.log('All promises resolved:', values);
            })
            .catch(error => {
                console.error('One of the promises failed:', error);
            });

在这个例子中,我们创建了三个 Promise 实例,分别模拟了三个异步操作。通过 Promise.all([promise1, promise2, promise3]),我们将这三个 Promise 实例包装成一个新的 Promise 实例。当所有的 Promise 都成功完成时,then 方法中的回调函数将会接收到一个包含所有 Promise 结果值的数组。如果其中任何一个 Promise 失败(rejected),则整个 Promise.all 实例会立即失败,并触发 catch 方法中的错误处理逻辑。

2. Promise.resolve

Promise.resolve 是一个用于创建一个已解决(resolved)状态的 Promise 对象的静态方法。它的语法如下:

js 复制代码
Promise.resolve(value);
  • 如果 value 是一个普通的值(比如数字、字符串、对象等),Promise.resolve 会返回一个以该值解析后的 Promise 对象。
  • 如果 value 是一个 Promise 对象,Promise.resolve 直接返回这个 Promise 对象。
  • 如果 value 是一个 thenable 对象(具有 then 方法的对象),Promise.resolve 会将其转化为 Promise 对象并立即执行 then 方法。

3.Promise.reject

Promise.reject() 是 Promise 对象提供的一个静态方法,用于返回一个带有拒绝状态的 Promise 对象。它通常在需要立即拒绝一个 Promise 的情况下使用。下面是 Promise.reject 的使用示例:

js 复制代码
const reason = 'Something went wrong';

const rejectedPromise = Promise.reject(reason);

rejectedPromise.catch((error) => {
            console.error('Promise rejected with reason:', error);
        });

在这个示例中,我们调用 Promise.reject 并传入一个拒绝的理由(reason)。Promise.reject 会立即返回一个带有拒绝状态的 Promise 对象 rejectedPromise。然后我们使用 .catch() 方法来捕获这个拒绝状态,并输出拒绝的理由到控制台。Promise.reject 是用来创建一个立即拒绝的 Promise 对象,并可以通过 .catch() 方法来处理拒绝状态。

4.Promise.race

Promise.race() 是 Promise 对象提供的一个静态方法,用于将多个 Promise 对象包装成一个新的 Promise 对象,并在其中任意一个 Promise 对象解决后立即返回该 Promise 对象的结果或拒绝原因。

js 复制代码
// 基本语法如下:
Promise.race(iterable);
  • iterable 是一个可迭代对象,通常是一个包含多个 Promise 对象的数组或类数组对象。
  • Promise.race() 方法返回一个新的 Promise 对象,它会在 iterable 中的任意一个 Promise 对象解决后立即解决(无论是成功还是失败),并将第一个解决的 Promise 对象的值或原因作为返回的 Promise 对象的值或原因。
js 复制代码
const promise1 = new Promise((resolve, reject) => {
            setTimeout(resolve, 100, 'Promise 1 resolved');
        });

const promise2 = new Promise((resolve, reject) => {
            setTimeout(resolve, 200, 'Promise 2 resolved');
        });

Promise.race([promise1, promise2])
            .then((value) => {
                console.log(value); // 输出第一个完成的 Promise 对象的值
            })
            .catch((reason) => {
                console.error(reason); // 输出第一个失败的 Promise 对象的拒绝原因
            });

在这个示例中,Promise.race() 接收了包含两个 Promise 对象的数组。无论哪个 Promise 对象率先解决,Promise.race() 返回的新 Promise 对象将采用该 Promise 对象的解决值或拒绝原因。

五、手写Promise

js 复制代码
class Promise {
  // 构造方法
  constructor(executor) {
    // 添加属性
    this.PromiseState = "pending";
    this.PromiseResult = null;
    this.callbacks = [];
    const self = this;
    function resolve(data) {
      if (self.PromiseState !== "pending") return;
      self.PromiseState = "fulfilled";
      self.PromiseResult = data;
      setTimeout(() => {
        self.callbacks.forEach((item) => {
          if (item.onFulfilled) {
            item.onFulfilled(data);
          }
        });
      });
    }
    function reject(data) {
      if (self.PromiseState !== "pending") return;
      self.PromiseState = "rejected";
      self.PromiseResult = data;
      setTimeout(() => {
        self.callbacks.forEach((item) => {
          if (item.onRejected) {
            item.onRejected(data);
          }
        });
      });
    }
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  // then 方法封装
  then(onFulfilled, onRejected) {
    const self = this;
    // 判断回调函数参数
    if (typeof onRejected !== "function") {
      onRejected = (reason) => {
        throw reason;
      };
    }
    if (typeof onFulfilled !== "function") {
      onFulfilled = (value) => {
        return value;
      };
    }
    return new Promise((resolve, reject) => {
      function callback(type) {
        try {
          let result = type(self.PromiseResult);
          if (result instanceof Promise) {
            result.then(
              (v) => {
                resolve(v);
              },
              (e) => {
                reject(e);
              }
            );
          } else {
            resolve(result);
          }
        } catch (err) {
          reject(err);
        }
      }
      // 判断当前的状态
      if (this.PromiseState === "fulfilled") {
        setTimeout(() => {
          callback(onFulfilled);
        });
      }
      // 判断当前的状态
      if (this.PromiseState === "rejected") {
        setTimeout(() => {
          callback(onRejected);
        });
      }
      // 判断当前的状态
      if (this.PromiseState === "pending") {
        // 保存回调函数
        this.callbacks.push({
          onFulfilled: function () {
            // 执行成功的回调函数
            callback(onFulfilled);
          },
          onRejected: function () {
            // console.log("err");
            callback(onRejected);
          },
        });
      }
    });
  }
  // catch 方法
  catch(onRejected) {
    return this.then(null, onRejected);
  }
  // resolve
  static resolve(value) {
    // 返回 promise 对象
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(
          (v) => {
            resolve(v);
          },
          (r) => {
            reject(r);
          }
        );
      } else {
        resolve(value);
      }
    });
  }

  // reject
  static reject(value) {
    // 返回 promise 对象
    return new Promise((resolve, reject) => {
      reject(value);
    });
  }
  // all
  static all(promises) {
    // 返回 promise 对象
    return new Promise((resolve, reject) => {
      // 计数变量
      let count = 0;
      // 存放所有成功的结果
      let arr = [];
      // 遍历
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (v) => {
            // 如果成功就+1
            count++;
            // 存入 promise 对象成功的结果
            arr[i] = v;
            // 判断 count 的值是否等于数组的长度,如果相等则说明传入的promise都成功了
            if (count === promises.length) {
              resolve(arr);
            }
          },
          (r) => {
            reject(r);
          }
        );
      }
    });
  }

  // race
  static race(promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (v) => {
            // 修改返回对象的状态 为成功
            resolve(v);
          },
          (r) => {
            // 修改返回对象的状态 为失败
            reject(r);
          }
        );
      }
    });
  }
}
相关推荐
轻口味8 分钟前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王43 分钟前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀1 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef3 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6414 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻4 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云4 小时前
npm淘宝镜像
前端·npm·node.js