【源码】手写实现一个Promise

手写实现一个Promise

一、实现思路

1、Promise的基本结构

首先我们先来看下Promise的基本结构 👀

const p = new Promise((resolve,reject) => {
  resolve(1)
})

2、Promise的状态转换

Promise有三个状态:

😮 pending(等待处理)

💖 fulfilled(处理成功)

💢 rejected(处理失败)

Promise 状态的改变是不可逆的,只能从 pending 转换到 fulfilled 或者从 pending 转换到 rejected.
当某个状态改变时,将执行then方法绑定的回调函数。

3、Promise的then方法

Promisethen 方法接收两个参数。

  • 第一个参数为成功时的回调函数
  • 第二个参数为失败时的回调函数。

一个常常用到的语法是在 then 方法中返回一个新的 Promise。如果在回调函数中 return 了一个普通值,那么将会把这个值作为参数,传递给后面的 then 方法的回调函数。

如果在回调函数中 return 了一个 Promise,那么下一个 then 方法的回调函数将会等到这个 Promise 执行完毕之后再执行,并且它的状态(成功或失败)也将作为参数,传递给后面的 then 方法的回调函数。

4、Promise的链式调用

Promise 的链式调用是通过在每次 then 方法中返回一个新的 Promise 实例来实现的。
链式调用保证了在每一个 then 中返回普通值或 Promise 都可以被后面 then 的回调函数获取到。

5、Promise的错误捕获

在 then 方法中捕获错误有两种方式:

  • 第一种方式是在每个 then 中使用 try catch 捕获错误(不推荐,但这里我并没有实现catch方法

  • 第二种方式是使用 Promise 对象的 catch 方法捕获错误。

二、具体实现(ts)

🐸 我们对Promise的基本思路已经有了比较清晰的定义,接下来我们来实现具体的代码(取名为MyPromise)。

1、MyPromise状态

首先定义一个枚举类,来定义MyPromise的状态。

enum PromiseStatus {
  OK = 'fulfilled',
  LOADING = 'pending',
  FAIlURE = 'rejected'
}

2.基本结构

回顾一下,Promise的使用,new一个Promise实例,然后传递一个函数,这个函数会在Promise实例创建的时候立即执行,并且接受两个参数,用来改变这个Promise的状态:resolvereject

const p = new Promise((resolve,reject) => {
  resolveg(1)
})

我们来实现一下这个基本结构。

class MyPromise {

  constructor(callback) {
     callback(this.resolve, this.reject);
  }

  resolve = (value: any) => {
    // 成功状态处理
  };

  reject = (errorValue: any) => {
    // 失败状态处理
  }

}

💦加点料,我们来实现一下MyPromise的状态转换。但在这之前,我们先来定义一下MyPromise的状态和对应的值。

class MyPromise {

  // 成功的值 
  value: any = void 0; 
  // 当前的状态 默认为等待状态 
  status = PromiseStatus.LOADING 
  // 失败的值 
  failureValue: any = void 0; 

  constructor(callback) {
     callback(this.resolve, this.reject);
  }

  resolve = (value: any) => {
    // 成功状态处理
  };

  reject = (errorValue: any) => {
    // 失败状态处理
  }

}

实现resolve(),只有当加载状态为pending的时候,才能改变状态为fulfilledrejected,并且把成功的值赋值给value

  resolve = (value: any) => {
    if (this.status === PromiseStatus.LOADING) {
      this.value = value;
      this.status = PromiseStatus.OK;
    }
  };

实现reject(),与resolve()类似。

  reject = (errorValue: any) => {
    if (this.status === PromiseStatus.LOADING) {
      this.failureValue = errorValue;
      this.status = PromiseStatus.FAIlURE;
    }
  };

3、then方法

现在我们已经实现了MyPromise的基本结构,接下来我们来实现then()方法。在 then 方法中,主要处理了okCallback和errorCallback的执行及参数传递。

  then(okCallback: any, errorCallback: any) {
    // 如果状态为成功,执行成功回调
    if (this.status === PromiseStatus.OK) {
      okCallback(this.value);
    }else if (this.status === PromiseStatus.FAIlURE) {
      // 如果状态为失败,执行失败回调
      errorCallback(this.failureValue);
    }else if(this.status === PromiseStatus.LOADING){
      // 如果还未完成
    }
  }

⚗️测试一下

const p = new MyPromise((resolve) => {
  console.log(1);
  resolve('ok')
})

p.then((value) => {
  console.log(value)
}, () => { })

输出结果:

1
ok

😄 一切正常。

4、链式调用

then()方法中,我们返回了一个新的MyPromise实例,这样就可以实现链式调用了。

  then(okCallback: any, errorCallback: any) {
    const newMyPromise = new MyPromise((resolve, reject) => {
      // 如果状态为成功,执行成功回调
      if (this.status === PromiseStatus.OK) {
        okCallback(this.value);
      } else if (this.status === PromiseStatus.FAIlURE) {
        // 如果状态为失败,执行失败回调
        errorCallback(this.failureValue);
      } else if (this.status === PromiseStatus.LOADING) {
        // 如果还未完成
      }
    })
    return newMyPromise
  }

⚗️再测试一下

const p = new MyPromise((resolve) => {
  console.log(1);
  resolve('ok')
}).then((value) => {
  console.log(value)
}, () => { })

⭐️输出结果:

1
ok

5、异步调用

现在的MyPromise实例是同步执行的,如果在回调中加入异步代码:

const p = new MyPromise((resolve) => {
  console.log(1);
  setTimeout(() => {
    resolve('ok')
  }, 1000)
}).then((value) => {
  console.log(value)
}, () => { })

输出结果:

1

💢 可以看到,这个ok并没有打印。

接下来我们来实现异步调用,选用的微队列实现方式技术栈是queueMicrotask

定义两个队列,分别用于存放成功和失败的回调。

  // ...

 // 成功的值
  value: any = void 0;
  // 当前的状态 默认为等待状态
  status = PromiseStatus.LOADING
  // 失败的值
  failureValue: any = void 0;
  // 成功的回调队列
  okCallbackQueue: Function[] = [];
  // 失败的回调队列
  errorCallbackQueue: Function[] = [];

  // ...

接着给then()加上异步的逻辑

  then(okCallback: any, errorCallback: any) {
    const newMyPromise = new MyPromise((resolve, reject) => {
      // 如果状态为成功,执行成功回调
      if (this.status === PromiseStatus.OK) {
        okCallback(this.value);
      } else if (this.status === PromiseStatus.FAIlURE) {
        // 如果状态为失败,执行失败回调
        errorCallback(this.failureValue);
      } else if (this.status === PromiseStatus.LOADING) {
        // 如果还未完成 
        this.okCallbackQueue.push(okCallback);
        this.errorCallbackQueue.push(errorCallback);
      }
    })
    return newMyPromise
  }

然后是resolvereject的异步逻辑

  resolve = (value: any) => {
    if (this.status === PromiseStatus.LOADING) {
      this.value = value;
      this.status = PromiseStatus.OK;

      while (this.okCallbackQueue.length) {
        this.okCallbackQueue.shift()!();
      } 
    }
  };

  reject = (errorValue: any) => {
    if (this.status === PromiseStatus.LOADING) {
      this.failureValue = errorValue;
      this.status = PromiseStatus.FAIlURE;

      while (this.errorCallbackQueue.length) {
        this.errorCallbackQueue.shift()!();
      } 
    }
  };
//这里的`!`是因为`shift()`可能返回`undefined`,所以加上`!`告诉ts,这里一定有值
this.okCallbackQueue.shift()!();
this.errorCallbackQueue.shift()!()

⚗️再测试一下

const p = new MyPromise((resolve) => {
  console.log(1);
  setTimeout(() => {
    resolve('ok')
  }, 1000)
}).then((value) => {
  console.log(value)
}, () => { })

输出结果:

1
ok

😄 完美!

还有一种情况需要处理,当连续的链式调用then,并且不带参数:

const p = new MyPromise((resolve) => {
  console.log(1);
  resolve('ok')
}).then() // ts会报错,因为then()的参数是必填的
  .then()
  .then()
  .then()
  .then((value) => {
    console.log(value)
  },() => {})

输出结果:

1

👀 'ok'没有打印,这是因为调用 then()之后的返回值没有处理。 接着来吧!

首先处理ts报错的问题,给then的参数加上默认值

  then(okCallback: any = (value: any) => value, errorCallback: any = (errorValue: any) => { throw errorValue }) {
    // ...
  }

然后参数归一化

  then(okCallback: any = (value: any) => value, errorCallback: any = (errorValue: any) => { throw errorValue }) {
    if (typeof okCallback !== 'function') {
      okCallback = (value: any) => value
    }
    if (typeof errorCallback !== 'function') {
      errorCallback = (errorValue: any) => { throw errorValue }
    }
    // ...
  }

处理then()的返回值,我们写一个辅助函数。

  returnValuePromiseManage(result: any, resolve: Function, reject: Function) {
    if (result instanceof MyPromise) {
      // then返回的是一个MyPromise 调用then
      result.then(resolve, reject);
    } else {
      // 不返回或者普通值
      resolve(result)
    }
  }

修改then()

  then(okCallback: any = (value: any) => value, errorCallback: any = (errorValue: any) => { throw errorValue }) {
    // 兼容js
    if (typeof okCallback !== 'function') {
      okCallback = (value: any) => value
    }
    if (typeof errorCallback !== 'function') {
      errorCallback = (errorValue: any) => { throw errorValue }
    }
    const newMyPromise = new MyPromise((resolve, reject) => {
      // 如果状态为成功,执行成功回调
      if (this.status === PromiseStatus.OK) {
        const result = okCallback(this.value); 
        this.returnValuePromiseManage(result, resolve, reject); 
      } else if (this.status === PromiseStatus.FAIlURE) {
        // 如果状态为失败,执行失败回调
        const result = errorCallback(this.failureValue); 
        this.returnValuePromiseManage(result, resolve, reject); 
      } else if (this.status === PromiseStatus.LOADING) {
        // 如果还未完成
        this.okCallbackQueue.push(okCallback);
        this.errorCallbackQueue.push(errorCallback);
      }
    })
    return newMyPromise
  }

再测试一下

const p = new MyPromise((resolve) => {
  console.log(1);
  resolve('ok')
}).then()
  .then()
  .then()
  .then()
  .then((value) => {
    console.log(value)
  }, () => { })

输出结果:

1
ok

😄 完美!

接着我们处理一下then()中返回自身的情况 ,在Promise/A+规范中,规定了这种情况下应该抛出一个错误。

const p = new Promise((resolve) => {
  resolve(1)
})

const p1 = p.then(() => {
  return p1
})
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

修改一下刚刚写的辅助函数

  returnValuePromiseManage(newPromise: MyPromise, result: any, resolve: Function, reject: Function) {

    if (newPromise === result) {
      // 这里简单的打印一下错误信息
      const msg = 'Chaining cycle detected for promise #<MyPromise>';
      console.error(msg)
      reject(new TypeError(msg))
    }

    if (result instanceof MyPromise) {
      // then返回的是一个MyPromise 调用then
      result.then(resolve, reject);
    } else {
      // 不返回或者普通值
      resolve(result)
    }
  }

修改then()

  then(okCallback: any = (value: any) => value, errorCallback: any = (errorValue: any) => { throw errorValue }) {
    // 兼容js
    if (typeof okCallback !== 'function') {
      okCallback = (value: any) => value
    }
    if (typeof errorCallback !== 'function') {
      errorCallback = (errorValue: any) => { throw errorValue }
    }
    const newMyPromise = new MyPromise((resolve, reject) => { 
      // 如果状态为成功,执行成功回调
      if (this.status === PromiseStatus.OK) {
        const result = okCallback(this.value);
        this.returnValuePromiseManage(newMyPromise, result, resolve, reject); 
      } else if (this.status === PromiseStatus.FAIlURE) {
        // 如果状态为失败,执行失败回调
        const result = errorCallback(this.failureValue);
        this.returnValuePromiseManage(newMyPromise, result, resolve, reject); 
      } else if (this.status === PromiseStatus.LOADING) {
        // 如果还未完成
        this.okCallbackQueue.push(okCallback);
        this.errorCallbackQueue.push(errorCallback);
      }
    })
    return newMyPromise
  }

由于这里的需要newMyPromise,但这里直接传入会报错

// Uncaught SyntaxError: Missing initializer in const declaration

所以我们需要把处理返回值的和执行okCallback的逻辑放到微队列中去执行,还需要处理异常,我这里就直接使用try捕获

  then(okCallback: any = (value: any) => value, errorCallback: any = (errorValue: any) => { throw errorValue }) {
    
    if (typeof okCallback !== 'function') {
      okCallback = (value: any) => value
    }
    if (typeof errorCallback !== 'function') {
      errorCallback = (errorValue: any) => { throw errorValue }
    }
    const newMyPromise = new MyPromise((resolve, reject) => {
      // 如果状态为成功,执行成功回调  
      if (this.status === PromiseStatus.OK) { 
        queueMicrotask(() => { 
          try { 
            const result = okCallback(this.value); 
            this.returnValuePromiseManage(newMyPromise, result, resolve, reject); 
          } catch (e) { 
            reject(e) 
          } 
        }) 
      } else if (this.status === PromiseStatus.FAIlURE) { 
        // 如果状态为失败,执行失败回调 
        queueMicrotask(() => { 
          try { 
            const result = errorCallback(this.failureValue); 
            this.returnValuePromiseManage(newMyPromise, result, resolve, reject); 
          } catch (e) { 
            reject(e) 
          } 
        }) 
      } else if (this.status === PromiseStatus.LOADING) {  
        // 如果还未完成 同样加入微队列 
        this.okCallbackQueue.push(() => { 
          queueMicrotask(() => { 
            try { 
              const result = okCallback(this.value); 
              this.returnValuePromiseManage(newMyPromise, result, resolve, reject); 
            } catch (e) { 
              reject(e) 
            } 
          }) 
        });
        this.errorCallbackQueue.push(() => { 
          queueMicrotask(() => { 
            try { 
              const result = errorCallback(this.failureValue); 
              this.returnValuePromiseManage(newMyPromise, result, resolve, reject); 
            } catch (e) { 
              reject(e) 
            }
          })
        });
      }
    })
    return newMyPromise
  }

emm…代码有些冗余,写一个高阶函数优化一下

    // 微队列生成器
  actuator = (call: Function, reject) => {
    return () => {
      queueMicrotask(() => {
        try {
          call();
        } catch (e) {
          reject(e)
        }
      })
    }
  }

then()

  then(okCallback: any = (value: any) => value, errorCallback: any = (errorValue: any) => { throw errorValue }) {

    if (typeof okCallback !== 'function') {
      okCallback = (value: any) => value
    }
    if (typeof errorCallback !== 'function') {
      errorCallback = (errorValue: any) => { throw errorValue }
    }

    const newMyPromise = new MyPromise((resolve, reject) => {

      const okFun = () => {
        const result = okCallback(this.value);
        this.returnValuePromiseManage(newMyPromise, result, resolve, reject);
      }

      const errorFun = () => {
        const result = errorCallback(this.failureValue);
        this.returnValuePromiseManage(newMyPromise, result, resolve, reject);
      }

      // 如果状态为成功,执行成功回调
      if (this.status === PromiseStatus.OK) {
        this.actuator(okFun, reject)(); 
      } else if (this.status === PromiseStatus.FAIlURE) {
        this.actuator(errorFun, reject)(); 
      } else if (this.status === PromiseStatus.LOADING) {
        // 如果还未完成 同样加入微队列 
        this.okCallbackQueue.push(() => {
          queueMicrotask(okFun)
        });
        this.errorCallbackQueue.push(() => {
          queueMicrotask(errorFun)
        });
      }
    })
    return newMyPromise
  }

在构造函数里也加入一个try catch

  constructor(callback: Function) {
    try {
      callback(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }

💦测试一下

const promise = new MyPromise((resolve, reject) => {
  resolve(1)
})

const p2 = promise.then(() => {
  return p2
})

p2.then((res) => {
  console.log('成功', res)
}, () => {
  console.log('失败')
})

运行结果

Chaining cycle detected for promise #<MyPromise>
失败

👀 完美! 这里的p2是一个MyPromise实例,所以会进入returnValuePromiseManage函数,然后判断p2newMyPromise是否相等,相等就会抛出异常,所以会进入catch,打印失败

然后我们实现resolvereject的静态方法


  static resolve(params: any = void 0) {
    if (params instanceof MyPromise) {
      return params;
    }

    return new MyPromise((resolve) => { resolve(params) })
  }

  static reject(params: any = void 0) {
    return new MyPromise((resolve, reject) => {
      return reject(params)
    })
  }

测试一下

const p1 = MyPromise.resolve(1)
const p2 = MyPromise.resolve(p1)
const p3 = MyPromise.reject(1)
const p4 = MyPromise.reject(p3)

p1.then((res) => {
  console.log('p1', res)
})

p2.then((res) => {
  console.log('p2', res)
})

p3.then((res) => {
  console.log('p3', res)
}, (err) => {
  console.log('p3', err)
})

p4.then((res) => {
  console.log('p4', res)
}, (err) => {
  console.log('p4', err)
})

运行结果

p1 1
p2 1
p3 1
p4 MyPromise { status: 2, value: 1, failureValue: undefined, okCallbackQueue: [], errorCallbackQueue: [] }

👀 完美!

到这里,一个简单的Promise就实现了,但是还有一些功能没有实现,比如catchfinallyallrace等等,这些功能都是基于then实现的,所以我们只需要在then方法上做一些修改就可以了。

💫全部代码



enum PromiseStatus {
  OK = 'fulfilled',
  LOADING = 'pending',
  FAIlURE = 'rejected'
}


class MyPromise {

  // 成功的值
  value: any = void 0;
  // 当前的状态 默认为等待状态
  status = PromiseStatus.LOADING
  // 失败的值
  failureValue: any = void 0;
  // 成功的回调队列
  okCallbackQueue: Function[] = [];
  // 失败的回调队列
  errorCallbackQueue: Function[] = [];

  constructor(callback) {
    try {
      callback(this.resolve, this.reject);
    } catch (e) {
      this.reject(e)
    }
  }

  resolve = (value: any) => {
    if (this.status === PromiseStatus.LOADING) {
      this.value = value;
      this.status = PromiseStatus.OK;

      while (this.okCallbackQueue.length) {
        this.okCallbackQueue.shift()!();
      }
    }
  };

  reject = (errorValue: any) => {
    if (this.status === PromiseStatus.LOADING) {
      this.failureValue = errorValue;
      this.status = PromiseStatus.FAIlURE;

      while (this.errorCallbackQueue.length) {
        this.errorCallbackQueue.shift()!();
      }
    }
  };


  then(okCallback: any = (value: any) => value, errorCallback: any = (errorValue: any) => { throw errorValue }) {

    if (typeof okCallback !== 'function') {
      okCallback = (value: any) => value
    }
    if (typeof errorCallback !== 'function') {
      errorCallback = (errorValue: any) => { throw errorValue }
    }

    const newMyPromise = new MyPromise((resolve, reject) => {

      const okFun = () => {
        const result = okCallback(this.value);
        this.returnValuePromiseManage(newMyPromise, result, resolve, reject);
      }

      const errorFun = () => {
        const result = errorCallback(this.failureValue);
        this.returnValuePromiseManage(newMyPromise, result, resolve, reject);
      }

      // 如果状态为成功,执行成功回调
      if (this.status === PromiseStatus.OK) {
        this.actuator(okFun, reject)();
      } else if (this.status === PromiseStatus.FAIlURE) {
        this.actuator(errorFun, reject)();
      } else if (this.status === PromiseStatus.LOADING) {
        // 如果还未完成 同样加入微队列
        this.okCallbackQueue.push(() => {
          queueMicrotask(okFun)
        });
        this.errorCallbackQueue.push(() => {
          queueMicrotask(errorFun)
        });
      }
    })
    return newMyPromise
  }

  returnValuePromiseManage(newPromise: MyPromise, result: any, resolve: Function, reject: Function) {

    if (newPromise === result) {
      const msg = 'Chaining cycle detected for promise #<MyPromise>';
      console.error(msg)
      reject(new TypeError(msg))
    }

    if (result instanceof MyPromise) {
      // then返回的是一个MyPromise 调用then
      result.then(resolve, reject);
    } else {
      // 不返回或者普通值
      resolve(result)
    }
  }

  // 微队列生成器
  actuator = (call: Function, reject) => {
    return () => {
      queueMicrotask(() => {
        try {
          call();
        } catch (e) {
          reject(e)
        }
      })
    }
  }


  static resolve(params: any = void 0) {
    if (params instanceof MyPromise) {
      return params;
    }

    return new MyPromise((resolve) => { resolve(params) })
  }

  static reject(params: any = void 0) {
    return new MyPromise((resolve, reject) => {
      return reject(params)
    })
  }
}

四、完结

😵 : 使用js实现的Promise性能自然不及原生,但理解Promise的原理和实现,对于我们更好的使用Promise是很有帮助的。

轻松愉悦的学习冒险,下次见!🍊

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值