Promise/A+ 实现

一. Promise 与 Promise/A+

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一。git

Promise/A+ 是 Promise 最小的一个规范。包括es6

- Promise 状态
- then 方法
- Promise 解析过层 
复制代码

只有一个then 方法, 没有catchraceall等方法 ECMAscript 6 Promise 是符合Promise/A+ 标准之一。github

二. 具体实现

  1. PromiseA 大致框架 举个栗子:
let p = new Promise(function(resolve, reject) {
  resolve('200');
})

p.then(data => {
  console.log(data);
}, err => {
  console.log('err', err)
})

// 200
复制代码

分析一下:编程

1. new Promise 返回一个promise对象, 接受一个`executor`执行器函数, 当即调用函数。
2. `executor` 接收两个参数`resolve`、`reject`, 同时这两个参数也是函数。
3. Promise 实例具备状态, 默认`pending`(等待), 执行器调用resolve后,实例状态变为`resolved`(成功)。 执行器调用reject,实例状态变为`rejected`(失败)。
4. Promise 实例状态一经改变, 将不能再修改。
5. promise 实例 都有 `then`方法。then 方法中有两个参数。`onResolved`成功回调函数, `onRejected`失败回调函数
6. 执行器`executor`调用`resolve`后, then中`onResolved`将会执行, 当执行器`executor`调用`reject`后, then方法第二个参数`onRejected`将会执行。
复制代码

实现一下:数组

// promise 三个状态
var PENDING = 'pending';
var RESOLVED = 'resolved';
var REJECTED = 'rejected';
function PromiseA (executor) {
  // 保存一下this, 防止this出现指向不明
  var _this = this; 
  // 初始化 promise 的值
  _this.data = undefined;
  // 初始化 promise 的状态
  _this.status = PENDING;
  function resolve(value) {
    // 在pending时修改对应状态, 和 promise 值
    if(_this.status === PENDING) {
      _this.status = RESOLVED;
      _this.data = value;
    }
  }
  function reject(reason) {
    // 在pending时修改对应状态, 和 promise 值
    if(_this.status === PENDING) {
      _this.status = REJECTED;
      _this.data = reason;
    }
  }
  executor(resolve, reject);
}

PromiseA.prototype.then = function(onResolved, onRejected) {
  var _this = this;
  // 状态是成功状态, 当即执行成功回调, 并传入其值
  if(_this.status === RESOLVED) {
    onResolved(_this.data);
  }
  // 状态是失败状态, 当即执行失败回调, 并传入其值
  if(_this.status === REJECTED) {
    onRejected(_this.data);
  }
}

module.exports = PromiseA;
复制代码
  1. 异步执行, then方法屡次调用 举个栗子:
let p = new PromiseTest(function(resolve, reject) {
  setTimeout(() => {
    resolve('200');
  }, 1000)
})

p.then(data => {
  console.log(1, data)
}, err => {
  console.log('err', err)
})

p.then(data => {
  console.log(2, data)
}, err => {
  console.log('err', err)
})
// 1, '200'
// 2, '200'
复制代码

分析一下:promise

结果将会在一秒中以后打印, 即then方法的失败和成功回调是在promise 的异步执行完以后才触发的, 因此 在调用then 方法的时候 promise 的状态并非成功或者失败, 先将成功或者失败的回调函数保存起来,等异步执行完成后再执行对应的成功或者失败回调函数。 then 方法能够调用屡次, 因此保存时须要使用数组框架

实现一下:异步

function PromiseA (executor) {
  // ...

  // 保存成功和失败的回调函数
  _this.resolvedCallbacks = [];
  _this.rejectedCallbacks = [];

  // 调用成功函数
  function resolve(value) {
    // 在pending时修改对应状态, 和 promise 值
    if(_this.status === PENDING) {
      _this.status = RESOLVED;
      _this.data = value;
      _this.resolvedCallbacks.forEach(function(fn) {
        fn();
      });
    }
  }
  // 调用失败函数
  function reject(reason) {
    // 在pending时修改对应状态, 和 promise 值
    if(_this.status === PENDING) {
      _this.status = REJECTED;
      _this.data = reason;
      _this.rejectedCallbacks.forEach(function(fn) {
        fn();
      });
    }
  }
}

PromiseA.prototype.then = function(onResolved, onRejected) {
  // ...

  // 状态是等待, 将回调函数保存起来
  if(_this.status === PENDING) {
    _this.resolvedCallbacks.push(function() {
      onResolved(_this.data);
    })
    _this.rejectedCallbacks.push(function() {
      onRejected(_this.data);
    })
  }
}
复制代码
  1. 错误捕获 举个栗子:
let p = new PromiseA((resolve, reject) => {throw new Error('error')});
p.then(data => {
  console.log(data);
}, err => {
  console.log('err', err)
})
// err Error: error
复制代码

分析一下: Promise 出错时会直接改变到失败状态, 并将失败缘由传递过去。 直接对执行函数executor 进行异常处理, 出错就进入reject函数。异步编程

实现一下:函数

function PromiseA (executor) {
  // ...
  try {
    executor(resolve, reject);
  } catch (reason) {
    reject(reason);
  }
}
复制代码
  1. then方法链式调用 举个栗子:
let p = new Promise(function(resolve, reject) {
  resolve('200');
})

p.then(data => {
  console.log(1, data)
  throw Error('oooo')
}, err => {
  console.log('1err', err)
}).then(data => {
  console.log(2, data);
}, err => {
  console.log('2err', err)
}).then(data => {
  console.log(3, data)
}, err => {
  console.log('3err', err)
})
console.log('start');

// start
// 1 '200'
// 2err Error: oooo
// 3 undefined
复制代码

分析一下:

  1. promise 是异步执行函数。 故先打印start, 使用setTimeout 保证执行顺序。
  2. Promise 实例调用then 方法后, 返回了一个新的Promise实例,
  3. 该Promise 执行成功或者失败的结果, 传递给下一个promise实例的then方法 onResolvedonRejected 回调的参数。
  4. Promise 实例链式调用 then 时, 当任何一个then执行出错时, 链时调用的下一个then时会执行错误的回调,
  5. 返回值未定义即undefined, 再次调用会执行成功的回调, 即上面的 3 undefined

实现一下:

function PromiseA (executor) {
  // ...
  function resolve(value) {
  // 在pending时修改对应状态, 和 promise 值
    setTimeout(function() {
      if(_this.status === PENDING) {
        _this.status = RESOLVED;
        _this.data = value;
        _this.resolvedCallbacks.forEach(function(fn) {
          fn();
        });
      }
    })
  }

  function reject(reason) {
  // 在pending时修改对应状态, 和 promise 值
    setTimeout(function() {
      if(_this.status === PENDING) {
        _this.status = REJECTED;
        _this.data = reason;
        _this.rejectedCallbacks.forEach(function(fn) {
          fn();
        });
      }
    })
  }
}

PromiseA.prototype.then = function(onResolved, onRejected) {
  var _this = this;
  var promise2;
  promise2 = new PromiseA(function(resolve, reject) {
    // 异步执行, 保证调用顺序
    setTimeout(function() {
      // 状态是成功状态, 当即执行成功回调, 并传入其值
      if(_this.status === RESOLVED) {
        // then方法执行 异常处理, 错误进入执行reject
        try {
          var x = onResolved(_this.data);
          resolvePromise(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason)
        }
      }
      // 状态是失败状态, 当即执行失败回调, 并传入其值
      if(_this.status === REJECTED) {
        var x = onRejected(_this.data);
        resolvePromise(promise2, x, resolve, reject);
      }
    
      // 状态是等待, 将回调函数保存起来
      if(_this.status === PENDING) {
        _this.resolvedCallbacks.push(function() {
          var x = onResolved(_this.data);
          resolvePromise(promise2, x, resolve, reject); 
        })
        _this.rejectedCallbacks.push(function() {
          var x = onRejected(_this.data);
          resolvePromise(promise2, x, resolve, reject);
        })
      }
    })
  })
  return promise2;
}

function resolvePromise(promise2, x, resolve, reject) {
  resolve(x);
}
复制代码
  1. 值的穿透 举个栗子:
let p = new Promise(function(resolve, reject) {
  resolve('200');
})

p.then()
.then(null, err => {
  console.log('1err', err)
})
.then(data => {
  console.log(data)
}, err => {
  console.log('2err', err)
})
// '200'
复制代码

分析一下: 当上一个then没有传递回调参数, 或者参数为null时, 须要将值传递给下一个then方法 then方法的两个参数都是可选参数onResolvedonRejected, 故, 判断回调函数是否为函数, 就把then的参数留空而且让值穿透到后面。

实现一下:

PromiseA.prototype.then = function(onResolved, onRejected) {
  onResolved = typeof onResolved === 'function' ? onResolved : function(value) {return value};
  onRejected = typeof onRejected === 'function' ? onREjected : function(reason) {throw reason};
  
  // ...
}
复制代码
  1. 循环引用 举个栗子:
let p = new Promise(function(resolve, reject) {
  resolve('200');
})

var p2 = p.then(() => {
  return p2;
})

