从0到1实现Promise

前言

Promise你们必定都不陌生了,JavaScript异步流程从最初的Callback,到Promise,到Generator,再到目前使用最多的Async/Await(若是对于这些不熟悉的能够参考我另外一篇文章《JavaScript异步编程》),这不只仅是技术实现的发展,更是思想上对于如何控制异步的递进。Promise做为后续方案的基础,是重中之重,也是面试时候最常被问到的。git

今天咱们就一块儿从0到1实现一个基于A+规范的Promise,过程当中也会对Promise的异常处理,以及是否可手动终止作一些讨论,最后会对咱们实现的Promise作单元测试。完整的代码已经上传到github,想直接看代码的能够点这里github

虽然已经有不少带你实现Promise类的文章了,但每一个人理解的程度不同,也许不一样的文章能够带给你不一样的思考呢,那咱们就开始吧。面试

正文

1. 基础框架

new Promise()时接收一个executor函数做为参数,该函数会当即执行,函数中有两个参数,它们也是函数,分别是resolve和reject,函数同步执行必定要放在try...catch中,不然没法进行错误捕获。算法

MyPromise.js编程

function MyPromise(executor) {

  function resolve(value) {

  }

  function reject(reason) {
    
  }

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

module.exports = MyPromise;

resolve()接收Promise成功值value,reject接收Promise失败缘由reason。segmentfault

test.js数组

let MyPromise = require('./MyPromise.js');

let promise = new MyPromise(function(resolve, reject) {
  resolve(123);
})

2. 添加状态机

目前实现存在的问题:promise

  1. Promise是一个状态机的机制,初始状态为 pending,成功状态为 fulfilled,失败状态为 rejected。只能从 pending -> fulfilled,或者从 pending -> rejected,而且状态一旦转变,就永远不会再变了。

因此,咱们须要为Promise添加一个状态流转的机制。微信

MyPromise.js框架

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function MyPromise(executor) {
  let self = this;
  self.state = PENDING;


  function resolve(value) {
    if (self.state === PENDING) {
      self.state = FULFILLED;
    }
  }

  function reject(reason) {
    if (self.state === PENDING) {
      self.state = REJECTED;
    }
  }

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

module.exports = MyPromise;

test.js

let MyPromise = require('./MyPromise.js');

let promise = new MyPromise(function(resolve, reject) {
  resolve(123);
});

promise.then(function(value) {
  console.log('value', value);
}, function(reason) {
  console.log('reason', reason);
})

3. 添加then方法

Promise拥有一个then方法,接收两个函数 onFulfilledonRejected,分别做为Promise成功和失败的回调。因此,在then方法中咱们须要对状态state进行判断,若是是fulfilled,则执行onFulfilled(value)方法,若是是rejected,则执行onRejected(reason)方法。

因为成功值value和失败缘由reason是由用户在executor中经过resolve(value)reject(reason)传入的,因此咱们须要有一个全局的valuereason供后续方法获取。

MyPromise.js

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function MyPromise(executor) {
  let self = this;

  self.state = PENDING;
  self.value = null;
  self.reason = null;

  function resolve(value) {
    if (self.state === PENDING) {
      self.state = FULFILLED;
      self.value = value;
    }
  }

  function reject(reason) {
    if (self.state === PENDING) {
      self.state = REJECTED;
      self.reason = reason;
    }
  }

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

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  let self = this;

  if (self.state === FULFILLED) {
    onFuifilled(self.value);
  }

  if (self.state === REJECTED) {
    onRejected(self.reason);
  }
};

module.exports = MyPromise;

4. 实现异步调用resolve

目前实现存在的问题:

  1. 同步调用resolve()没有问题,但若是是异步调用,好比放到setTimeout中,由于目前的代码在调用then()方法时,state还是pending状态,当timer到时候调用resolve()state修改成fulfilled状态,可是onFulfilled()函数已经没有时机调用了。

针对上述问题,进行以下修改:

MyPromise.js

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function MyPromise(executor) {
  let self = this;

  self.state = PENDING;
  self.value = null;
  self.reason = null;
  self.onFulfilledCallbacks = [];
  self.onRejectedCallbacks = [];

  function resolve(value) {
    if (self.state === PENDING) {
      self.state = FULFILLED;
      self.value = value;

      self.onFulfilledCallbacks.forEach(function(fulfilledCallback) {
        fulfilledCallback();
      });
    }
  }

  function reject(reason) {
    if (self.state === PENDING) {
      self.state = REJECTED;
      self.reason = reason;

      self.onRejectedCallbacks.forEach(function(rejectedCallback) {
        rejectedCallback();
      });
    }
  }

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

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  let self = this;

  if (self.state === PENDING) {
    self.onFulfilledCallbacks.push(() => {
        onFuifilled(self.value);
    });
    self.onRejectedCallbacks.push(() => {
        onRejected(self.reason);
    });
  }

  if (self.state === FULFILLED) {
    onFuifilled(self.value);
  }

  if (self.state === REJECTED) {
    onRejected(self.reason);
  }
};

module.exports = MyPromise;

咱们添加了两个回调函数数组onFulfilledCallbacksonRejectedCallbacks,用来存储then()方法中传入的成功和失败回调。而后,当用户调用resolve()reject()的时候,修改state状态,并从相应的回调数组中依次取出回调函数执行。

同时,经过这种方式咱们也实现了能够注册多个then()函数,而且在成功或者失败时按照注册顺序依次执行。

test.js

let MyPromise = require('./MyPromise.js');

let promise = new MyPromise(function(resolve, reject) {
  setTimeout(function() {
    resolve(123);
  }, 1000);
});

promise.then(function(value) {
  console.log('value1', value);
}, function(reason) {
  console.log('reason1', reason);
});

promise.then(function(value) {
  console.log('value2', value);
}, function(reason) {
  console.log('reason2', reason);
});

5. then返回的还是Promise

读过PromiseA+规范的同窗确定知道,then()方法返回的还是一个Promise,而且返回Promise的resolve的值是上一个Promise的onFulfilled()函数或onRejected()函数的返回值。若是在上一个Promise的then()方法回调函数的执行过程当中发生了错误,那么会将其捕获到,并做为返回的Promise的onRejected函数的参数传入。好比:

let promise = new Promise((resolve, reject) => {
  resolve(123);
});

promise.then((value) => {
  console.log('value1', value);
  return 456;
}).then((value) => {
  console.log('value2', value);
});

let promise = new Promise((resolve, reject) => {
  resolve(123);
});

打印结果为:

value1 123
value2 456
let promise = new Promise((resolve, reject) => {
  resolve(123);
});

promise.then((value) => {
  console.log('value1', value);
  a.b = 2;    // 这里存在语法错误
  return 456;
}).then((value) => {
  console.log('value2', value);
}, (reason) => {
  console.log('reason2', reason);
});

打印结果为:

value1 123
reason2 ReferenceError: a is not defined

能够看到,then()方法回调函数若是发生错误,会被捕获到,那么then()返回的Promise会自动变为onRejected,执行onRejected()回调函数。

let promise = new Promise((resolve, reject) => {
  reject(123);
});

promise.then((value) => {
  console.log('value1', value);
  return 456;
}, (reason) => {
  console.log('reason1', reason);
  return 456;
}).then((value) => {
  console.log('value2', value);
}, (reason) => {
  console.log('reason2', reason);
});

打印结果为:

reason1 123
value2 456

好啦,接下来咱们就去实现then()方法依然返回一个Promise。

MyPromise.js

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  let self = this;
  let promise2 = null;

  promise2 = new MyPromise((resolve, reject) => {
    if (self.state === PENDING) {
      self.onFulfilledCallbacks.push(() => {
        try {
          let x = onFuifilled(self.value);
          self.resolvePromise(promise2, x, resolve, reject);
        } catch(reason) {
          reject(reason);
        }
      });
      self.onRejectedCallbacks.push(() => {
        try {
          let x = onRejected(self.reason);
          self.resolvePromise(promise2, x, resolve, reject);
        } catch(reason) {
          reject(reason);
        }
      });
    }
  
    if (self.state === FULFILLED) {
      try {
        let x = onFuifilled(self.value);
        self.resolvePromise(promise2, x, resolve, reject);
      } catch (reason) {
        reject(reason);
      }
    }
  
    if (self.state === REJECTED) {
      try {
        let x = onRejected(self.reason);
        self.resolvePromise(promise2, x, resolve, reject);
      } catch (reason) {
        reject(reason);
      }
    }
  });

  return promise2;
};

能够看到,咱们新增了一个promise2做为then()方法的返回值。经过let x = onFuifilled(self.value) 或者 let x = onRejected(self.reason)拿到then()方法回调函数的返回值,而后调用self.resolvePromise(promise2, x, resolve, reject),将新增的promise2xpromise2resolvereject传入到resolvePromise()中。

因此,下面咱们重点看一下resolvePromise()方法。

MyPromise.js

MyPromise.prototype.resolvePromise = function(promise2, x, resolve, reject) {
  let self = this;
  let called = false;   // called 防止屡次调用

  if (promise2 === x) {
    return reject(new TypeError('循环引用'));
  }

  if (x !== null && (Object.prototype.toString.call(x) === '[object Object]' || Object.prototype.toString.call(x) === '[object Function]')) {
    // x是对象或者函数
    try {
      let then = x.then;

      if (typeof then === 'function') {
        then.call(x, (y) => {
          // 别人的Promise的then方法可能设置了getter等,使用called防止屡次调用then方法
          if (called) return ;
          called = true;
          // 成功值y有可能仍是promise或者是具备then方法等,再次resolvePromise,直到成功值为基本类型或者非thenable
          self.resolvePromise(promise2, y, resolve, reject);
        }, (reason) => {
          if (called) return ;
          called = true;
          reject(reason);
        });
      } else {
        if (called) return ;
        called = true;
        resolve(x);
      }
    } catch (reason) {
      if (called) return ;
      called = true;
      reject(reason);
    }
  } else {
    // x是普通值,直接resolve
    resolve(x);
  }
};

resolvePromise()是用来解析then()回调函数中返回的还是一个Promise,这个Promise有多是咱们本身的,有多是别的库实现的,也有多是一个具备then()方法的对象,因此这里靠resolvePromise()来实现统一处理。

下面是翻译自PromiseA+规范关于resolvePromise()的要求:


Promise 解决过程

Promise 解决过程是一个抽象的操做,其需输入一个 promise 和一个值,咱们表示为 [[Resolve]](promise, x),若是 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;不然其用 x 的值来执行 promise 。

这种 thenable 的特性使得 Promise 的实现更具备通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法便可;这同时也使遵循 Promise/A+ 规范的实现能够与那些不太规范但可用的实现能良好共存。

运行 [[Resolve]](promise, x) 需遵循如下步骤:

  • x 与 promise 相等

若是 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise

  • x 为 Promise

若是 x 为 Promise ,则使 promise 接受 x 的状态:

- 若是 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
- 若是 x 处于执行态,用相同的值执行 promise
- 若是 x 处于拒绝态,用相同的据因拒绝 promise
  • x 为对象或函数

若是 x 为对象或者函数:

- 把 x.then 赋值给 then
- 若是取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
- 若是 then 是函数,将 x 做为函数的做用域 this 调用之。传递两个回调函数做为参数,第一个参数叫作 resolvePromise ,第二个参数叫作 rejectPromise:
    - 若是 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
    - 若是 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
    - 若是 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了屡次,则优先采用首次调用并忽略剩下的调用
    - 若是调用 then 方法抛出了异常 e:
        - 若是 resolvePromise 或 rejectPromise 已经被调用,则忽略之
        - 不然以 e 为据因拒绝 promise
    - 若是 then 不是函数,以 x 为参数执行 promise
- 若是 x 不为对象或者函数,以 x 为参数执行 promise

若是一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise。


参考上述规范,结合代码中的注释,相信你们能够理解resolvePromise()的做用了。

测试:

test.js

let MyPromise = require('./MyPromise.js');

let promise = new MyPromise(function(resolve, reject) {
  setTimeout(function() {
    resolve(123);
  }, 1000);
});

promise.then((value) => {
  console.log('value1', value);
  return new MyPromise((resolve, reject) => {
    resolve(456);
  }).then((value) => {
    return new MyPromise((resolve, reject) => {
      resolve(789);
    })
  });
}, (reason) => {
  console.log('reason1', reason);
}).then((value) => {
  console.log('value2', value);
}, (reason) => {
  console.log('reason2', reason);
});

打印结果:

value1 123
value2 789

6. 让then()方法的回调函数老是异步调用

官方Promise实现的回调函数老是异步调用的:

console.log('start');

let promise = new Promise((resolve, reject) => {
  console.log('step-');
  resolve(123);
});

promise.then((value) => {
  console.log('step--');
  console.log('value', value);
});

console.log('end');

打印结果:

start
step-
end
step--
value1 123

Promise属于微任务,这里咱们为了方便用宏任务setTiemout来代替实现异步,具体关于宏任务、微任务以及Event Loop能够参考个人另外一篇文章带你完全弄懂Event Loop

MyPromise.js

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  let self = this;
  let promise2 = null;

  promise2 = new MyPromise((resolve, reject) => {
    if (self.state === PENDING) {
      self.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onFuifilled(self.value);
            self.resolvePromise(promise2, x, resolve, reject);
          } catch (reason) {
            reject(reason);
          }
        }, 0);
      });
      self.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(self.reason);
            self.resolvePromise(promise2, x, resolve, reject);
          } catch (reason) {
            reject(reason);
          }
        }, 0);
      });
    }
  
    if (self.state === FULFILLED) {
      setTimeout(() => {
        try {
          let x = onFuifilled(self.value);
          self.resolvePromise(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      }, 0);
    }
  
    if (self.state === REJECTED) {
      setTimeout(() => {
        try {
          let x = onRejected(self.reason);
          self.resolvePromise(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      }, 0);
    }
  });

  return promise2;
};

测试:

test.js

let MyPromise = require('./MyPromise.js');

console.log('start');

let promise = new MyPromise((resolve, reject) => {
  console.log('step-');
  setTimeout(() => {
    resolve(123);
  }, 1000);
});

promise.then((value) => {
  console.log('step--');
  console.log('value', value);
});

console.log('end');

打印结果:

start
step-
end
step--
value1 123

通过以上步骤,一个最基本的Promise就已经实现完了,下面咱们会实现一些不在PromiseA+规范的扩展方法。

7. 实现catch()方法

then()方法的onFulfilledonRejected回调函数都不是必传项,若是不传,那么咱们就没法接收reject(reason)中的错误,这时咱们能够经过链式调用catch()方法用来接收错误。举例:

let promise = new Promise((resolve, reject) => {
  reject('has error');
});

promise.then((value) => {
  console.log('value', value);
}).catch((reason) => {
  console.log('reason', reason);
});

打印结果:

reason has error

不只如此,catch()能够做为Promise链式调用的最后一步,前面Promise发生的错误会冒泡到最后一个catch()中,从而捕获异常。举例:

let promise = new Promise((resolve, reject) => {
  resolve(123);
});

promise.then((value) => {
  console.log('value', value);
  return new Promise((resolve, reject) => {
    reject('has error1');
  });
}).then((value) => {
  console.log('value', value);
  return new Promise((resolve, reject) => {
    reject('has error2');
  });
}).catch((reason) => {
  console.log('reason', reason);
});

打印结果:

value 123
reason has error1

那么catch()方法究竟是如何实现的呢?

答案就是在Promise的实现中,onFulfilledonRejected函数是有默认值的:

MyPromise.js

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  onFuifilled = typeof onFuifilled === 'function' ? onFuifilled : value => {return value;};
  onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
};

MyPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
};

能够看到,onRejected的默认值是把错误reason经过throw抛出去。因为咱们对于同步代码的执行都是在try...catch中的,因此若是Promise发生了错误,若是没传onRejected,默认的函数会把错误reason抛出,而后会被promise2捕捉到,做为reject(reason)决议。

catch()实现就是调用this.then(null, onRejected),因为promise2reject,因此会执行onRejected回调,因而就捕捉到了第一个promise的错误。

总结来讲,then()方法中不传onRejected回调,Promise内部会默认帮你写一个函数做为回调,做用就是throw抛出reject或者try...catch到的错误,而后错误reason会被promise2做为reject(reason)进行决议,因而会被下一个then()方法的onRejected回调函数调用,而catch只是写了一个特殊的then(null, onRejected)而已。

因此,咱们在写Promise的链式调用的时候,在then()中能够不传onRejected回调,只须要在链式调用的最末尾加一个catch()就能够了,这样在该链条中的Promise发生的错误都会被最后的catch捕获到。

举例1:

let promise = new Promise((resolve, reject) => {
  reject(123);
});

promise.then((value) => {
  // 注意,不会走这里,由于第一个promise是被reject的
  console.log('value1', value);
  return new Promise((resolve, reject) => {
    reject('has error1');
  });
}).then((value) => {
  console.log('value2', value);
  return new Promise((resolve, reject) => {
    reject('has error2');
  });
}, (reason) => {
  // 注意,这个then有onRejected回调
  console.log('reason2', reason);
}).catch((reason) => {
  // 错误在上一个then就被捕获了,因此不会走到这里
  console.log('reason3', reason);
});

