手写一个自己的Promise(上)

My Promise

前言

近期,自己打算更换一个状态/工作,于是开始对堆积已久的前端手写面试题进行复习;想了想,感觉Promise自己应该只算熟悉,不算精通,只停留会用阶段,因此打算今日好好将其抽丝剥茧,掌握它!

注:在实现的过程需要了解Promise的规范,

作用

想要实现Promise,首先得了解Promise的作用是什么? MDN解释:Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。

什么意思呢?意思是,Promise只是一个对象,使用者可以在Promise的状态决定之后通过链式/(async/await)的方式来获取到异步的结果,避免掉回调地狱(在有需要到上一个异步请求返回值的时候)。

状态

需了解Promise的状态才能够实现Promise,Promise的状态分别为:Pending(等待)、Fulfilled(完成)、Reject(拒绝)。

注:Promise的状态一旦确定之后,无法再更改。

除了了解Promise的状态外,还需对其值有所谓了解,Promise的值共有:

  • status(当前Promise实例的状态,一经确认无法改变)
  • reason(失败原因,reject函数携带回来给catch函数使用)
  • value(最终resolve函数给then函数的第一个函数使用)
  • onFulfilledFnList(存储then函数传递的第一个函数,后续在resolve执行时调用)
  • onRejectedFnList(存储then函数传递的第二个函数/catch传递的函数,后续要reject执行时调用)

建立

首先,先构建一个类,因为Promise都是new出来的,然后需要往constructor传入一个立即执行函数;立即执行函数中还会接受两个回调,分别是resolve,reject;

  • resolve:当在立即执行函数中调用时,则表示当前的Promise状态为Fulfilled(完成);
  • reject:当在立即函数中调用时,则表示当前的promise状态reject(失败);
js 复制代码
const PromiseStatus = {
  PENDING: "pending",
  FULFILLED: "fulfilled",
  REJECTED: "rejected",
};

class MyPromise {
  constructor(executor) {
    // 状态
    this.status = PromiseStatus.PENDING;
    // 最终值
    this.value = undefined;
    // 错误值
    this.reason = undefined;
    // 成功回调队列
    this.onFulfilledFnList = [];
    // 失败回调队列
    this.onRejectedFnList = [];

    // resolve
    const resolve = (val) => {
      ...
    };
		
    // reject
    const reject = (err) => {
      ...
    };

    executor(resolve, reject);
  }
}

const p = new MyPromise((resolve, reject) => {
  resolve(111);
});

resolve的实现

resolve函数主要的功能为改变Promise状态,存储携带过来的结果 ,执行当前已经存储的onFulFilledFnL。

注:在处理时,需判断当前的状态是否是pending,如果不是pending,则不再处理任何操作。

js 复制代码
 const resolve = (val) => {
  // 如果当前状态已改变,不再处理
  if (this.status !== PromiseStatus.PENDING) {
    return;
  }
  this.value = val;
  this.status = PromiseStatus.FULFILLED;
  this.onFulfilledFnList.forEach((f) => f());
};

 const reject = (err) => {
  // 如果当前状态已改变,不再处理
  if (this.status !== PromiseStatus.PENDING) {
    return;
  }

  this.reason = err;
  this.status = PromiseStatus.REJECTED;
  this.onRejectedFnList(() => f());
};

注:reject函数同理,则不再过多叙述。

then方法

then方法是整个promise中最重要的一环,贯穿全局;then方法接受两个参数,分别是onFulFilled、onRejected(则resolve/reject执行后调用的函数),且它的返回值为一个新的promise,并将resolve/reject的结果带到下一个promise的then中。

  • 注册函数:then方法接受的两个参数,都需要进行判断类型,如果不是函数类型,则添加对应的默认值。注:onRejected添加的默认值中,需要throw error。
  • 返回新的promise,在新的promise中,需要去判定当前promise的状态,如果状态是pending,则需要onFulfilled/onRejected添加到对应的队列中;如果是Fulfilled,则直接执行onFulfilled方法,将其返回值给新的promise的resolve函数,反之Rejected同理。
