Promise
为什么要自己实现一个promise
深入研究Promise设计思想、Promise A+规范
开始
- 查看promise,可从vscode中查看
declare type PromiseConstructorLike = new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) => PromiseLike<T>;
promise的构造函数接收一个执行器函数executor其中接收两个参数resolve,reject, resolve可将promise状态更改为fulfilled, reject可将promise状态更改为rejected
// 根据上述定义可写出初步代码
function Promise(excutor){
this.state = 'pending'
const self = this
function resolve(value) {
self.state = 'fulfilled'
}
function reject(reason) {
self.state = 'rejected'
}
excutor(resolve, reject)
}
以下是翻译以及根据规范的实现思路
一. 术语
- “promise” 是一个拥有then方法并符合规范的对象或者函数。
- “thenable” 是定义then方法的对象或函数
- “value” 是任何合法的JavaScript值(包括undefined,thenable 或 promise)
- “exception” 是throw抛出的异常值
- “reason” 是表示rejected状态的值
二. 规范要求
Promise States (状态)
promise 必须存在于 pending、 fulfilled、 rejected三种状态之一
- 当处于pending状态时,可转变为fulfilled或rejected状态
- 当处于fulfilled状态时不可转变状态,并具有一个不可更改的value
- 当处于rejected状态时不可转变状态,并具有一个不可更改的reason
//根据上述规范完善代码
function Promise(executor) {
this.state = 'pending' // 初始pending状态
this.value = null // 保存fulfilled状态的value
this.reason = null // 保存rejected状态的reason
const self = this
self.onResolvedCallbacks = [] // 需要两个队列来存储pending状态的回调下文会有提及
self.onRejectedCallbacks = []
function resolve(value) {
if (self.state === 'pending') {
self.state = 'fulfilled'
self.value = value
self.onResolvedCallbacks.forEach(fn =>{
fn(self.value)}
)
}
}
function reject(reason) {
if(self.state === 'pending') {
self.state = 'rejected'
self.reason = reason
self.onRejectedCallbacks.forEach(fn => {
fn(self.reason)
})
}
}
executor(resolve, reject)
}
then方法
promise必须提供一个then方法来访问其value或reason,then方法接收两个参数(onFulfilled, onRejected)
- onFulfilled, onRejected是两个可选参数,如果他们不是函数,则会被忽略
- 如果onFulfilled是一个函数,必须在promise状态为fulfilled时被调用,并且将value传入onFulfilled第一个参数,不能调用多次
- 如果onRejected是一个函数,必须在promise状态为rejected时被调用,并且将reason传入onRejected第一个参数,不能多次调用
- onFulfilled onRejected在执行上下文包含其他代码时(可理解为其需要一个纯净的堆栈)平台代码除外之前不可被调用(在note3.1中说明可使用宏任务、微任务队列如setTimeout,setImmediate,mutationobserver,process.nexttick)
- onFulfilled onRejected只能被当成函数执行,this指向全局对象或undefined,取决于是否为严格模式。
- then方法可能会在同个promise被调用多次,当(如果)promise完成或拒绝时onFulfilled,onRejected回调必须按照其调用顺序执行then(此时需要两个队列,用来保存回调)
Promise.prototype.then = function(onFulfilled, onRejected) {
const self = this
if(self.state === 'fulfilled') { // "2"
setTimeout(() => { // "4"
typeof onFulfilled === 'function' && onFulfilled(self.value) // "1,5"
},0)
}
if(self.state === 'rejected') { // "3"
setTimeout(() => { // "4"
typeof onRejected === 'function' && onRejected(self.reason) // "1,5"
},0)
}
// pending状态处理 发布订阅
if(self.state === 'pending') {
self.onResolvedCallbacks.push(() => { // "6"
typeof onFulfilled === 'function' && onFulfilled(self.value)
})
self.onRejectedCallbacks.push(() => { // "6"
typeof onRejected === 'function' && onRejected(self.reason)
})
}
}
- then方法返回值必须是一个新的promise
promise2 = promise1.then() // 下文会引用promise1 promise2 - 如果onFulfilled onRejected返回一个value x 需要执行Promise Resolution Procedure(下文会详细说明)
- 如果onFulfilled onRejected抛出一个异常e,promise2要捕获这个异常e并将其作为reason置为rejected状态
- 如果onFulfilled不是一个函数并且promise1状态是fulfilled,则promise2状态必须置为fufilled,并且有与promise1相同的value
- 如果onRejected不是一个函数并且promise1状态是rejected,则promise2状态必须置为rejected,并且有与promise1相同的reason
Promise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val // "10"
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e } // "11"
const self = this
const promise2 = new Promise((resolve, reject) => { // "7"
if(self.state === 'fulfilled') {
try { // '9'
setTimeout(() => {
const x = onFulfilled(self.value) // '8'
resolvePromise(promise2, x, resolve, reject) // '8'
},0)
} catch (e) {
reject(e)
}
}
if(self.state === 'rejected') {
try { // '9'
setTimeout(() => {
const x = onRejected(self.reason) // '8'
resolvePromise(promise2, x, resolve, reject) // '8'
},0)
} catch (e) {
reject(e)
}
}
// pending状态处理 发布订阅
if(self.state === 'pending') {
self.onResolvedCallbacks.push(() => {
onFulfilled(self.value)
})
self.onRejectedCallbacks.push(() => {
onRejected(self.reason)
})
}
})
return promise2 // "7"
}
Promise Resolution Procedure
需要让不同的promise代码互相套用 , 统一叫做resolvePromise
- 如果promise2与x指向同一对象,置为reject并抛出TypeError(自己不能等待自己完成)
- 如果x是一个promise,将采用x的状态为promise2的状态,promise2的value与reason与x相同
- 其他情况,如果x是一个函数或对象,使
then = x.then - 如果检索属性x.then异常需要捕获其错误作为promise2的reason
- 如果then是函数则将x作为其this指向,第一个参数为resolvePromise,第二个参数为rejectPromise
- resolvePromise接收一个参数y 并且运行Promise Resolution Procedure resolvePromise
- rejectPromise接收一个参数r,并且将promise2状态置为reject
- resolvePromise与rejectPromise两者只能调用其一,并且只能调用一次。
- 如果调用then发生异常,抛弃resolvePromise,rejectPromise执行结果,捕获异常作为promise2的reason
- 如果then不是一个函数将promise2置为resolve并将x作为promise2的value
- 如果x不是一个函数或对象将promise2置为resolve并将x作为promise2的value
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) { // '1'
return reject(new TypeError('Circular reference error'))
}
let called = false // '8'
if(x != null && (typeof x === 'object' || typeof x === 'function')) { // '11'
try { // '4'
let then = x.then // '3'
if(typeof then === 'function') {
try {
then.call(x, (y) => { // '6'
if(called) return
called = true
resolvePromise(x, y, resolve, reject)
}, (r) => { // '7'
if(called) return
called = true
reject(r)
})
} catch (e) { // '9' 可以与外层try catch共用一个可删去
if(called) return
called = true
reject(e)
}
} else { // '10'
resolve(x)
}
} catch(e) {
if(called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
- excutor可能会执行出错,需要捕获错误
function Promise(excutor) {
....
try {
excutor(resolve, reject)
} catch (e) {
reject(e)
}
}
完整代码
function Promise(excutor) {
this.state = 'pending' // 初始pending状态
this.value = null // 保存fulfilled状态的value
this.reason = null // 保存rejected状态的reason
const self = this
self.onResolvedCallbacks = [] // 需要两个队列来存储pending状态的回调下文会有提及
self.onRejectedCallbacks = []
function resolve(value) {
if (self.state === 'pending') {
self.state = 'fulfilled'
self.value = value
self.onResolvedCallbacks.forEach(fn =>{
fn(self.value)}
)
}
}
function reject(reason) {
if(self.state === 'pending') {
self.state = 'rejected'
self.reason = reason
self.onRejectedCallbacks.forEach(fn => {
fn(self.reason)
})
}
}
excutor(resolve, reject)
}
Promise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val // "10"
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e } // "11"
const self = this
const promise2 = new Promise((resolve, reject) => {
if(self.state === 'fulfilled') {
try { // '9'
setTimeout(() => {
const x = onFulfilled(self.value) // '8'
resolvePromise(promise2, x, resolve, reject) // '8'
},0)
} catch (e) {
reject(e)
}
}
if(self.state === 'rejected') {
try { // '9'
setTimeout(() => {
const x = onRejected(self.reason) // '8'
resolvePromise(promise2, x, resolve, reject) // '8'
},0)
} catch (e) {
reject(e)
}
}
// pending状态处理 发布订阅
if(self.state === 'pending') {
self.onResolvedCallbacks.push(() => {
onFulfilled(self.value)
})
self.onRejectedCallbacks.push(() => {
onRejected(self.reason)
})
}
})
return promise2
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) { // '1'
return reject(new TypeError('Circular reference error'))
}
let called = false // '8'
if(x != null && (typeof x === 'object' || typeof x === 'function')) { // '11'
try { // '4'
let then = x.then // '3'
if(typeof then === 'function') {
then.call(x, (y) => { // '6'
if(called) return
called = true
resolvePromise(x, y, resolve, reject)
}, (r) => { // '7'
if(called) return
called = true
reject(r)
})
} else { // '10'
resolve(x)
}
} catch(e) {
if(called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
三、测试用例
- promises-aplus-tests插件可供我们进行promise测试
// 全局安装
npm install promises-aplus-tests -g
- 需要在我们的文件里添加一段代码,供测试使用
// 实现一个promise的延迟对象 defer()
Promise.deferred = function() {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
- 命令行输入promises-aplus-tests 被测试文件名称
// 例如文件名为 promise.js
promises-aplus-tests promise.js
四、promise其他方法实现
Promise.resolve Promise.reject
Promise.prototype.resolve = function (value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
Promise.prototype.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
Promise.all
- Promise.all接收一个可迭代对象Map Set Array
Promise.prototype.all = function (promiseArray) {
let valueList = []
let num = 0
return new Promise((resolve, reject) => {
try {
let index = 0
for(let item of promiseArray) {
if(typeof item === 'object' && typeof item !== null && typeof item.then === 'function') {
;(function (index) {
item.then(value => {
valueList[index] = value
num++
if(num === promiseArray.length) return resolve(valueList)
}).catch(e => { reject(e) })
})(index)
} else {
valueList[index] = item
num++
if(num === promiseArray.length) return resolve(valueList)
}
index++
}
} catch(e) {
throw new Error(e)
reject(e)
}
})
}
Promise.retry
Promise.retry = function(promise, times, delay) {
return new Promise((resolve, reject) => {
function attemp() {
promise().then((data) => {
resolve(data)
}).catch((err) => {
if (times === 0) {
reject(err)
} else {
times--
setTimeout(attemp, delay)
}
})
}
attemp()
})
}
Promise.race
Promise.prototype.race = function(promises) {
return new Promise((resolve,reject) =>{
for(let i = 0 ; i < promises.length ; i++) {
promises[i].then(resolve,reject);
}
})
}