PromiseA+

132 阅读8分钟

Promise简单使用

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
### })

Promsie基本术语和要求

术语

  1. "promise"是具有then方法的对象或函数,其行为符合此规范
  2. "thenable"是定义then方法的对象或函数,可以是个对象也可以是个函数
  3. "value"是任意合法的Javascript值,(包括undefined,thenable, promise)
  4. "exception"是使用throw语句抛出的值
  5. "reason"是表示promise为什么被rejected的值

要求

1.Promsie状态

  1. 一个promise必须处于三种状态之一: 请求态(pending), 完成态(fulfilled),拒绝态(rejected)
  2. 当promise处于请求状态(pending)时,promise可以转为fulfilled或rejected状态
  3. 当promise处于完成状态(fulfilled)时,promise不能转为任何其他状态,必须有一个值,且此值不能改变
  4. 当promise处于拒绝状态(rejected)时,promise不能转为任何其他状态,必须有一个原因(reason),且此原因不能改变

2.then方法

promise必须提供then方法来存取它当前或最终的值或者原因。 promise的then方法接收两个参数

promise.then(onFulfilled, onRejected)

2.1 onFulfilled和onRejected都是可选的参数:

如果 onFulfilled不是函数,必须忽略,如果 onRejected不是函数,必须忽略

2.2 如果onFulfilled是函数:

  1. 此函数必须在promise 完成(fulfilled)后被调用,并把promise 的值作为它的第一个参数
  2. 此函数在promise完成(fulfilled)之前绝对不能被调用
  3. 此函数绝对不能被调用超过一次

2.3 如果onRejected是函数

......

2.4 在执行上下文堆栈(execution context)仅包含平台代码之前,不得调用 onFulfilled和onRejected(暂时忽略)

2.5 onFulfilled和onRejected必须被当做函数调用

英文原版:(onFulfilled and onRejected must be called as functions (i.e. with no this value))

2.6 then可以在同一个promise里被多次调用

  1. 如果/当 promise 完成执行(fulfilled),各个相应的onFulfilled回调 必须根据最原始的then 顺序来调用
  2. 如果/当 promise 被拒绝(rejected),各个相应的onRejected回调 必须根据最原始的then 顺序来调用

初步的源码:

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        const resolve = (value) => {
            if(this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
            }
        }
        const reject = (reason) => {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
            }
        }
        executor(resolve, reject)
    }
    then (onFulfilled, onRejected) {
        if(this.status === FULFILLED) {
            onFulfilled(this.value)
        }
        if(this.status === REJECTED) {
            onRejected(this.reason)
        }
    }
}
module.exports = MyPromise

稍作改善

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        const resolve = (value) => {
            if(this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
            }
        }
        const reject = (reason) => {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
            }
        }
        try { // 改动点
            executor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }
    then (onFulfilled, onRejected) {
        if(this.status === FULFILLED) {
            onFulfilled(this.value)
        }
        if(this.status === REJECTED) {
            onRejected(this.reason)
        }
    }
}
module.exports = MyPromise

实现真正的异步操作 问题:

let promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('Success')
    }, 2000)
})

promise.then((value) => {
    console.log('FulFilled', value)
}, (reason) => {
    console.log('Rejected', reason)
})

解决方法:采用发布订阅模式

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) => {
            if(this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                // 发布
                this.onFulfilledCallbacks.forEach(fn => fn())
            }
        }
        const reject = (reason) => {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                // 发布
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }
    then (onFulfilled, onRejected) {
        if(this.status === FULFILLED) {
            onFulfilled(this.value)
        }
        if(this.status === REJECTED) {
            onRejected(this.reason)
        }
        if(this.status === PENDING) {
            // 订阅
            this.onFulfilledCallbacks.push(() => {
                onFulfilled(this.value)
            })
            // 订阅
            this.onRejectedCallbacks.push(() => {
                oRejected(this.reason)
            })
        }
    }
}

Promise链式调用特性

特性1:通过return返回结果,下一个then的回调函数里能获取到

let promise1 = new Promise((resolve, reject) => {
    resolve('success')
}).then((value) => {
    return value
}).then((value) => {
    console.log('value', value)
})