js 复制代码
then(fulfilled, rejected) {
  const onFulfilled = typeof fulfilled === "function" ? fulfilled : () => {};
  const onRejected =
    typeof rejected === "function"
      ? rejected
      : (err) => {
          throw Error(err);
        };

  const promise = new MyPromise((resolve, reject) => {
    // 判断状态,如果是pending,则分别将onFulfilled,onReject添加到对应的队列中
    if (this.status === PromiseStatus.PENDING) {
      try {
        this.onFulfilledFnList.push(() => {
          queueMicrotask(() => {
            try {
              const res = onFulfilled(this.value);
              this.resolvePromise(promise, res, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });

        this.onRejectedFnList.push(() => {
          queueMicrotask(() => {
            try {
              const err = onRejected(this.reason);
              this.resolvePromise(promise, err, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
      } catch (error) {
        reject(error);
      }
    }

    if (this.status === PromiseStatus.FULFILLED) {
      queueMicrotask(() => {
        try {
          const res = onFulfilled(this.value);
          this.resolvePromise(promise, res, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    }

    if (this.status === PromiseStatus.REJECTED) {
      queueMicrotask(() => {
        try {
          const err = onRejected(this.reason);
          this.resolvePromise(promise, err, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    }
  });

  return promise;
}

resolve的返回值

在对应的规范中,resolve的返回值分别有以下规定:

  • 规则一:如果当前的promise与res返回值一致,则报错,不允许返回相同的promise
  • 规则二:如果当前的res是promise,则直接调用res的then方法
  • 规则三:如果res是一个对象,且拥有then函数,则调用该res的then函数
  • 规则四:如果都不属于,直接resolve对应值
js 复制代码
resolvePromise(promise, res, resolve, reject) {
  // 规则一:如果当前的promise与res返回值一致,则报错,不允许返回相同的promise
  if (promise === res) {
    return reject(new Error("Chaining cycle detected for promise"));
  }

  // 规则二:如果当前的res是promise,则直接调用res的then方法
  if (res instanceof MyPromise) {
    res.then(resolve, reject);
    return;
  }

  // 规则三:如果res是一个对象,且拥有then函数,则调用该res的then函数
  if (typeof res === 'object' && typeof res.then === 'function') {
    try {
      res.then(resolve, reject);
    } catch (error) {
      reject(error);
    }
    return;
  }

  // 规则四:如果都不属于,直接resolve对应值
  resolve(res);
}

catch方法

直接调用then方法即可,onFulfilled方法无法传递,只传递onRejected即可。

js 复制代码
catch(onRejected) {
  this.then(null, onRejected);
}

finally方法

注意点:

  • 无论状态是fulfilled或者rejected,都需要执行该方法,且后续还可以通过链式继续调用
  • 实现的过程需要忽略finally方法的返回值,只需传递上一个promise的值即可
js 复制代码
 finally(callback) {
  return this.then(
    (value) => MyPromise.resolve(callback()).then(() => value),
    (err) => MyPromise.reject(callback()).catch(() => { throw err; })
  )
}

static resolve方法

注意点:需要判断当前传入的值是否为promise,如果不是,需进行包括才能返回,如果是,直接返回

js 复制代码
static resolve(value) {
  if(value instanceof MyPromise) {
    return value;
  }

  return new MyPromise((resolve) => resolve(value));
}
相关推荐
祈澈菇凉3 小时前
Webpack的基本功能有哪些
前端·javascript·vue.js
小纯洁w3 小时前
Webpack 的 require.context 和 Vite 的 import.meta.glob 的详细介绍和使用
前端·webpack·node.js
想睡好4 小时前
css文本属性
前端·css
qianmoQ4 小时前
第三章:组件开发实战 - 第五节 - Tailwind CSS 响应式导航栏实现
前端·css
记得早睡~4 小时前
leetcode150-逆波兰表达式求值
javascript·算法·leetcode
zhoupenghui1684 小时前
golang时间相关函数总结
服务器·前端·golang·time
White graces4 小时前
正则表达式效验邮箱格式, 手机号格式, 密码长度
前端·spring boot·spring·正则表达式·java-ee·maven·intellij-idea
庸俗今天不摸鱼4 小时前
Canvas进阶-4、边界检测(流光,鼠标拖尾)
开发语言·前端·javascript·计算机外设
bubusa~>_<5 小时前
解决npm install 出现error,比如:ERR_SSL_CIPHER_OPERATION_FAILED
前端·npm·node.js
[廾匸]5 小时前
cesium视频投影
javascript·无人机·cesium·cesium.js·视频投影