p2.then(() => {
  console.log(1)
}, err => {
  console.log('err1', err)
})
// err1 TypeError: Chaining cycle detected for promise
复制代码

分析一下: 上述代码, 让 p 的then方法回调本身, 就会产生循环回调, 故, then 方法中的回调函数不能是本身自己

实现一下:

function resolvePromise(promise2, x, resolve, reject) {
  if(promise2 === x) {
    return reject(new TypeError('循环引用'));
  }
}
// ...
复制代码
  1. resolvePromise 函数实现 resolvePromise 涉及到的Promise/A+ 规范
    • 将每一个 Promise 实例调用then后返回的新 Promise 实例称为 promise2,将 then 回调返回的值称为x
    • promise2 不能够和 x 为同一个对象, 本身等待本身, 循环引用。
    • 若是x是一个对象或者函数且不是null,就去取 x 的 then 方法,若是 x 是对象,防止x 是经过 Object.defineProperty 添加 then 属性,并添加 get 和 set 监听,若是在监听中抛出异常,须要被捕获到,x.then 是一个函数,就看成 x 是一个 Promise 实例,直接执行xthen 方法,执行成功就让 promise2 成功,执行失败就让promise2 失败,若是 x.then 不是函数,则说明 x 为普通值,直接调用promise2resolve 方法将 x 传入,不知足条件说明该返回值就是一个普通值,直接执行 promise2resolve 并将 x 做为参数传入;
    • 若是每次执行xthen 方法,回调中传入的参数仍是一个 Promise 实例,循环往复,须要递归 resolvePromise 进行解析
    • 在递归的过程当中存在内、外层同时调用了 resolvereject 的状况,应该声明一个标识变量 called 作判断来避免这种状况

实现一下:

function resolvePromise(promise2, x, resolve, reject) {
  var then;
  // 为了不屡次调用
  var called = false;

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

  // x 若是是普通值(非 object 或 function), 直接resolve
  if(x !== null && (typeof x === 'object' || typeof x === 'function')){ 
    // 每一个promise 都会有then方法, 使用_then保存, 防止出错, 使用try catch 
    try {
      then = x.then;
      if(typeof then === 'function') {
        // 肯定 this 指向x
        then.call(x, function(y) {
          if(called) return;
          called = true;
          return resolvePromise(promise2, y, resolve, reject);
        }, function(e) {
          if(called) return;
          called = true;
          return reject(e);
        })

      } else {
        resolve(x);
      } 

    } catch (err) {
      if(called) return;
      called = true;
      reject(err);
    }

  } else {
    resolve(x);
  } 
}
复制代码

三. 测试一下

使用 这个包promises-aplus-tests 走下单元测试 按其说明,须要提供一个这样的类静态函数:

PromiseA.deferred = PromiseA.defer = function() {
  var dfd = {}
  dfd.promise = new PromiseA(function(resolve, reject) {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}
复制代码

四. 扩展方法

  1. catch方法 举个栗子:
let p = new Promise(function(resolve, reject) {
  resolve('200');
})

p.then(data => {
    throw new Error('eeee');
}, err => {
    console.log('err', err)
}).catch(err => {
    console.log(err)
})
// Error eeee
复制代码

实现一下:

PromiseA.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
}
复制代码
  1. resolve 方法 举个栗子:
let p = Promise.resolve('200');
p.then(data => {
    console.log(data)
}, err => {
    console.log('err', err)
})
// 200
复制代码

实现一下:

Promise.prototype.resolve = function(value) {
  return new Promise(function(resolve, reject) {
    resolve(value);
  })
}
复制代码
  1. reject 方法 举个栗子:
let p = Promise.reject('eeee');

p.then(data => {
    console.log(data)
}, err => {
    console.log('err', err)
})
// err eeee
复制代码

实现一下:

Promise.reject = function(reason) {
  return new Promise(function(resolve, reject) {
    reject(reason)
  })
}
复制代码
  1. race 方法 举个栗子:
let p1 = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve(1)
  }, 1000)
}) 
let p2 = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve(2)
  }, 2000)
}) 
let p3 = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve(3)
  }, 3000)
}) 

let p = Promise.race([p1, p2, p3])
p.then(data => {
  console.log(data)
}, err => {
  console.log('err', err)
})

// 1
复制代码

实现一下:

Promise.race = function(promises) {
  return new Promise(function(resolve, reject) {
    promises.forEach(function(promise) {
      promise.then(resolve, reject)
    }) 
  })
}
复制代码

五. 所有代码

// promise 三个状态
var PENDING = 'pending';
var RESOLVED = 'resolved';
var REJECTED = 'rejected';

/** * * @param {function} executor */
function PromiseA (executor) {
  // 保存一下this, 防止this出现指向不明
  var _this = this; 

  // 初始化 promise 的值
  _this.data = undefined;

  // 初始化 promise 的状态
  _this.status = PENDING;

  // 保存成功和失败的回调函数
  _this.resolvedCallbacks = [];
  _this.rejectedCallbacks = [];

  // 调用成功函数
  function resolve(value) {
    // 在pending时修改对应状态, 和 promise 值
    setTimeout(function() {
      if(_this.status === PENDING) {
        _this.status = RESOLVED;
        _this.data = value;
        _this.resolvedCallbacks.forEach(function(fn) {
          fn();
        });
      }
    })
  }

  // 调用失败函数
  function reject(reason) {
    // 在pending时修改对应状态, 和 promise 值
    setTimeout(function() {
      if(_this.status === PENDING) {
        _this.status = REJECTED;
        _this.data = reason;
        _this.rejectedCallbacks.forEach(function(fn) {
          fn();
        });
      }
    })
  }

  // 用于处理 new PromiseA((resolve, reject) => {throw new Error('error')})
  try {
    executor(resolve, reject);
  } catch (reason) {
    reject(reason)
  }
}

/** * * @param {promise} promise2 then 执行后返回 新的Promise对象 * @param {*} x promise中onResolved 的返回值 * @param {*} resolve promise2的resolve方法 * @param {*} reject promise2的reject方法 */
function resolvePromise(promise2, x, resolve, reject) {
  var then;
  // 为了不屡次调用
  var called = false;

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

  // x 若是是普通值(非 object 或 function), 直接resolve
  if(x !== null && (typeof x === 'object' || typeof x === 'function')){ 
    // 每一个promise 都会有then方法, 使用_then保存, 防止出错, 使用try catch 
    try {
      then = x.then;
      if(typeof then === 'function') {
        // 肯定 this 指向x
        then.call(x, function(y) {
          if(called) return;
          called = true;
          return resolvePromise(promise2, y, resolve, reject);
        }, function(e) {
          if(called) return;
          called = true;
          return reject(e);
        })

      } else {
        resolve(x);
      } 

    } catch (err) {
      if(called) return;
      called = true;
      reject(err);
    }

  } else {
    resolve(x);
  } 
}

/** * @param {function} onResolved 成功回调 * @param {function} onRejected 失败回调 */
PromiseA.prototype.then = function(onResolved, onRejected) {
  var _this = this;
  var promise2;
  // 值的穿透
  onResolved = typeof onResolved === 'function' ? onResolved : function(value) {return value};
  onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {throw reason};

  return promise2 = new PromiseA(function(resolve, reject) {
    // 异步执行, 保证调用顺序
    setTimeout(function() {
      // 状态是成功状态, 当即执行成功回调, 并传入其值
      if(_this.status === RESOLVED) {
        // 针对内部
        try {
          var x = onResolved(_this.data);
          resolvePromise(promise2, x, resolve, reject);
        } catch(reason) {
          reject(reason);
        }
      }
      // 状态是失败状态, 当即执行失败回调, 并传入其值
      if(_this.status === REJECTED) {
        try {
          var x = onRejected(_this.data);
          resolvePromise(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      }
    
      // 状态是等待, 将回调函数保存起来
      if(_this.status === PENDING) {
        _this.resolvedCallbacks.push(function() {
          try {
            var x = onResolved(_this.data);
            resolvePromise(promise2, x, resolve, reject); 
          } catch (reason) {
            reject(reason);
          }
        })
        _this.rejectedCallbacks.push(function() {
          try {
            var x = onRejected(_this.data);
            resolvePromise(promise2, x, resolve, reject);
          } catch (reason) {
            reject(reason)
          }
        })
      }
    })
  })
}

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

PromiseA.resolve = function(value) {
  return new PromiseA(function(resolve, reject) {
    resolve(value);
  })
}

PromiseA.reject = function(reason) {
  return new PromiseA(function(resolve, reject) {
    reject(reason)
  })
}

PromiseA.race = function(promises) {
  return new PromiseA(function(resolve, reject) {
    promises.forEach(function(promise) {
      promise.then(resolve, reject)
    }) 
  })
}

// 配合使用 promises-aplus-tests 测试
PromiseA.deferred = PromiseA.defer = function() {
  var dfd = {}
  dfd.promise = new PromiseA(function(resolve, reject) {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

module.exports = PromiseA;
复制代码

六. Reference

相关文章
相关标签/搜索