彻底掌握 Promise 从手写一系列方法开始

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

一个 Promise 是一个代理,它代表一个在创建 promise 时不一定已知的值,它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。这使得异步方法可以像同步方法一样返回值:异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值。

一个 Promise 必然处于以下几种状态之一:

  • 待定(pending) :初始状态,既没有被兑现,也没有被拒绝
  • 已兑现(fulfilled) :意味着操作成功完成
  • 已拒绝(rejected) :意味着操作失败

一个待定的 Promise 最终状态 可以是已兑现 并返回一个值,或者是已拒绝 并返回一个原因(错误)。当其中任意一种情况发生时,通过 Promise 的 then 方法串联的处理程序将被调用。

1. Promise.all

Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组;如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。

js 复制代码
Promise._all = (iterObj) => {
    // 1. iterObj 必须是一个可迭代对象, 否则, 无法正常进行则抛出错误
    if(!(typeof iterObj === "object" && iterObj !== null && typeof iterObj[Symbol.iterator] === "function")){
        throw new TypeError(`${iterObj} is not iterable`);
    }
    iterObj = [...iterObj];
    /*
     *  2. 函数返回值为 `<Promise>` 对象, 当参数 `iterObj` 内所有的 `Promise` 成功, 
     *     该 `<Promise>` 对象成功, 成功数据为所有成功的 `Promise` 结果数组,
     *     有一个不成功, 则该 `<Promise>` 不成功, 失败数据为失败原因字符串
     */
    return new Promise((resolve, reject) => {
        const len = iterObj.length;
        let count = 0;
        if(len === 0) return resolve([]);

        const res = new Array(len);
        iterObj.forEach(async (item, index) => {
            const newItem = Promise.resolve(item);
            try{
                const result = await newItem;
                res[index] = result;
                if(++count === len){
                    resolve(res)
                }
            }catch(err){
                reject(err);
            }
        })
    })
}

测试验证:

js 复制代码
// 验证:
function test(){
    try{
        Promise._all(null).then(res=>console.log(res), rej=>console.log(rej)); 
        // throw err: null is not iterable
    }catch(e){
        console.log(e)
    }

    try{
        Promise._all({}).then(res=>console.log(res), rej=>console.log(rej)); 
        // throw err: [object object] is not iterable
    }catch(e){
        console.log(e)
    }
    
    Promise._all([]).then(res=>console.log(res), rej=>console.log(rej)); 
    // []
    
    Promise._all(new Set()).then(res=>console.log(res), rej=>console.log(rej)); 
    // []
    
    Promise._all(new Map()).then(res=>console.log(res), rej=>console.log(rej)); 
    // []
    
    Promise._all([
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3),
        4,
      ]).then(res=>console.log(res), rej=>console.log(rej))
    
    // [1, 2, 3, 4]
    
    Promise._all([
        Promise.reject(1),
        Promise.resolve(2),
        Promise.resolve(3),
        4,
      ]).then(res=>console.log(res), rej=>console.log(rej))
    // 1
}
test();

2. Promise.prototype.catch

Promise 实例的 catch() 方法用于注册一个在 promise 被拒绝时调用的函数,它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 的方法。

此方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。

js 复制代码
 /**
   * 本质就是then,只是少传了一个onFulfilled
   * 所以仅处理失败的场景
   * @param {*} onRejected
   */
Promise.prototype.catch = function(onRejected) {
    return this.then(null, onRejected);
}

3. Promise.race

Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise,这个返回的 promise 会随着第一个 promise 的敲定而敲定。

js 复制代码
Promise._race = function (promises) {
  if(!(typeof promises === "object" && promises !== null && typeof promises[Symbol.iterator] === "function")){
        throw new TypeError(`${promises} is not iterable`);
    }
  promises = [...promises];
  return new Promise((resolve, reject) => {
    for (let p of promises) {
      Promise.resolve(p).then(resolve).catch(reject);
    }
  });
};

4. Promise.allSettled

Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。

js 复制代码
Promise.prototype.mySettled = function (promises) {
  return new Promise((resolve) => {
    const data = [],
      len = promises.length;
    let cnt = 0;
    for (let i = 0; i < len; i++) {
      const promise = promises[i];
      Promise.resolve(promise)
        .then(
          (res) => {
            data[i] = { status: "fulfilled", value: res };
          },
          (error) => {
            data[i] = { status: "rejected", reason: error };
          }
        )
        .finally(() => {
          if (cnt === len) resolve(data);
        });
    }
  });
};
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) =>
  setTimeout(reject, 100, "foo")
);
const promises = [promise2, promise1];
Promise.allSettled(promises).then((results) =>
  results.forEach((result) => console.log(result))
);

5. Promise.reject

Promise.reject() 静态方法返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数。

js 复制代码
  Promise._reject = function(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }

6. Promise.resolve

Promise.resolve() 静态方法将给定的值转换为一个 Promise,如果该值本身就是一个 Promise,那么该 Promise 将被返回;如果该值是一个 thenable 对象,Promise.resolve() 将调用其 then() 方法及其两个回调函数;否则,返回的 Promise 将会以该值兑现。

该函数将嵌套的类 Promise 对象(例如,一个将被兑现为另一个 Promise 对象的 Promise 对象)展平,转化为单个 Promise 对象,其兑现值为一个非 thenable 值。

js 复制代码
Promise._resolve = function(value) {
  // 如果 value 已经是 Promise 对象,则直接返回该 Promise 对象
  if (value && value instanceof Promise) {
    return value;
  }
  // 如果 value 是 thenable 对象,则包装成 Promise 对象并返回
  if (value && typeof value.then === 'function') {
    return new Promise(function(resolve, reject) {
      value.then(resolve, reject);
    });
  }
  // 将传入的值作为 Promise 的成功值,并返回 Promise 对象
  return new Promise(function(resolve) {
    resolve(value);
  });
}

7. Promise.prototype.finally

Promise 实例的 finally() 方法用于注册一个在 promise 敲定(兑现或拒绝)时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 方法;这可以让你避免在 promise 的 then()catch() 处理器中重复编写代码。

js 复制代码
/**
   * 无论成功还是失败都会执行回调
   * @param {Function} onSettled
   */
 Promise.prototype.finally = function (onSettled) {
    return this.then(
      (data) => {
        onSettled(); // 实现了收不到参数了
        return data;
      },
      (reason) => {
        onSettled();
        throw reason;
      }
    );
    // finally函数 返回结果应该是无效的
  }
  
/******test finally*******/
// 无论什么结果,都会运行
const pro = new Promise((resolve, reject) => {
  resolve(1);
});
const pro2 = pro.finally((d) => {
  console.log("finally", d); // 收不到d参数
  // 本身不改变状态,但是抛出一个错误,数据就会变成它的错误
  // throw 123;
  return 123; //不起作用
});
setTimeout(() => {
  console.log(pro2);
});
相关推荐
你挚爱的强哥44 分钟前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
y先森1 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy1 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189111 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿2 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡3 小时前
commitlint校验git提交信息
前端
虾球xz4 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇4 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒4 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员4 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js