特性2:return的如果是一个promise的话,下一个then获取到的是这个promsie返回的结果,不是这个promise

let promise1 = new Promise((resolve, reject) => {
    resolve('success')
}).then((value) => {
    return new Promise((resolve) => {
        resolve('success2')
    })
}).then((value) => {
    console.log('value', value)
})

特性3:上一个then返回的promise执行reject,下一个then走的是onRejected

let promise1 = new Promise((resolve, reject) => {
    reject('reason')
}).then((value) => {
    console.log('value', value)
}, (reason) => {
    console.log('reason', reason)
})

特性4:上一个then走了失败的回调函数以后,下一个then走成功回调函数,值是上一个rejected返回的值

let promise1 = new Promise((resolve, reject) => {
    reject('reason')
}).then((value) => {
    console.log('value', value)
}, (reason) => {
    console.log('reason', reason)
}).then((value) => {
    console.log('value2', value)
})

特性5:上一个then的回调发生异常,下一then走的是失败的回调函数

let promise1 = new Promise((resolve, reject) => {
    reject('reason')
}).then((value) => {
    throw new Error('Error')
}, (reason) => {
    throw new Error('Error')
}).then((value) => {
    console.log('Error', value)
}, (reason) => {
    console.log('reason2', reason)
})

特性6:上一个then回调发生异常,下一个then没有定义onRejected,但是后面跟一个.catch,会执行.catch的回调

let promise1 = new Promise((resolve, reject) => {
    reject('reason')
}).then((value) => {
    throw new Error('Error')
}, (reason) => {
    throw new Error('Error')
}).then((value) => {
    console.log('Error', value)
}).catch((r) => {
    console.log('r', r)
})

特性7:又有then,又有catch,会找最近的失败的回调函数执行

let promise1 = new Promise((resolve, reject) => {
    reject('reason')
}).then((value) => {
    throw new Error('Error')
}, (reason) => {
    throw new Error('Error')
}).then((value) => {
    console.log('Error', value)
}, (r) => {
    console.log('onRejected中的r', r)
}).catch((r) => {
    console.log('catch中的r', r)
})

特性8:catch以后继续.then执行成功的回调函数

let promise1 = new Promise((resolve, reject) => {
    reject('reason')
}).then((value) => {
    throw new Error('Error')
}, (reason) => {
    throw new Error('Error')
}).then((value) => {
    console.log('Error', value)
}, (r) => {
    console.log('onRejected中的r', r)
}).catch((r) => {
    console.log('catch中的r', r)
}).then((value) => {
    console.log('catch之后的then的onFulFilled',value)
}, (reason) => {
    console.log('catch之后的then的onRejected',reason)
})

总结

执行成功的的回调函数的条件

  1. 上一个then return了一个普通的javascript值
  2. 上一个then return新的promise,并且这个promise最后状态是成功,resolve(value),这个value在回调函数里可以拿到

执行失败的回调函数的条件

  1. 上一个then return新的promise,并且这个新的promise最后状态是失败,reject(reason),reason在回调函数里可以获取到
  2. 上一个then抛出了异常

then为什么最后能进行链式调用,因为它返回了一个新的promise

链式调用源码

then必须返回一个promise

