likes
comments
collection
share

手写promise

作者站长头像
站长
· 阅读数 23

主要由三部分来实现,promise构造函数,then方法和promise处理程序(判断返回值或者决议的值)

promise构造函数

function myPromise(executor) {
  this.status = "pending";
  this.data = null;

  this.successCbArr = [];//收集fulfilled后的回调
  this.failCbArr = [];//收集rejected后的回调

  const _this = this;//保存this,因为resolve是独立函数调用,直接使用this会是window

  function resolve(value) {
    //settimeout为了保证在resolve后面的同步代码会先执行打印,
    setTimeout(() => {
      if (_this.status === "pending") {
        _this.status = "fulfilled";
        _this.data = value;

        _this.successCbArr.forEach((cb) => {
          cb();
        });
      }
    });
  }

  function reject(err) {
    setTimeout(() => {
      if (_this.status === "pending") {
        _this.status = "rejected";
        _this.data = err;

        _this.failCbArr.forEach((cb) => {
          cb();
        });
      }
    });
  }

  try {
    executor(resolve, reject);
  } catch (err) {
    reject(err);
  }
}

resolve和reject里面的setTimeout是为了让resolve(xxx)或者reject(xxx)后面的代码先执行

例如下面,222会比‘1111’先打印

const p=new Promise((res,rej)=>{
        res('1111')
        console.log(2222)
    }
)

p.then(res=>{
    console.log(res)
})

then方法

//2.then回调处理
myPromise.prototype.then = function (successCb, failCb) {
  let promise2;
  promise2 = new myPromise((resolve, reject) => {
    if (this.status === "fulfilled") {
      setTimeout(() => {
        if (typeof successCb === "function") {
          //这里的settimeout,是为了能够让then调用地方的后面的同步代码先执行打印
          //因为then的回调属于微任务,

          //只要执行回调函数的地方都要try catch出现错误立即reject掉
          try {
            const x = successCb(this.data);
            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        } else {
          //如果不是函数,则这个then的结果必须跟then的调用者的决议值一样。下面代码类似
          resolve(this.data);
        }
      });
    } else if (this.status === "rejected") {
      setTimeout(() => {
        if (typeof failCb === "function") {
          try {
            const x = failCb(this.data);
            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        } else {
          reject(this.data);
        }
      });
    } else if (this.status === "pending") {//如果是pending,此时要将会回调进行收集,等到由结果在进行执行
      //pengding状态
      this.successCbArr.push(() => {
        if (typeof successCb === "function") {
          try {
            const x = successCb(this.data);
            //根据回调函数返回值,决定promise2的状态是fulfilled还是rejected
            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        } else {
          resolve(this.data);
        }
      });
      this.failCbArr.push(() => {
        if (typeof failCb === "function") {
          try {
            const x = failCb(this.data);
            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        } else {
          reject(this.data);
        }
      });
    }
  });

  //为了链式调用,必须返回promise对象
  return promise2;
};

对于上面fulfilled判断里面或者rejected判断里面的setTimeout,是为了让在then调用的地方后面的代码,能够先执行,因为then的回调属于微任务。

例如下面


const p = new Promise((res, rej) => {
  res("1111");
  console.log(2222);
});

p.then((res) => {
  console.log(res);
});

console.log(333);

//打印结果
2222
333
1111

当执行到then的时候,由于上面的promise也就决议了,所以在调用then函数时会进入this.status === "fulfilled"的判断中,并执行里面的代码,但是此时then函数后面还有同步代码,所以需要将then函数的执行延后。

promise处理程序

//3.promise处理程序,处理返回值或者决议一个thenable的对象或函数时
function promiseResolutionProcedure(promise2, x, resolve, reject) {
  //1.首先判断循环引用
  if (promise2 === x) {
    return reject(new TypeError("存在循环引用"));
  }

  //2.判断返回值x是不是promise实例
  //例子
  //   p.then(res=>{
  //     return new myPromise((res,rej)=>{
  //         res(111)
  //     })
  //   })
  if (x instanceof myPromise) {
    //2.1判断这个实例的状态,来决定promise2的结果
    if (x.status === "pending") {
      //pending时,需要等到他的结果(fulfilled或rejected)出来在进一步判断
      x.then(function (value) {
        promiseResolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else if (x.status === "fulfilled") {
      resolve(x.data);
    } else if (x.status === "rejected") {
      //否决状态,rejected
      reject(x.data);
    }
    return;
  }

  //3.判断是否是一个对象或者函数
  if (x && (typeof x === "object" || typeof x === "function")) {
    //为了防止thenable里面的两个回调函数调用多次,一旦resolve或reject后,状态值就不改变了
    let isCalled = false;
    try {
      let then = x.then;
      if (typeof then === "function") {
        //这里需要调用thenable方法,得状态值,来决定promise2的状态
        then.call(
          x,
          function resolvePromise(value) {
            if (isCalled) return;
            isCalled = true;
            return promiseResolutionProcedure(promise2, value, resolve, reject);
          },
          function rejectPromise(err) {
            if (isCalled) return;
            isCalled = true;
            return reject(err);
          }
        );
      } else {
        resolve(x);
      }
    } catch (error) {
      if (isCalled) return; //如果已经resolve或reject过了,或者已经发生过异常了,则后面调用时产生的异常忽略
      isCalled = true;
      reject(error);
    }
  } else {
    resolve(x);
  }
}

thenable对象:是指一个对象,虽然它可能不是真正的 Promise 实例,但它具有类似于 Promise 的特性,即它有一个 then 方法,可以被用于处理异步操作。

// 定义一个 thenable 对象
const thenableObject = {
  then: function(resolve, reject) {
    setTimeout(function() {
      resolve("Hello from thenable!");
    }, 1000);
  }
};

// 使用 Promise 处理 thenable 对象
const promise = new Promise(function(resolve, reject) {
  resolve(thenableObject);
});

promise.then(function(result) {
  console.log(result); // 输出: Hello from thenable!
});

设置isCalled的目的:一旦当resolve或reject后,后面再调用resolve都没有用了,结果已经确定了

例如上面的代码改写如下

const thenableObject = {
  then: function(resolve, reject) {
    setTimeout(function() {
      resolve("Hello from thenable!");
      
          resolve("Hello ");//后面又调用了resolve或者reject
      reject('error');
    }, 1000);
  }
};

// 使用 Promise 处理 thenable 对象
const promise = new Promise(function(resolve, reject) {
  resolve(thenableObject);
});

promise.then(function(result) {
  console.log(result); // 输出: Hello from thenable!    
});

上面result打印的结果仍然是 Hello from thenable!

代码测试

将我们上面写的代码导出,并在下面这个test.js文件中导入

//test.js
const Promise = require("./promise2.js");

Promise.deferred = function () {
  const obj = {};

  obj.promise = new Promise(function (resolve, reject) {
    obj.resolve = resolve;
    obj.reject = reject;
  });

  return obj;
};

module.exports = Promise;

运行测试命令 npx promises-aplus-tests test.js

结果:

手写promise

转载自:https://juejin.cn/post/7270830185167568911
评论
请登录