promise详解 : 实现promise(附实现代码)
promise then 的特点 :
- then 函数的返回值是一个 promise, 可以继续调用 then 函数
- 回调函数 resolve 和 reject 的参数 value /reason, 可以传递给 then函数的回调函数, 最终 resolve(res) 的res 传递给了 then(onFulfilled(v)=>{},onrejected(e)=>{}) 中的 v, 而 reject(err) 中的 err 传递给了其中的 e
then 函数的回调函数的返回值可以分成 :
- 普通值(字符串、对象、函数、undefined等), 只要不是 promise 或者 错误, 都可以看做是普通值
- Promise 的实例
- 错误
根据 then 函数的回调函数的返回值, 确定下一次链式调用的方式
- then 函数的回调函数的返回值 : 即 onFulfilled 函数的返回值 , 或者 onRejected 的函数的返回值
1. then 函数的回调函数的返回值是普通值
- then 函数的返回值可以是 onFulFilled 函数的返回值, 也可以是 onRejected 函数的返回值, 若是不用 return 指定返回值, 则默认返回undefined。 如果该返回值是普通的值, 则下一次链式调用(即调用then)进入 onFulfilled 回调函数, 并且将该返回值作为 onFulfilled 回调函数的参数
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
resolve('ok1')
},1000)
})
p.then(res=> {
return 'ok2'
}).then(res=> {
console.log(res)
}).then(res=> {
console.log(res)
})
// 输出:
// ok2
// undefined
- 对于 onReject 返回普通类型的值, 也按照上述规则:
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
reject('not ok1')
},1000)
})
p.then(res=> {
return 'ok2'
},(e)=> {
return 'not ok2'
}).then(res=> {
console.log(res)
},(e)=> {
console.log(e)
}).then(res=> {
console.log(res)
})
// 输出 :
// not ok2
// undefined
- 在 onRejected 回调函数中不用return指定返回值, 在下一次链式调用的时候, 也会 进入 resolve 函数, 只有发成错误才可以进入下个链式调用的 Onrejected 回调函数:
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
reject('not ok1')
},1000)
})
p.then(res=> {
return 'ok2'
},(e)=> {
console.log(e)
}).then(res=> {
console.log(res)
},(e)=> {
console.log(e)
}).then(res=> {
throw new Error('Error')
}, e=> {
console.log(e)
}).then(res=> {
console.log(res)
},e=> {
console.log(e)
})
// not ok1
// undefined
// Error: Error
2. then 函数的回调函数的返回值是 Promise 的实例, 即一个新的 promise
- 下一次链式调用会采用这个被返回的 promise 的状态, 如果这个 promise 的状态是 fulfilled 的则, 下次链式调用进入 onFulfilled 函数, 如果这个promise 的状态是 rejected, 则下次链式调用进入 onRejected 函数
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
resolve('ok1')
},1000)
})
p.then(res=> {
return new Promise((resolve, reject)=> {
console.log(res)
resolve('ok2')
})
},(e)=> {
console.log(e, 'error1')
}).then((res)=> {
console.log(res)
return new Promise((reolve,reject)=> {
reject('notok1')
})
},e=> {
console.log(e, 'error2')
}).then(res=> {
console.log(res)
}, e=> {
console.log(e, 'error3')
})
// ok1
// ok2
// notok1 error3
3. then 函数的回调函数中发生错误
如果错误没有被就近处理, 则会沿着链式调用的链条一直向下传递, 直到找到一个错误处理的 onRejected 回调函数, 再根据 onRejected 的返回值确定后边的链式调用的执行情况。 跑错之后, onRejected 回调函数之前的所有代码, 都无法被执行, 参考下例:
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
reject('notok1')
},1000)
})
p.then(res=> {
console.log(res)
}).then(res=> [
console.log(res)
]).then(res=> {
console.log(res)
},e=> {
console.log(e)
return 'caught error'
}).then(res=>{
console.log('success :' , res)
},e=>console.log(e))
// 输出
// notok1
// success : caught error
实现代码如下
// promise 的三种状态, 等待、完成(成功)、拒绝(失败)
// 也可以根据promise的状态划分成 已决和未决两种状态, 已决 : FULFILLED、REJECTED, 未决: PENDING
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
const isPromise = p => typeof p.then === "function"
// 解析 x 与 promise2 , 最终确定 promise2 的状态
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) return reject(new TypeError('循环调用了同一个未决的 promise'))
// 定义一个哨兵变量, 防止 promise 的状态在已决后发生改变
let isCalled
if (typeof x === 'object' && x !== null || typeof x === 'function') {
try {
// then = x.then 将初次调用 x.then 时,取到的值缓存
// 而多次调用 x.then 可能发生属性读取错误( 虽然一般不会)
// 所以此处直接调用 then.call, 而不是再次去读取 x.then
const then = x.then
// 可以判断 x 为一个promise, 即 resolve(new Promise()) 这种情况
if ('function' === typeof then) {
// x 是一个 promise, promise2 的状态最终取决有 x 这个 promise 的状态
// 所以要调用属于 x 的 then 函数, 才会进入 onFulfilled 或者 onRjected, 得到下一次链式调用中的回调函数的返回值
then.call(x, y => {
if (isCalled) return
isCalled = true
resolvePromise(promise2, y, resolve, reject)
}, e => {
if (isCalled) return
isCalled = true
reject(e)
})
} else {
// x 不是一个promise , 可能是类似这种有 then 属性的普通对象 x = {then : '100'}
// 直接 resolve, 将状态改成 FULFILLED
resolve(x)
}
} catch (error) {
if (isCalled) return
isCalled = true
reject(error)
}
} else {
// 排除掉 x 为一个 promise 的可能性, 直接resolve(x)
resolve(x)
}
}
// Promise 类
class Promise {
constructor(executor) {
// promise 初始状态为 PENDING
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
// 递归解析 resolve 的参数, 直到参数不为 promise 的实例
if(value instanceof Promise) {
return value.then(resolve,reject)
}
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())
}
}
// 立即执行 executor, 捕获错误后直接变成拒绝态
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
static resolve (value) {
return new Promise(resolve => resolve(value))
}
static reject(reason) {
return new Promise((resolve,reject)=> reject(reason))
}
static all(promises) {
return new Promise((resolve, reject)=> {
let arr = [], index = 0
const processData = (i, data)=> {
arr[i] = data
if(++index >= promises.length) {
resolve(arr)
}
}
for(let i =0 ; i< promises.length; i++) {
if(isPromise(promises[i])) {
promises[i].then(data=> {
processData(i,data)
},reason=> {
reject(reason)
})
}else {
processData(i, promises[i])
}
}
})
}
static race(promises) {
return new Promise((resolve, reject)=> {
for(let i = 0; i < promises.length; i++) {
if(isPromise(promises[i])) {
promises[i].then(resolve,reject)
}else {
resolve(promises[i])
}
}
})
}
// then 函数返回一个新的 Promise 实例, 实现链式调用
// 这个新的 promise 会采用当前 promise 的状态
then(onFulfilled, onRejected) {
// 如果 onFulfilled 不是一个函数, 则将 resolve 的参数传递给下个then 函数的 onFulfilled 作为参数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// 如果 onRjected 不是一个函数, 则将错误抛出, 传递到下一个 then 函数的 onRjected 作为参数
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
const _this = this
const promise2 = new Promise((resolve, reject) => {
// 如果当前实例状态是 FULFILLED, 则调用成功的回调函数, 即 then(onFulfilled, onRejected) 中的 onFulfilled
// 然后将 onFulfilled 的返回值, 继续用 resolvePromise 解析,直到解析到一个不为 promise 的普通值, 或者解析出错
const getFulfilled = () => {
const x = onFulfilled(_this.value)
resolvePromise(promise2, x, resolve, reject)
}
const getRejected = () => {
const x = onRejected(_this.reason)
resolvePromise(promise2, x, resolve, reject)
}
// 此处设置 promise2 的状态, 由于此段代码包含在 promise2 实例化的过程中,因此, 无法获取 promise2 的实例
// 故而将 resolvePromise 函数 (解析 promise2 和 当前实例的 then 函数的回调函数的返回值 )放在一个延时定时器中
// 使得执行 resolvePromise 的任务放在任务队列的最后, 从而确保 先实例化 promise2 成功
// 然后根据当前实例的状态, 得到当前实例回调函数的返回值 x , 并将 x 传入 resolvePromise 进行统一解析处理
const getAsyncTaskResult = f => {
setTimeout(() => {
try {
f()
} catch (error) {
reject(error)
}
}, 0);
}
// 根据当前实例的状态制定不同的策略 :
// 1. 当前实例为 FULFILLED 状态时,则直接调用 onFulfilled , 并将结果交由 resolvePromise 处理
// 2. 当前实例为 REJECTED 状态时,则直接调用 onRejected , 并将结果交由 resolvePromise 处理
// 3.1 当前实例为 PENDING 状态时, 则将 onRjected 和 onFulfilled 的调用缓存在一个任务队列中
// 3.2 然后等待当前实例成功(resolve)或者失败(reject)
// 3.3 由于在构造函数中订阅了 resolve 和 reject, 所以当前实例一旦发布状态, 对应的任务队列将会按照序被调用
const stratagies = {
PENDING() {
_this.onFulfilledCallbacks.push(() => getAsyncTaskResult(getFulfilled))
_this.onRejectedCallbacks.push(() => getAsyncTaskResult(getRejected))
},
FULFILLED() {
getAsyncTaskResult(getFulfilled)
},
REJECTED() {
getAsyncTaskResult(getRejected)
},
}
Reflect.has(stratagies, this.status) && stratagies[this.status]()
})
return promise2
}
// catch 是一个只有失败回调的then函数
catch(errorCallback) {
this.then(null, errorCallback)
}
// finally
// 不管上一个 promise 成功还是失败, finally 的回调函数都会执行
// 不改变 then 函数两个回调函数的参数, 仍然用的是上一次链式调用的最终结果
finally(callback) {
return this.then(value=> {
return Promise.resolve( allback()).then(()=> value)
}, reason=> {
return Promise.resolve(callback()).then(()=> {throw reason})
})
}
}
// 测试代码
// 安装 : npm i promises-aplus-tests -g
// 运行 : promises-aplus-tests filename
Promise.defer = Promise.deferred = function () {
const dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise