Promise成为大家解决异步回调地狱问题的重要方法,理解它的原理和底层实现有助于我们更加合理的使用。以下遵循PromiseA+规范实现了一个简单的Promise.
实现分为六步:
- 实现同步promise
- 实现异步promise和多次then
- 实现promise的链式调用
- 特殊情况的处理
- 实现then穿透
- 测试
实现同步的promise
-
明确promise有三个状态: pending fulfilled rejected
-
在new Promise时, excutor会立即执行
-
每个promise上会有一个then方法, then方法中传递两个参数onFulfilled和onRejected
代码如下:
class Promise{
constructor(excutor){
this.status = 'pending'; // 默认是等待态
this.value = undefined;
this.reason = undefined;
let resolve = (value) => { // 调用resolve, 变成成功态
if (this.status === 'pending') {
this.status= 'fulfilled';
this.value = value;
}
};
let rejected = (reason) => { // 调用reject, 变成失败态
if (this.status === 'pending') {
this.status= 'rejected';
this.reason = reason;
}
};
excutor(resolve, rejected);
}
then(onFulfilled, onRejected){
console.log(this.status, 'status');
if (this.status === 'fulfilled') { // 变成成功态fulfilled时执行成功回调
onFulfilled(this.value);
}
if (this.status === 'rejected') { // 变成失败态rejected时执行失败回调
onRejected(this.reason);
}
}
}
实现异步promise和多个then
核心思路: 将成功和失败的回调放在数组中, 当状态改变时, 执行对应状态的事件
class Promise{
constructor(excutor){
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCbs = []; // 存放成功状态的回调
this.onRejectedCbs = []; // 存放失败状态的回调
let resolve = (value) => {
if (this.status === 'pending') {
this.status= 'fulfilled';
this.value = value;
this.onFulfilledCbs.forEach(cb => cb()); // 依次执行成功回调
}
};
let rejected = (reason) => {
if (this.status === 'pending') {
this.status= 'rejected';
this.reason = reason;
this.onRejectedCbs.forEach(cb => cb()); // 依次执行失败回调
}
};
excutor(resolve, rejected)
}
then(onFulfilled, onRejected){
...
if (this.status === 'pending') { // 有异步逻辑时,状态为pending 将callback放在数组中
this.onFulfilledCbs.push(() => {
onFulfilled(this.value);
});
this.onRejectedCbs.push(() => {
onRejected(this.reason);
});
}
}
}
实现then的链式调用
思路: 返回一个新的promise实例. jquery中的方法也能链式调用, 是通过return this实现的.但是promise不能返回this, 因为, 一个promise的状态如果是fulfilled,就不能变成rejected.
then的特点:
- 无论成功还是失败 会将成功或失败的值传递给下一级
- 如果没有接受到返回值 会默认执行下个then的
- 如果抛出错误,没有捕获,会报错
- 主动抛出异常 走onRejected 此时可以将onFulfilled赋值为null
- 主动抛出异常也可以通过catch进行捕获 并且 then()可以穿透
- catch后可以执行then 返回值会传给下个onFulfilled
- err捕获的原则: 先找最近的捕获,没有找下一个
class Promise{
constructor(excutor){
...
try{ // 1. 在执行excutor时有可能抛出错误 直接捕获错误
excutor(resolve, reject);
}catch(err) {
reject(err);
}
}
// resolvePromise 判断当前promise返回结果x和promise2的关系
resolvePromise(promise2, x, resolve, reject) {
if((typeof x === 'object' && x !== null) || typeof x === 'function'){
try{ // 取then方法时 有可能报错
let then = x.then;
if (typeof then === 'function') { // 2.2.2 返回一个promise 执行promise
then.call(x, (y) => {
resolve(y);
}, (r) => {
reject(r);
});
} else { // 2.2.3 返回一个带有then属性的普通对象 {then: ...}
resolve(x);
}
}catch(e){
reject(e);
}
} else {// 2.2.1 返回一个常量
resolve(x);
}
}
then(onFulfilled, onRejected){
// 2.实现then的链式调用(jq)
let promise2 = new Promise((resolve, reject) => { // 2.1 返回一个新的promise
if (this.status === 'fulfilled') {
try{ // 如果方法执行报错 直接抛出错误
let x = onFulfilled(this.value); // 2.2 处理前一个promise中onFulfilled的不同返回结果和下一个promise的关系: 普通值/promise
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}
if (this.status === 'rejected') {
try{
let x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}
if (this.status === 'pending') {
this.onFulfilledCbs.push(() => {
try{
let x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
});
this.onRejectedCbs.push(() => {
try{
let x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
});
}
});
return promise2;
}
}
特殊情况处理
- 返回的promise2和当前的promise不能相同: 如果相同会导致promise2的循环调用
- new Promise(resolve, reject)中resolve传递的值可能也是一个promise, 需要实现递归解析
- 避免promise即调用成功又调用失败
class Promise{
...
resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) { // 4.1 return的值不能为promise2(自己等待自己) 否则会报循环引用错误
return reject(new Error('循环引用'));
}
let called; // 4.4 定义called 避免成功失败只能调用一个
if((typeof x === 'object' && x !== null) || typeof x === 'function'){
try{
let then = x.then;
if (typeof then === 'function') {
then.call(x, (y) => {
if (called) return; // 成功和失败只会掉一个
called = true;
this.resolvePromise(promise2, y, resolve, reject); // 4.3 如果y是一个promise 进行递归解析
}, (r) => {
if (called) return; // 如果掉过成功
called = true;
reject(r);
});
} else {
if (called) return;
called = true;
resolve(x);
}
}catch(e){
if (called) return;
called = true;
reject(e);
}
} else {
if (called) return;
called = true;
resolve(x);
}
}
then(onFulfilled, onRejected){
let promise2 = new Promise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => { // 4.2 如果不加setTimeout会导致promise2为undefined-->then方法都是异步的
try{
let x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject); // 初始化promise2未完成, 为undefined
}catch(e){
reject(e);
}
});
}
if (this.status === 'rejected') {
setTimeout(() => {
try{
let x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
});
}
....
});
return promise2;
}
}
module.exports = Promise;
then穿透
思路: 如果promise执行resolve, 就将resolve中的值通过onFULfilled传递; 如果promise执行reject, 就将reject中的值通过onRejected传递
then(onFulfilled, onRejected){
// 5 穿透实现
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (data) => {
return data;
};
onRejected = typeof onRejected === 'function' ? onRejected : (err) => {
throw err;
};
...
}
测试
在promise A+规范中提出, 通过promises-aplus-tests插件对自己写的promise进行检验, 但是这个插件使用前需要通过promise.defer将promise的属性挂载在defer上, 代码如下:
Promise.deferred = Promise.defer = () => {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
下面就可以通过promises-aplus-tests插件对自己写的promise进行检验了:
- yarn add promises-aplus-tests
- npx promises-aplus-tests promise.js