如何实现一个promise
作者:互联网
如何实现一个Promise
目录1 实现Promise构造函数
Promise自身包含一个状态属性PromiseStaus,一个异步结果属性PromiseResult
状态只在pending、fulfilled、rejected之间枚举,改变状态的两个函数resolve、reject由Promise自身的执行器函数提供(根据A+规范这个executor执行器函数必传否则抛出异常),而Promise的结果由用户提供,且状态一旦从pending更改为其他便不可逆
1.1 关于executor
executor函数在Promise构造函数的执行就立即同步执行。在这个过程中,用户可以调用resolve和reject改变promise实例的状态
状态的改变可以是异步的,比如在定时器的回调中修改,或者在一个网络请求返回后修改,当然也可能是同步的,即直接同步修改
执行过程中一旦抛出异常,Promise内部便会捕获它,同时调用reject将其处理
1.2 Promise构造函数及其作用
Promise构造函数的作用:
- 生成Promise实例
- 向外暴露改变Promise实例状态的方法
- 捕获执行器函数抛出的异常,使用reject将其处理
- 根据1.1,如果状态的修改是异步的,那么需要将then中的回调函数存储在promise实例中,一旦状态改变就立即调用(后续then的实现中会说明)
const PromiseStatusMap = {
pending: 'pending',
fulfilled: 'fulfilled',
rejected: 'rejected'
}
const isPendingStatus = status => status === PromiseStatusMap.pending
const isFunction = target => typeof target === 'function'
const doAsyncTask = callback => {
setTimeout(() => {
callback()
}, 0)
}
function Promise(executor) {
if (typeof executor !== 'function') {
throw 'the first parameter must be a function.'
}
this.PromiseResult = null
this.PromiseStatus = PromiseStatusMap.pending
this.callbacks = []
const onFulfilled = (value) => {
// 状态一但改变不可再次更改
if (!isPendingStatus(this.PromiseStatus)) {
return
}
this.PromiseResult = value
this.PromiseStatus = PromiseStatusMap.fulfilled
doAsyncTask(() => {
this.callbacks.forEach(handleCallback => {
handleCallback()
})
})
}
const onRejected = (reason) => {
// 状态一但改变不可再次更改
if (!isPendingStatus(this.PromiseStatus)) {
return
}
this.PromiseResult = reason
this.PromiseStatus = PromiseStatusMap.rejected
doAsyncTask(() => {
this.callbacks.forEach(handleCallback => {
handleCallback()
})
})
}
try {
// 改变状态的方法由promise提供,方法的参数由用户提供给promise处理
executor(onFulfilled, onRejected)
} catch (error) {
// 一旦抛出异常,状态变为失败
console.warn(error)
onRejected(error)
}
}
2 实现then
2.1 关于then
then是promise原型上的方法,它将Promise成功或者失败的回调提供给用户,同时将promise的结果注入到回调函数的形参中,让用户能在promise得到结果之后去处理接下来的事情
在promise中,then中的回调是异步执行的
2.2 then到底做了什么事情
在promise中,then要做什么事情,取决于promise的状态。根据1.1可知,then回调执行时,promise的状态可能已经改变,也可能尚未改变
- 如果状态的改变是同步的,即then执行的时候promise的状态不为pending,那么then需要根据promise的状态分别执行成功或失败的回调,并将promise结果值注入到回调的形参中去
- 如果状态的修改是异步的,即then执行的时候promise的状态为pending,那么then需要将用户提供的回调存储起来,等待promise的状态改变的时候再执行对应的回调
简而言之,then就做了两件事情。一是在promise拿到结果之后执行对应的成功或失败的回调,二是对外返回一个新的promise
2.3 then的返回值
then一定返回一个新的promise,这个新的promise根据成功或失败回调的返回值callbackResult决定
-
如果用户没有定义成功/失败的回调,那么给一个默认的function,实现then链式传递或异步中断的效果
-
如果callbackResult的返回值是Promise类型,那么then的返回值状态和结果与callbackResult保持一致
-
如果callbackResult的返回值不是Promise类型,则返回一个成功的Promise
-
如果抛出异常,则调用reject,返回一个失败的promise
2.4 实现then
根据以上,为promise的原型添加then方法
Promise.prototype.then = function (onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
// 2.3.1
!isFunction(onFulfilled) && (onFulfilled = () => { })
!isFunction(onRejected) && (onRejected = (reason) => { throw reason })
// 根据promise状态去执行成功/失败的回调,并且注入相应的promise结果
const handleCallback = () => {
try {
const fn = this.PromiseStatus === PromiseStatusMap.rejected ? onRejected : onFulfilled
const callbackResult = fn(this.PromiseResult)
if (callbackResult instanceof Promise) {
callbackResult.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(callbackResult)
}
} catch (error) {
console.warn(error)
reject(error)
}
}
// 2.2.1 如果then执行的时候状态已经改变
if (this.PromiseStatus !== PromiseStatusMap.pending) {
// 2.1 回调的执行是异步的
doAsyncTask(handleCallback)
} else {
// pending状态
// 2.2.2
this.callbacks.push(handleCallback)
}
})
}
3 实现静态方法resolve与reject
Promise.resolve返回一个成功的promise
Promise.reject返回一个失败的promise
如果参数是一个promise,那么采用这个promise的状态与结果
Promise.resolve = function(result){
return new Promise((resolve,reject)=>{
if(result instanceof Promise){
result.then((value)=>{
resolve(value)
},(reason)=>{
console.log('here')
reject(reason)
})
}else{
resolve(result)
}
})
}
Promise.reject = function(result){
return new Promise((resolve,reject)=>{
if(result instanceof Promise){
result.then(value=>{
resolve(value)
},reason=>{
reject(reason)
})
}else{
reject(result)
}
})
}
4 实现catch方法
执行回调的过程中,异常已经被handleCallback中的trycatch捕获并reject处理,因此只需要传递一个失败的回调给then即可
Promise.prototype.catch = function(reason){
return new Promise((undefined,reject)=>{
reject(reason)
})
}
5 实现静态方法all
all用于并行执行异步操作,接受一个可迭代的promises对象作为参数,返回一个新的Promise
只有全部异步操作都成功时成功,有一个异步操作失败就失败
如果成功,则返回的成功promise与传入的promises顺序保持一致
需要注意:
- 输入判断。接受的参数是一个可迭代的类型,并不一定是数组,因此需要先判断入参是否可迭代,并且使用forof迭代entries属性以便同时拿到索引和元素
- 为了保证输入与输出的顺序一致,我们需要根据索引去赋值PromiseResult数组中的元素。但是在判定所有promise都得到结果的时候,我们不应该用.length属性做判断,因为js数组的长度是可以被随意更改指定的,而且我们不确定哪个promise会先完成,也就是说,我们数组长度和完成的异步任务的数量并不一定对等,因此需要创建一个递增的变量count来作这件事情。
- 异常捕获,一旦抛出异常就调用reject将其处理
Promise.all = function (promises) {
if (typeof promises?.[Symbol.iterator] !== 'function') {
throw 'the first parameter must be iterable(need a Symbol.iterator property)'
}
return new Promise((resolve, reject) => {
try {
const result = []
// 根据索引赋值,不能用result.length做判断,因为我们不确定哪个promise会先完成
// arr[2] = 1 我们可能会返回:arr:[empty,empty,1]
let count = 0
for (const [index, promiseItem] of promises.entries()) {
if (promiseItem instanceof Promise) {
promiseItem.then(value => {
result[index] = value
++count === promises.length && resolve(result)
}, reason => {
reject(reason)
})
} else {
result[index] = promiseItem
++count === promises.length && resolve(result)
}
}
} catch (error) {
console.warn(error)
reject(error)
}
})
}
5.1 实现静态方法allSettled
对于all,所有异步任务成功才成功,有一个失败就返回失败,而有时候我们的场景可能是这样的:即使某些任务失败了,也将所有异步任务的结果返回。这就是allSettled的用途
在all的基础上实现allSettled其实非常容易,我们只需将"一旦失败就返回一个失败的promise"这个逻辑改成"不论成功或失败,所有异步任务得到结果才返回一个新的Promise"即可
const judgeResolve = ()=> ++count === promises.length && resolve(result)
if (promiseItem instanceof Promise) {
promiseItem.then(value => {
result[index] = { status: 'fulfilled', value }
judgeResolve()
}, reason => {
result[index] = { status: 'rejected', reason }
judgeResolve()
})
} else {
result[index] = { status: 'fulfilled', value: promiseItem }
judgeResolve()
}
6 实现静态方法race
和all一样,race方法接受一个可迭代的数据作为形参,返回一个新的promise,返回的promise值采用第一个得到结果的promise的result值
Promise.race = function (promises) {
if (typeof promises?.[Symbol.iterator] !== 'function') {
throw 'the first parameter must be iterable(need a Symbol.iterator property)'
}
return new Promise((resolve, reject) => {
try {
for (const promiseItem of promises) {
if (promiseItem instanceof Promise) {
promiseItem.then((value) => {
resolve(value)
}, (reasom) => {
reject(reasom)
})
} else {
resolve(promiseItem)
}
}
} catch (error) {
console.warn(error)
reject(error)
}
})
}
7 实现finally方法
Promise.prototype.finally = function(fn){
return new Promise((resolve,reject)=>{
this.then((value)=>{
resolve(value)
},(reason)=>{
reject(reason)
})
})
}
8 代码汇总
const PromiseStatusMap = {
pending: 'pending',
fulfilled: 'fulfilled',
rejected: 'rejected'
}
const isPendingStatus = status => status === PromiseStatusMap.pending
const isFunction = target => typeof target === 'function'
const doAsyncTask = callback => {
setTimeout(() => {
callback()
}, 0)
}
function Promise(executor) {
if (typeof executor !== 'function') {
throw 'the first parameter must be a function.'
}
this.PromiseResult = undefined
this.PromiseStatus = PromiseStatusMap.pending
this.callbacks = []
const onFulfilled = (value) => {
// 状态一但改变不可再次更改
if (!isPendingStatus(this.PromiseStatus)) {
return
}
this.PromiseResult = value
this.PromiseStatus = PromiseStatusMap.fulfilled
doAsyncTask(() => {
this.callbacks.forEach(handleCallback => {
handleCallback()
})
})
}
const onRejected = (reason) => {
// 状态一但改变不可再次更改
if (!isPendingStatus(this.PromiseStatus)) {
return
}
this.PromiseResult = reason
this.PromiseStatus = PromiseStatusMap.rejected
doAsyncTask(() => {
this.callbacks.forEach(handleCallback => {
handleCallback()
})
})
}
try {
// 改变状态的方法由promise提供,方法的参数由用户提供给promise处理
executor(onFulfilled, onRejected)
} catch (error) {
// 一旦抛出异常,状态变为失败
console.warn(error)
onRejected(error)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
// 2.3.1
!isFunction(onFulfilled) && (onFulfilled = () => { })
!isFunction(onRejected) && (onRejected = (reason) => { throw reason })
// 根据promise状态去执行成功/失败的回调,并且注入相应的promise结果
const handleCallback = () => {
try {
const fn = this.PromiseStatus === PromiseStatusMap.rejected ? onRejected : onFulfilled
const callbackResult = fn(this.PromiseResult)
if (callbackResult instanceof Promise) {
callbackResult.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(callbackResult)
}
} catch (error) {
console.warn(error)
reject(error)
}
}
// 2.2.1 如果then执行的时候状态已经改变
if (this.PromiseStatus !== PromiseStatusMap.pending) {
// 2.1 回调的执行是异步的
doAsyncTask(handleCallback)
} else {
// pending状态
// 2.2.2
this.callbacks.push(handleCallback)
}
})
}
Promise.resolve = function (result) {
return new Promise((resolve, reject) => {
if (result instanceof Promise) {
result.then((value) => {
resolve(value)
}, (reason) => {
reject(reason)
})
} else {
resolve(result)
}
})
}
Promise.reject = function (result) {
return new Promise((resolve, reject) => {
if (result instanceof Promise) {
result.then(value => {
resolve(value)
}, reason => {
reject(reason)
})
} else {
reject(result)
}
})
}
Promise.prototype.catch = function (reason) {
return new Promise((undefined, reject) => {
reject(reason)
})
}
Promise.all = function (promises) {
if (typeof promises?.[Symbol.iterator] !== 'function') {
throw 'the first parameter must be iterable(need a Symbol.iterator property)'
}
return new Promise((resolve, reject) => {
try {
const result = []
// 根据索引赋值,不能用result.length做判断,因为我们不确定哪个promise会先完成
// arr[2] = 1 我们可能会返回:arr:[empty,empty,1]
let count = 0
for (const [index, promiseItem] of promises.entries()) {
if (promiseItem instanceof Promise) {
promiseItem.then(value => {
result[index] = value
++count === promises.length && resolve(result)
}, reason => {
reject(reason)
})
} else {
result[index] = promiseItem
++count === promises.length && resolve(result)
}
}
} catch (error) {
console.warn(error)
reject(error)
}
})
}
Promise.race = function (promises) {
if (typeof promises?.[Symbol.iterator] !== 'function') {
throw 'the first parameter must be iterable(need a Symbol.iterator property)'
}
return new Promise((resolve, reject) => {
try {
for (const promiseItem of promises) {
if (promiseItem instanceof Promise) {
promiseItem.then((value) => {
resolve(value)
}, (reasom) => {
reject(reasom)
})
} else {
resolve(promiseItem)
}
}
} catch (error) {
console.warn(error)
reject(error)
}
})
}
Promise.prototype.finally = function(fn){
return new Promise((resolve,reject)=>{
this.then((value)=>{
resolve(value)
},(reason)=>{
reject(reason)
})
})
}
module.exports = {
Promise
}
标签:resolve,实现,如何,reason,promise,result,reject,Promise 来源: https://www.cnblogs.com/ltfxy/p/16131536.html