基于Promise A+规范手写一个Promise类
# 1、Promise核心逻辑实现
原生promise使用如下:
const promise = new Promise((resolve,reject)=>{
resolve('success')
reject('err')
})
promise.then(val=>{
console.log('resolve',val)
},res=>{
console.log('reject',res)
})
根据原生Promise的使用,可分析出Promise的一些基本特征:
Promise 是一个类,在执行这个类的时候会传入一个执行方法,并在会立即执行
Promise 会又三种状态
- Pending 等待
- Fulfilled 完成
- Rejected 失败
状态只能由Pending -> Fulfilled 或 Pennding -> Rejected,且不可二次修改
Promise 中使用resolve 和 reject 两个函数来修改状态
then方法则是根据状态执行响应的回调方法,状态为成功则调用成功回调函数,反之则调用失败回调函数
根据以上基本特征我们确定这个类的大致结构:
// 先定义三个状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 创建一个了
class MyPromise{
constructor(executor){
// 构造函数接收一个执行器,并立即执行
// 执行器 传入resolve 和 reject 方法
executor(this.resolve,this.reject)
}
// 存储状态的变量
_status = PENDING
// 成功之后的值
_value = null
// 失败的原因
_reason = null
// 更改成功后的状态
resolve = (value) => {
if(this._status === PENDING){
this._status = FULFILLED
this._value = value
}
}
// 更改失败后的状态
reject = (reason) => {
if(this._status === PENDING){
this._status = REJECTED
this._reason = reason
}
}
then(onFulfilled,onRejected){
if(this._status === FULFILLED){
onFulfilled(this._value)
}else if(this._status === REJECTED){
onRejected(this._reason)
}
}
}
# 2、Promise类中加入异步逻辑
原生使用如下:
const promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('success')
reject('err')
},2000)
})
promise.then(val=>{
console.log('resolve',val)
},res=>{
console.log('reject',res)
})
添加异步后,then不会立即执行对应的回调,因为这个时候promise的状态还处于 pending 状态,所以我们需要在 then 方法中对 pending 状态做响应处理。
改造后的代码如下:
// 先定义三个状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 创建一个了
class MyPromise{
constructor(executor){
// 构造函数接收一个执行器,并立即执行
// 执行器 传入resolve 和 reject 方法
executor(this.resolve,this.reject)
}
// 存储状态的变量
_status = PENDING
// 成功之后的值
_value = null
// 失败的原因
_reason = null
// 存放成功后的回调
_onFulfilledCallback = null
// 存放失败后的回调
_onRejectedCallback = null
// 更改成功后的状态
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if(this._status === PENDING){
this._status = FULFILLED
this._value = value
this._onFulfilledCallback && this._onFulfilledCallback(this._value)
}
}
// 更改失败后的状态
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if(this._status === PENDING){
this._status = REJECTED
this._reason = reason
this._onRejectedCallback && this._onRejectedCallback(this._reason)
}
}
then(onFulfilled,onRejected){
if(this._status === FULFILLED){
onFulfilled(this._value)
}else if(this._status === REJECTED){
onRejected(this._reason)
}else if(this._status === PENDING){
// 状态为等待,则记录回调函数
// 等到执行成功失败函数的时候再执行
this._onFulfilledCallback = onFulfilled
this.onRejectedCallback = onRejected
}
}
}
# 3、实现 then 方法的多次调用
之前的代码,多次调用then, 如果是执行方法是同步代码,则直接执行当前then的回调,如果执行方法异步方法,那么我们现在的MyPromise只会执行最后一次then的回调。 所以对MyPromise加以调整
- 增加两个数组存放回调函数
// 存储成功回调函数
// _onFulfilledCallback = null
_onFulfilledCallbacks = []
// 存储失败回调函数
// _onRejectedCallback = null
_onRejectedCallbacks = []
- 将回调函数存入数组中
then(onFulfilled,onRejected){
if(this._status === FULFILLED){
onFulfilled(this._value)
}else if(this._status === REJECTED){
onRejected(this._reason)
}else if(this._status === PENDING){
// 状态为等待,则记录回调函数
// 等到执行成功失败函数的时候再执行
this._onFulfilledCallback.push(onFulfilled)
this._onRejectedCallback.push(onRejected)
}
}
- 循环执行回调函数
// 更改成功后的状态
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if(this._status === PENDING){
this._status = FULFILLED
this._value = value
// 循环执行回调
while(this._onFulfilledCallback.length){
this._onFulfilledCallback.shift()(value)
}
}
}
// 更改失败后的状态
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if(this._status === PENDING){
this._status = REJECTED
this._reason = reason
// 循环执行回调
while(this._onRejectedCallback.length){
this._onRejectedCallback.shift()(reason)
}
}
}
# 4、实现 then 方法的链式调用
then 方法要链式调用,那么就需要返回一个 Promise 对象。
then 方法里 return 一个返回值作为下一个 then 方法的参数,如果是 return 一个Promise对象,则需要判断它的状态。
修改如下:
- 修改 then 方法
then(onFulfilled,onRejected){
// 为了链式调用,这里创建一个MyPromise,并return出去
const promise = new MyPromise((resolve,reject)=>{
if(this._status === FULFILLED){
// 获得回调处理结果
let res = onFulfilled(this._value)
// 集中处理回调返回值
this.resolvePromise(res,resolve,reject)
}else if(this._status === REJECTED){
let res = onRejected(this._value)
// 集中处理回调返回值
this.resolvePromise(res,resolve,reject)
}else if(this._status === PENDING){
// 状态为等待,则记录回调函数
// 等到执行成功失败函数的时候再执行
this._onFulfilledCallback.push(()=>{
let res = onFulfilled(this._value)
this.resolvePromise(res,resolve,reject)
})
this._onRejectedCallback.push(()=>{
let res = onRejected(this._value)
this.resolvePromise(res,resolve,reject)
})
}
})
return promise
}
// 处理回调返回值
resolvePromise(res, resolve, reject){
// 判断 res 是不是 MyPromise 实例
if(res instanceof MyPromise){
res.then(resolve,reject)
}else{
resolve(res)
}
}
# 5、then 特殊情况处理与异常捕获
# 识别 Promise 是否返回自己
当 then 方法返回自己的Promise 对象时,则会出相循环调用的情况,这时我们需要处理这种情况。因此对代码做出以下调整:
then(onFulfilled, onRejected) {
// 为了链式调用,这里创建一个MyPromise,并return出去
const promise = new MyPromise((resolve, reject) => {
if (this._status === FULFILLED) {
// 获得回调处理结果
let res = onFulfilled(this._value)
// 集中处理回调返回值
this.resolvePromise(promise, res, resolve, reject)
} else if (this._status === REJECTED) {
let res = onRejected(this._value)
// 集中处理回调返回值
this.resolvePromise(res, resolve, reject)
} else if (this._status === PENDING) {
// 状态为等待,则记录回调函数
// 等到执行成功失败函数的时候再执行
this._onFulfilledCallback.push(() => {
let res = onFulfilled(this._value)
this.resolvePromise(promise, res, resolve, reject)
})
this._onRejectedCallback.push(() => {
let res = onRejected(this._value)
this.resolvePromise(promise, res, resolve, reject)
})
}
})
return promise
}
// 处理回调返回值
resolvePromise(promise, res, resolve, reject) {
if (promise === res) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 判断 res 是不是 MyPromise 实例
if (res instanceof MyPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
}
运行会发现,报错:
ReferenceError: Cannot access 'promise' before initialization
不能在 promise 初始化前使用,那么这个时候就需要用到一个异步函数去等待 promise 初始完成,这里使用 queueMicrotask 来创建一个微任务
改造如下:
then(onFulfilled, onRejected) {
// 为了链式调用,这里创建一个MyPromise,并return出去
const promise = new MyPromise((resolve, reject) => {
if (this._status === FULFILLED) {
queueMicrotask(()=>{
// 获得回调处理结果
let res = onFulfilled(this._value)
// 集中处理回调返回值
this.resolvePromise(promise, res, resolve, reject)
})
} else if (this._status === REJECTED) {
queueMicrotask(()=>{
let res = onRejected(this._value)
// 集中处理回调返回值
this.resolvePromise(res, resolve, reject)
})
} else if (this._status === PENDING) {
// 状态为等待,则记录回调函数
// 等到执行成功失败函数的时候再执行
this._onFulfilledCallback.push(() => {
queueMicrotask(()=>{
let res = onFulfilled(this._value)
this.resolvePromise(promise, res, resolve, reject)
})
})
this._onRejectedCallback.push(() => {
queueMicrotask(()=>{
let res = onRejected(this._value)
this.resolvePromise(promise, res, resolve, reject)
})
})
}
})
return promise
}
# 错误捕获
- 捕获执行器错误
constructor(executor){
// 构造函数接收一个执行器,并立即执行
// 执行器 传入resolve 和 reject 方法
try {
executor(this.resolve,this.reject)
}catch(err){
// 如果有错误,直接执行 reject
this.reject(err)
}
}
- then 执行时的错误捕获
then(onFulfilled, onRejected) {
......
queueMicrotask(()=>{
try{
// 获得回调处理结果
let res = onFulfilled(this._value)
// 集中处理回调返回值
this.resolvePromise(promise, res, resolve, reject)
}catch(err){
reject(error)
}
})
......
}
# then 参数可选
then(onFulfilled, onRejected) {
// 如果不传,就使用默认函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
......
}
# 实现 resolve 和 reject 的静态调用
为支持 Promise.resolve 和 Promise.reject 的用法,我们在MyPromise类中添加两个静态方法:
class MyPromise{
......
// resolve 静态方法
static resolve(arg){
// 如果参数时MyPromise实例就直接返回
if(arg instanceof MyPromise){
return arg
}
// 转换成常规方式
return new MyPromise((resolve, reject)=>{
resolve(arg)
})
}
// reject 静态方法
static reject(reason){
return new MyPromise((resolve, reject)=>{
reject(reason)
})
}
}
# Promise A+ 测试
# 安装 promises-aplus-tests 并配置启动命令
npm install promises-aplus-tests -D
// package.json
{
"name": "promise",
"version": "1.0.0",
"description": "手写的promise",
"main": "MyPromise.js",
"scripts": {
"test": "promises-aplus-tests MyPromise"
},
"author": "liuxia",
"license": "ISC",
"devDependencies": {
"promises-aplus-tests": "^2.1.2"
}
}
然后开启测试
npm run test
我们会发现如下报错:
TypeError: adapter.deferred is not a function
# 手写代码中加入 deferred
MyPromise {
......
}
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
module.exports = MyPromise
# 完善 resolvePromise 方法
// 处理回调返回值
function resolvePromise(promise, x, resolve, reject) {
// 如果返回的是自己,抛出错误并返回
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 判断 x 是不是 MyPromise 实例
if (typeof x === 'object' || typeof x === 'function') {
if(x === null){
return resolve(x)
}
let then
try {
// 把 x.then 赋值给then
then = x.then
}catch(error){
// 如果取 x.then 的值时抛出错误 error ,则以 error 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (typeof then === 'function') {
let called = false;
try {
then.call(
x, // this 指向 x
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 实现这条需要前面加一个变量 called
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r => {
if (called) return;
called = true;
reject(r);
});
} catch (error) {
// 如果调用 then 方法抛出了异常 error:
// 如果 resolvePromise 或 rejectPromise 已经被调用,直接返回
if (called) return;
// 否则以 error 为据因拒绝 promise
reject(error);
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else {
resolve(x)
}
}