打印结果:

reason2 123

举例2:

let promise = new Promise((resolve, reject) => {
  reject(123);
});

promise.then((value) => {
  console.log('value1', value);
  return new Promise((resolve, reject) => {
    reject('has error1');
  });
}).then((value) => {
  console.log('value2', value);
  return new Promise((resolve, reject) => {
    reject('has error2');
  });
}).catch((reason) => {
  // 因为链条中的then都没有onRejected回调,因此会一直被冒泡到最后的catch这里
  console.log('reason3', reason);
});

catchthen同样都是返回一个新的Promise。有的同窗可能会有疑问,若是catch中的回调执行也发生错误该怎么办呢,这个咱们后续在Promise异常处理中再作讨论。

打印结果:

reason3 123

8. 实现finally方法

finally是某些库对Promise实现的一个扩展方法,不管是resolve仍是reject,都会走finally方法。

MyPromise.js

MyPromise.prototype.finally = function(fn) {
    return this.then(value => {
       fn();
       return value;
    }, reason => {
        fn();
        throw reason;
    });
};

9. 实现done方法

done方法做为Promise链式调用的最后一步,用来向全局抛出没有被Promise内部捕获的错误,而且再也不返回一个Promise。通常用来结束一个Promise链。

MyPromise.js

MyPromise.prototype.done = function() {
    this.catch(reason => {
        console.log('done', reason);
        throw reason;
    });
};

10. 实现Promise.all方法

Promise.all()接收一个包含多个Promise的数组,当全部Promise均为fulfilled状态时,返回一个结果数组,数组中结果的顺序和传入的Promise顺序一一对应。若是有一个Promiserejected状态,则整个Promise.allrejected

MyPromise.js

MyPromise.all = function(promiseArr) {
  return new MyPromise((resolve, reject) => {
    let result = [];

    promiseArr.forEach((promise, index) => {
      promise.then((value) => {
        result[index] = value;

        if (result.length === promiseArr.length) {
          resolve(result);
        }
      }, reject);
    });
  });
};

test.js

let MyPromise = require('./MyPromise.js');

let promise1 = new MyPromise((resolve, reject) => {
  console.log('aaaa');
  setTimeout(() => {
    resolve(1111);
    console.log(1111);
  }, 1000);
});

let promise2 = new MyPromise((resolve, reject) => {
  console.log('bbbb');
  setTimeout(() => {
    reject(2222);
    console.log(2222);
  }, 2000);
});

let promise3 = new MyPromise((resolve, reject) => {
  console.log('cccc');
  setTimeout(() => {
    resolve(3333);
    console.log(3333);
  }, 3000);
});

Promise.all([promise1, promise2, promise3]).then((value) => {
  console.log('all value', value);
}, (reason) => {
  console.log('all reason', reason);
})

