手把手教你实现 Promise的使用方法

MarukoMa 2020-09-02

前言

很多 JavaScript 的初学者都曾感受过被回调地狱支配的恐惧,直至掌握了 Promise 语法才算解脱。虽然很多语言都早已内置了 Promise ,但是 JavaScript 中真正将其发扬光大的还是 jQuery 1.5 对 $.ajax 的重构,支持了 Promise,而且用法也和 jQuery 推崇的链式调用不谋而合。后来 ES6 出世,大家才开始进入全民 Promise 的时代,再后来 ES8 又引入了 async 语法,让 JavaScript 的异步写法更加优雅。

今天我们就一步一步来实现一个 Promise,如果你还没有用过 Promise,建议先熟悉一下 Promise 语法再来阅读本文。

构造函数

在已有的 Promise/A+ 规范 中并没有规定 promise 对象从何而来,在 jQuery 中通过调用 $.Deferred() 得到 promise 对象,ES6 中通过实例化 Promise 类得到 promise 对象。这里我们使用 ES 的语法,构造一个类,通过实例化的方式返回 promise 对象,由于 Promise 已经存在,我们暂时给这个类取名为 Deferred

class Deferred {
 constructor(callback) {
 const resolve = () => {
  // TODO
 }
 const reject = () => {
  // TODO
 }
 try {
  callback(resolve, reject)
 } catch (error) {
  reject(error)
 }
 }
}

构造函数接受一个 callback,调用 callback 的时候需传入 resolve、reject 两个方法。

Promise 的状态

Promise 一共分为三个状态:

手把手教你实现 Promise的使用方法

 pending :等待中,这是 Promise 的初始状态;

手把手教你实现 Promise的使用方法 

fulfilled :已结束,正常调用 resolve 的状态;

手把手教你实现 Promise的使用方法 

 rejected :已拒绝,内部出现错误,或者是调用 reject 之后的状态;

手把手教你实现 Promise的使用方法

我们可以看到 Promise 在运行期间有一个状态,存储在 [[PromiseState]] 中。下面我们为 Deferred 添加一个状态。

//基础变量的定义
const STATUS = {
 PENDING: 'PENDING',
 FULFILLED: 'FULFILLED',
 REJECTED: 'REJECTED'
}

class Deferred {
 constructor(callback) {
 this.status = STATUS.PENDING

 const resolve = () => {
  // TODO
 }
 const reject = () => {
  // TODO
 }
 try {
  callback(resolve, reject)
 } catch (error) {
  // 出现异常直接进行 reject
  reject(error)
 }
 }
}

这里还有个有意思的事情,早期浏览器的实现中 fulfilled 状态是 resolved,明显与 Promise 规范不符。当然,现在已经修复了。

手把手教你实现 Promise的使用方法

内部结果

除开状态,Promise 内部还有个结果 [[PromiseResult]] ,用来暂存 resolve/reject 接受的值。

手把手教你实现 Promise的使用方法

手把手教你实现 Promise的使用方法

继续在构造函数中添加一个内部结果。

class Deferred {
 constructor(callback) {
 this.value = undefined
 this.status = STATUS.PENDING

 const resolve = value => {
  this.value = value
  // TODO
 }
 const reject = reason => {
  this.value = reason
  // TODO
 }
 try {
  callback(resolve, reject)
 } catch (error) {
  // 出现异常直接进行 reject
  reject(error)
 }
 }
}

储存回调

使用 Promise 的时候,我们一般都会调用 promise 对象的 .then 方法,在 promise 状态转为 fulfilledrejected 的时候,拿到内部结果,然后做后续的处理。所以构造函数中,还需要构造两个数组,用来存储 .then 方法传入的回调。

class Deferred {
 constructor(callback) {
 this.value = undefined
 this.status = STATUS.PENDING

 this.rejectQueue = []
 this.resolveQueue = []

 const resolve = value => {
  this.value = value
  // TODO
 }
 const reject = reason => {
  this.value = reason
  // TODO
 }
 try {
  callback(resolve, reject)
 } catch (error) {
  // 出现异常直接进行 reject
  reject(error)
 }
 }
}

resolve 与 reject

 修改状态

接下来,我们需要实现 resolve 和 reject 两个方法,这两个方法在被调用的时候,会改变 promise 对象的状态。而且任意一个方法在被调用之后,另外的方法是无法被调用的。

new Promise((resolve, reject) => {
 setTimeout(() => {
 resolve(' 

相关推荐