彻底掌握 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);
});
相关推荐
万叶学编程2 小时前
Day02-JavaScript-Vue
前端·javascript·vue.js
前端李易安4 小时前
Web常见的攻击方式及防御方法
前端
PythonFun4 小时前
Python技巧:如何避免数据输入类型错误
前端·python
知否技术4 小时前
为什么nodejs成为后端开发者的新宠?
前端·后端·node.js
hakesashou4 小时前
python交互式命令时如何清除
java·前端·python
天涯学馆4 小时前
Next.js与NextAuth:身份验证实践
前端·javascript·next.js
HEX9CF4 小时前
【CTF Web】Pikachu xss之href输出 Writeup(GET请求+反射型XSS+javascript:伪协议绕过)
开发语言·前端·javascript·安全·网络安全·ecmascript·xss
ConardLi5 小时前
Chrome:新的滚动捕捉事件助你实现更丝滑的动画效果!
前端·javascript·浏览器
ConardLi5 小时前
安全赋值运算符,新的 JavaScript 提案让你告别 trycatch !
前端·javascript