打印结果:

aaaa
bbbb
cccc
1111
2222
all reason 2222
3333

11. 实现Promise.race方法

Promise.race()接收一个包含多个Promise的数组,当有一个Promisefulfilled状态时,整个大的Promiseonfulfilled,并执行onFulfilled回调函数。若是有一个Promiserejected状态,则整个Promise.racerejected

MyPromise.js

MyPromise.race = function(promiseArr) {
  return new MyPromise((resolve, reject) => {
    promiseArr.forEach(promise => {
      promise.then((value) => {
        resolve(value);   
      }, reject);
    });
  });
};

test.js

let MyPromise = require('./MyPromise.js');

let promise1 = new MyPromise((resolve, reject) => {
  console.log('aaaa');
  setTimeout(() => {
    resolve(1111);
    console.log(1111);
  }, 1000);
});

let promise2 = new MyPromise((resolve, reject) => {
  console.log('bbbb');
  setTimeout(() => {
    reject(2222);
    console.log(2222);
  }, 2000);
});

let promise3 = new MyPromise((resolve, reject) => {
  console.log('cccc');
  setTimeout(() => {
    resolve(3333);
    console.log(3333);
  }, 3000);
});

Promise.race([promise1, promise2, promise3]).then((value) => {
  console.log('all value', value);
}, (reason) => {
  console.log('all reason', reason);
})

打印结果:

aaaa
bbbb
cccc
1111
all reason 1111
2222
3333

12. 实现Promise.resolve方法

Promise.resolve用来生成一个fulfilled完成态的Promise,通常放在整个Promise链的开头,用来开始一个Promise链。

MyPromise.js

MyPromise.resolve = function(value) {
  let promise;

  promise = new MyPromise((resolve, reject) => {
    this.prototype.resolvePromise(promise, value, resolve, reject);
  });

  return promise;
};

test.js

let MyPromise = require('./MyPromise.js');

MyPromise.resolve(1111).then((value) => {
  console.log('value1', value);
  return new MyPromise((resolve, reject) => {
    resolve(2222);
  })
}).then((value) => {
  console.log('value2', value);
})

打印结果:

value1 1111
value2 2222

因为传入的value有多是普通值,有多是thenable,也有多是另外一个Promise,因此调用resolvePromise进行解析。

12. 实现Promise.reject方法

Promise.reject用来生成一个rejected失败态的Promise

MyPromise.js

MyPromise.reject = function(reason) {
  return new MyPromise((resolve, reject) => {
    reject(reason);
  });
};

test.js

let MyPromise = require('./MyPromise.js');

MyPromise.reject(1111).then((value) => {
  console.log('value1', value);
  return new MyPromise((resolve, reject) => {
    resolve(2222);
  })
}).then((value) => {
  console.log('value2', value);
}).catch(reason => {
  console.log('reason', reason);
});

打印结果:

reason 1111

13. 实现Promise.deferred方法

Promise.deferred能够用来延迟执行resolvereject

MyPromise.js

MyPromise.deferred = function() {
    let dfd = {};
    dfd.promies = new MyPromise((resolve, reject) => {
      dfd.resolve = resolve;
      dfd.rfeject = reject;
    });
    return dfd;
};

这样,你就能够在外部经过调用dfd.resolve()dfd.reject()来决议该Promise

13. 如何中止一个Promise

假设这样一个场景,咱们有一个很长的Promise链式调用,这些Promise是依次依赖的关系,若是链条中的某个Promise出错了,就不须要再向下执行了,默认状况下,咱们是没法实现这个需求的,由于Promise不管是then仍是catch都会返回一个Promise,都会继续向下执行thencatch。举例:

new Promise(function(resolve, reject) {
  resolve(1111)
}).then(function(value) {
  // "ERROR!!!"
}).catch()
  .then()
  .then()
  .catch()
  .then()

有没有办法让这个链式调用在ERROR!!!的后面就停掉,彻底不去执行链式调用后面全部回调函数呢?

咱们本身封装一个Promise.stop方法。

MyPromise.js

MyPromise.stop = function() {
  return new Promise(function() {});
};

stop中返回一个永远不执行resolve或者rejectPromise,那么这个Promise永远处于pending状态,因此永远也不会向下执行thencatch了。这样咱们就中止了一个Promise链。

new MyPromise(function(resolve, reject) {
  resolve(1111)
}).then(function(value) {
  // "ERROR!!!"
  MyPromise.stop();
}).catch()
  .then()
  .then()
  .catch()
  .then()

可是这样会有一个缺点,就是链式调用后面的全部回调函数都没法被垃圾回收器回收。

14. 如何解决Promise链上返回的最后一个Promise出现错误

看以下例子:

new Promise(function(resolve) {
  resolve(42)
}).then(function(value) {
  a.b = 2;
});

这里a不存在,因此给a.b赋值是一个语法错误,onFulfilled回调函数是包在try...catch中执行的,错误会被catch到,可是因为后面没有thencatch了,这个错误没法被处理,就会被Promise吃掉,没有任何异常,这就是常说的Promise有可能会吃掉错误

那么咱们怎么处理这种状况呢?

方法一

就是咱们前面已经实现过的done()

new Promise(function(resolve) {
  resolve(42)
}).then(function(value) {
  a.b = 2;
}).done();

done()方法至关于一个catch,可是却再也不返回Promise了,注意done()方法中不能出现语法错误,不然又没法捕获了。

方法二

普通错误监听windowerror事件能够实现捕获

window.addEventListener('error', error => {
  console.log(error); // 不会触发
});

Promise没有被onRejected()处理的错误须要监听unhandledrejection事件

window.addEventListener('unhandledrejection', error => {
  console.log('unhandledrejection', error); // 能够触发,并且还能够直接拿到 promise 对象
});

14. 单元测试

结束

相关单元测试以及完整代码能够到个人github查看,若是对你有帮助的话,就来个star吧~

单元测试

github

欢迎关注个人公众号

微信公众号

参考文档

PromiseA+规范

相关文章
相关标签/搜索