promise作为es6中新增的一种异步解决方案,不仅要会使用,还要熟悉它的工作原理,下面将通过代码加注释的方式完整的手写一遍promise源码
// 只关注是如何实现的,故采用js语法
// Promise的三种状态
const PENNDIN = 'PEDDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
// promise的构造函数
const mPromise = function (executor) {
// 保存promise状态
this.PromiseStatus = PENNDIN
// 保存结果
this.PromiseResult = null
// 成功和失败的回调队列
this.fulfilledCallBackList = []
this.rejectedCallBackList = []
// 触发成功的resolve方法,使期约变为成功态
const resolve = (data) => {
if (this.PromiseStatus !== PENNDIN) {
return
}
this.PromiseStatus = FULFILLED
this.PromiseResult = data
// 去触发成功的回调队列
this.fulfilledCallBackList.forEach(fn => fn())
}
// 触发失败的reject方法,使期约变为失败态
const reject = (data) => {
if (this.PromiseStatus !== PENNDIN) {
return
}
this.PromiseStatus = REJECTED
this.PromiseResult = data
// 去触发失败的回调队列
this.rejectedCallBackList.forEach(fn => fn())
}
// 同步尝试执行传入的函数
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
// promise的原生api方法---
/**
* 1、入参为promise对象,则原封不动的返回这个promise
* 2、入参为thenable对象,则返回一个新的promise且跟随这个对象的状态
* 3、其余情况,返回一个成功态的promise且成功的理由为入参
*/
mPromise.resolve = function (val) {
if (val instanceof mPromise) {
return val
}
return new mPromise((resolve, reject) => {
if (val && typeof val === 'bigint' && typeof val.then === 'function') {
setTimeout(() => {
val.then(resolve, reject)
})
} else {
resolve(val)
}
})
}
/**
* 有别于promise.resolve方法,该api只会返回一个失败态的promise,且将入参原封不动作为失败理由
*/
mPromise.reject = function (val) {
return new mPromise((resolve, reject) => {
reject(val)
})
}
/**
* ps:很强大的一个方法,在遇到多接口需同步处理返回数据时很好用
* 一般来讲,接收的参数就是一个数据,数组元素组成可以使promise,也可以是非promise
* 数组中的所有promise都成功时,all返回成功的promise,当有一个失败时整体失败
*/
mPromise.all = function (pmList) {
pmList = [...pmList]
return new mPromise((resolve, reject) => {
let idx = 0
let result = []
if (pmList.length === 0) {
resolve(result)
} else {
const rowValue = (i, val) => {
result[i] = val
idx += 1
if (idx === pmList.length) {
resolve(result)
}
}
for (let i = 0; i < pmList.length; i++) {
mPromise.resolve(pmList[i].then((val) => {
rowValue(i, val)
}, (err) => {
reject(err)
return
}))
}
}
})
}
// 寻找第一个成功的promise,遇到失败则整体失败 若入参为空 则返回一个永远等待的promise
mPromise.race = function (pmList) {
pmList = [...pmList]
return new mPromise((resolve, reject) => {
if (pmList.length === 0) {
return
} else {
for (let i = 0; i < pmList.length; i++) {
mPromise.resolve(pmList[i].then((val) => {
resolve(val)
return
}, (err) => {
reject(err)
return
}))
}
}
})
}
// 判断传入值的一个工具函数类似于
function resolvePromise(newPromise, val, resolve, reject) {
let self = this
// 如果回调返回当前promise则会无限循环下去
if (newPromise === val) {
reject(new TypeError('链循环'))
}
if (val && typeof val === 'object' || typeof val === 'function') {
let isReject = false
try {
let then = val.then
if (typeof then === 'function') {
then.call(val, (v) => {
if (isReject) {
return
}
isReject = true
resolvePromise(newPromise, v, resolve, reject)
}, (e) => {
if (isReject) {
return
}
isReject = true
reject(e)
})
} else {
if (isReject) {
return
}
resolve(val)
}
} catch (error) {
if (isReject) {
return
}
reject(error)
}
}
}
/**
* 当promise状态改变时会执行then传入的回调
* 入参必须是函数类型,且应当是微任务
* onFulfilled:当且仅当promise变为fulfilled时调用一次
* onRejected:当且仅当promise变为rejected时调用一次
* then可以链式调用,故需返回一个新的promise
*/
mPromise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
let self = this
let newPromise = new mPromise((resolve, reject) => {
if (self.PromiseStatus === FULFILLED) {
setTimeout(() => {
try {
let val = onFulfilled(self.PromiseResult)
resolvePromise(newPromise, val, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (self.PromiseStatus === REJECTED) {
setTimeout(() => {
try {
let val = onRejected(self.PromiseResult)
resolvePromise(newPromise, val, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (self.PromiseStatus === PENNDIN) {
self.fulfilledCallBackList.push(() => {
setTimeout(() => {
try {
let val = onFulfilled(self.PromiseResult)
resolvePromise(newPromise, val, resolve, reject)
} catch (error) {
reject(error)
}
})
})
self.rejectedCallBackList.push(() => {
setTimeout(() => {
try {
let val = onRejected(self.PromiseResult)
resolvePromise(newPromise, val, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
})
return newPromise
}
mPromise.prototype.catch = function (onRejected) {
return this.then(null, onRejected)
}
mPromise.prototype.finally = function (callBack) {
return this.then((value) => {
return mPromise.resolve(callBack()).then(() => {
return value
})
}, (err) => {
return mPromise.resolve(callBack()).then(() => {
throw err
})
})
}
modele.exports = mPromise