then (onFulfilled, onRejected) {
        let promise2 = new MyPromise((resolve, reject) => {
            if(this.status === FULFILLED) {
                try { // 失败总结2
                    let x = onFulfilled(this.value)
                    resolve(x)
                } catch (e) {
                    reject(e)
                }
            }
            if(this.status === REJECTED) {
                try {
                    let x = onRejected(this.reason)
                    reject(x)
                } catch (e) {
                    reject(e)
                }
            }
            if(this.status === PENDING) {
                // 订阅
                this.onFulfilledCallbacks.push(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolve(x)
                    } catch (e) {
                        reject(e)
                    }
                })
                // 订阅
                this.onRejectedCallbacks.push(() => {
                    try {
                        let x = onRejected(this.reason)
                        reject(x)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }
  1. 如果onFulfilled或onRejected返回一个值x, 运行 Promise Resolution
  2. 如果onFulfilled或onRejected抛出一个异常e,promise2 必须被拒绝(rejected)并把e当作原因
  3. 如果onFulfilled不是一个方法,并且promise1已经完成(fulfilled), promise2必须使用与promise1相同的值来完成(fulfiled)
  4. 如果onRejected不是一个方法,并且promise1已经被拒绝(rejected), promise2必须使用与promise1相同的原因来拒绝(rejected)

链式调用重写then方法

x不一定是普通值,可能又是一个promise

let promise1 = new MyPromise((resolve, reject) => {
    resolve('success')
}).then((value) => {
    return new MyPromise((resolve, reject) => {
        resolve('success2')
    })
}).then((value) => {
    console.log('value', value)
})

对x的值进行判断

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) => {
            if(this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                // 发布
                this.onFulfilledCallbacks.forEach(fn => fn())
            }
        }
        const reject = (reason) => {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                // 发布
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }
    then (onFulfilled, onRejected) {
        let promise2 = new Promise((resolve, reject) => {
            if(this.status === FULFILLED) {
                try {
                    let x = onFulfilled(this.value)
                    resolvePromise(promise2, x, resolve, reject)
                    resolve(x)
                } catch (e) {
                    reject(e)
                }
            }
            if(this.status === REJECTED) {
                try {
                    let x = onRejected(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            }
            if(this.status === PENDING) {
                // 订阅
                this.onFulfilledCallbacks.push(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
                // 订阅
                this.onRejectedCallbacks.push(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    resolve(x)
}

PromiseA+规范:如果回调函数返回了一个promise,那么现在当前promise的状态取决于返回的promsie的状态

所以禁止返回的promsie和当前promsie不能是同一个

这里有两个问题:

  1. onFulfilled必须是异步操作完成后再调用

  2. 这个时候promise2这个实例还没创建完成

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                // 发布
                this.onFulfilledCallbacks.forEach(fn => fn())
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                // 发布
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }
    then(onFulfilled, onRejected) {
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                        resolve(x)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === PENDING) {
                // 订阅
                this.onFulfilledCallbacks.push(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                        resolve(x)
                    } catch (e) {
                        reject(e)
                    }
                })
                // 订阅
                this.onRejectedCallbacks.push(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    resolve(x)
}

实现resolvePromise

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                // 发布
                this.onFulfilledCallbacks.forEach(fn => fn())
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                // 发布
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }
    then(onFulfilled, onRejected) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        console.log('onRejected低啊用')
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === PENDING) {
                // 订阅
                this.onFulfilledCallbacks.push(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
                // 订阅
                this.onRejectedCallbacks.push(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    if(promise2 === x) {
        console.log(1)
        return reject(new TypeError('Chaining cycle detected for promise#<MyPromise>'))
    }
    if((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try {
            let then = x.then // throw error
            if(typeof then === 'function') { // 走到这里就认定x是一个Promise
                then.call(x, (y) => {
                    resolve(y)
                }, (r) => {
                    reject(r)
                })
            } else {
                resolve(x)
            }
        } catch(e) {
            reject(e)
        }
    } else {
        resolve(x)
    }
}
  1. 返回的是个promsie而且调用了多次resolve或reject
  2. x的回调函数又是一个promise
  3. promise2.then().then().then().then
function resolvePromise(promise2, x, resolve, reject) {
    if(promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise#<MyPromise>'))
    }
    let called = false
    if((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try {
            let then = x.then // throw error
            if(typeof then === 'function') { // 走到这里就认定x是一个Promise
                then.call(x, (y) => {
                    if(called) return
                    called = true
                    resolvePromise(promise2, y, resolve, reject)
                }, (r) => {
                    if(called) return
                    called = true
                    reject(r)
                })
            } else {
                resolve(x)
            }
        } catch(e) {
            if(called) return
            called = true
            console.log('hahahha')
            reject(e)
        }
    } else {
        resolve(x)
    }
}

then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason } 
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === PENDING) {
                // 订阅
                this.onFulfilledCallbacks.push(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
                // 订阅
                this.onRejectedCallbacks.push(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }