Promise详解从使用到手写

570 阅读9分钟

什么是Promise呢?

Promise是一个类,可以翻译成承诺 在通过new创建一个Promise对象时,我们需要传入一个回调函数,我们称之为executor

  • 这个回调函数会被立即执行,并且给传入另外两个回调函数resolve,reject;
  • 当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数
  • 当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数

Promise使用过程,可以划分为三个状态:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝;
    • 当执行executor中的代码时,处于该状态;
  • 已兑现(fulfilled):意味着操作成功完成;
    • 执行了resolve时,处于该状态;
  • 已拒绝(rejected):意味着操作失败;
    • 执行了reject时,处于该状态;

Executor

Executor是在创建Promise时需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数

new Promise((resolve, reject) => {
    console.log("executor代码")
})

通常我们会在Executor中确定我们的Promise状态:

  • 通过resolve 可以兑现(fulfilled)Promise的状态,我们也可以称之为已决议(resolved);
  • 通过reject,可以拒绝(reject)Promise的状态; 在这里需要注意:一旦状态被确定下来,Promise的状态会被锁死,该Promise的状态是不可更改的
  • 在我们调用resole的时候,如果resolve传入的值本身不是一个Promise,那么会将该Promise的状态变成兑现(fulfilled);
  • 在之后我们去调用reject时,已经不会有任何的响应了(并不是这行代码不执行,而是无法改变Promise的状态);

resolve不同值的区别

  1. 如果resolve传入一个普通的值或者对象,那么这个值会作为then回调的参数;
  2. 如果resolve中传入的是另外一个Promise,那么这个新Promise会决定原Promise的状态
  3. 如果resolve中传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,并且根据then方法的结果来决定Promise的状态;

then/catch方法-多次调用

一个Promise的then/catch方法是可以被多次调用的

  • 每次调用我们都可以传入对应的fulfilled或者rejected回调;
  • 当Promise的状态变成fulfilled或rejected的时候,这些回调函数都会被执行;
promise.then(res =>{
    console.log('处理1')
})
promise.then(res =>{
    console.log('处理2')
})
promise.then(res =>{
    console.log('处理3')
})

promise.catch(err =>{
    console.log('处理1')
})
promise.catch(err =>{
    console.log('处理2')
})
promise.catch(err =>{
    console.log('处理3')
})

then方法-返回值

then方法本身是有返回值的,它的返回值是一个Promise,所以我们可以进行链式调用;

  • 当then方法中的回调函数本身在执行的时候,那么它处于pending状态;
  • 当then方法中的回调函数返回一个结果时,那么它处于fulfilled状态,并且会将结果作为resolve的参数;
    • 情况一:返回一个普通的值;
    • 情况二:返回一个Promise;
    • 情况三:返回一个thenable的值;
  • 当then方法抛出一个异常时,那么它处于reject状态

catch方法-返回值

catch方法也是会返回一个Promise对象的,所以catch方法后面我们可以继续调用then方法或者catch方法:

promise.catch(err => {
    console.log(1)
}).catch(err => {
    console.log(2)
}).then(res => {
    console.log(3)
})
//如果promise最初状态是fulfilled,输出结果: 1 3  因为catch传入的回调在执行完后,默认状态依然会是fulfilled的

//如果我们希望后续继续执行catch,那么需要抛出一个异常
promise.catch(err => {
    console.log(1)
    throw new Error('error message')
}).catch(err => {
    console.log(2)
}).then(res => {
    console.log(3)
})
//如果promise最初状态是fulfilled,输出结果 1 2 3

finally方法

finally是在ES9中新增的一个特性:finally方法不接收参数,它表示无论promise状态如何,最终都会执行的代码.

promise.then(res => {
    console.log(1)
}).catch(err => {
    console.log(2)
}).finally(() => {
    console.log(3)
})
//如果promise最初状态是fulfilled,输出结果 1  3

resolve类方法

我们可以使用Promise.resolve将现成内容转成Promise resolve参数形态:

  • 参数是一个普通的值或者对象
  • 参数本身是个Promise
  • 参数是一个thenable
Promise.resolve("luo")
// 等价于
new Promise((resolve)=>resolve("luo"))

reject类方法

我们可以使用Promise.reject将现成内容转成Promise的rejected状态 Promise.reject传入的参数无论是什么形态,都会直接作为reject状态的参数传递到catch的

Promise.reject("luo")
// 等价于
new Promise((resolve,reject)=>reject("luo"))

all类方法

它将多个Promise包裹在一起形成一个新的Promise; 新Promise状态由包裹的所有Promise共同决定:

  • 当所有的Promise状态为fulfilled时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组;
  • 当又一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数;
 let p1 = Promise.resolve("luo1")
 let p2 = Promise.resolve("luo2")
 let p3 = Promise.reject("luo3")
 Promise.all([p1, p2, p3]).then(res => {
     console.log(res)
 }).catch(err => {
     console.log(err)
 })
 //输出 luo3

allSettled方法

  • all方法有一个缺陷:当有其中一个Promise变成reject时,新Promise就会立即变成对应的reject状态.
  • 那么对于已经resolved的,以及依然处于pending状态的Promise,我们是获取不到对应的结果的; 在ES11中,添加了新的API Promise.allSettled
  • 该方法会在所有Promise都有结果(settled),无论fulfilled还是reject 才会有最终的状态;并且这个Promise的结果一定是fulfilled的
 let p1 = Promise.resolve("luo1")
 let p2 = Promise.resolve("luo2")
 let p3 = Promise.reject("luo3")
 Promise.allSettled([p1, p2, p3]).then(res => {
     console.log(res)
 }).catch(err => {
     console.log(err)
 })

image.png

race方法

如果一个Promise有了结果,我们就希望决定最终新Promise的状态,那么可以使用race方法;

  • race是竞赛的意思,表示多个Promise 谁先有结果,那么就使用谁的结果;
let p1 = Promise.resolve("luo1")
let p2 = Promise.resolve("luo2")
let p3 = Promise.reject("luo3")
Promise.race([p1, p2, p3]).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})
// 输出 luo1

any方法

any方法是ES12中新增的方法,和race方法是类似的:

  • any方法会等到一个fulfilled状态,才会决定新Promise的状态;
  • 如果所有Promise都是reject的,那么也会等到所有Promise都变成rejected状态;并且会报一个AggregateError的错误.
let p1 = Promise.resolve("luo1")
let p2 = Promise.resolve("luo2")
let p3 = Promise.reject("luo3")
Promise.any([p1, p2, p3]).then(res => {
   console.log('1',res)
}).catch(err => {
   console.log('2',err)
})

image.png

手写Promise

myPromise constructor实现

    const PROMISE_STATUS_PENDING = "pending"
    const PROMISE_STATUS_FULFILLED = "fulfilled"
    const PROMISE_STATUS_REJECTED = "rejected"
    class myPromise {
      constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.reason = undefined
        const resolve = (value) => {
          if (this.status === PROMISE_STATUS_PENDING) {
            this.status = PROMISE_STATUS_FULFILLED
            this.value = value
            console.log("resolve")
          }
        }
        const reject = (reason) => {
          if (this.status === PROMISE_STATUS_PENDING) {
            this.status = PROMISE_STATUS_REJECTED
            this.reason = reason
            console.log("reject")
          }
        }
        executor(resolve, reject)
      }
    }
    const promise = new myPromise((resolve, reject) => {
      console.log("立即执行")
      resolve("111")
      reject("222")
    })

myPromise then基础版本实现

const PROMISE_STATUS_PENDING = "pending"
const PROMISE_STATUS_FULFILLED = "fulfilled"
const PROMISE_STATUS_REJECTED = "rejected"

function execFunctionWithCatchError(execFn, value, resolve, reject){
    try{
        const result = execFn(value)  //判断result是不是一个promise或者thenable  如果是应该由result决定promise状态
        resolve(result)
    }catch(err){
        reject(err)
    }
}

class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledFns = []
        this.onRejectedFns = []
        const resolve = (value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                //添加微任务
                queueMicrotask(() => {
                    if(this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_FULFILLED
                    this.value = value
                    this.onFulfilledFns.forEach(fn => {
                        fn(this.value)
                    })
                })
            }
        }
        const reject = (reason) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                //添加微任务
                queueMicrotask(() => {
                    if(this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_REJECTED
                    this.reason = reason
                    this.onRejectedFns.forEach(fn => {
                        fn(this.reason)
                    })
                })
            }
        }
        try {
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onFulfilled, onRejected) {
        return new myPromise((resolve, reject) => {
            if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled){
                execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
            }
            if (this.status === PROMISE_STATUS_REJECTED && onRejected){
                execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
            }
    
            if(this.status === PROMISE_STATUS_PENDING){
                //将成功和失败的回调收集到数组中
                this.onFulfilledFns.push(() => {
                    execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
                })
                this.onRejectedFns.push(() => {
                    execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
                })
            }
        })
    }
}
const promise = new myPromise((resolve, reject) => {
    console.log("立即执行")
    // resolve("111")
    // reject("222")
    throw new Error('error')
})
promise.then((res) => {
    console.log("res1", res)
    return 333
}, (err) => {
    console.log("err1", err)
    throw new Error('444')
}).then((res) => {
    console.log("res2", res)
}, (err) => {
    console.log("err2", err)
})

setTimeout(() => {
    promise.then((res) => {
        //console.log("res4", res)
    })
}, 1000);

myPromise catch/finally 实现

const PROMISE_STATUS_PENDING = "pending"
const PROMISE_STATUS_FULFILLED = "fulfilled"
const PROMISE_STATUS_REJECTED = "rejected"

function execFunctionWithCatchError(execFn, value, resolve, reject){
    try{
        const result = execFn(value)  //这边还要判断result是不是一个promise或者thenable  如果是应该由result决定promise状态
        resolve(result)
    }catch(err){
        reject(err)
    }
}

class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledFns = []
        this.onRejectedFns = []
        const resolve = (value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                //添加微任务推迟执行 因为执行resolve或者reject的时候.then方法都还没执行 获取不到传进来的函数
                queueMicrotask(() => {
                    if(this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_FULFILLED
                    this.value = value
                    this.onFulfilledFns.forEach(fn => {
                        fn(this.value)
                    })
                })
            }
        }
        const reject = (reason) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if(this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_REJECTED
                    this.reason = reason
                    this.onRejectedFns.forEach(fn => {
                        fn(this.reason)
                    })
                })
            }
        }
        try {
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onFulfilled, onRejected) {
        /*解决.then不写第二个err函数 直接写.catch 
        导致onRejected是undefined没有被成功收集到onRejectedFns数组里 
        我们应该在第一个then没处理的时候 移交给下一个catch或者有err函数的then*/
        onRejected = onRejected || (err => { throw err })
        /* 同样是解决promise先写.catch后  它之后的.then或者是.finally 
        因为onFulfilled是undefined没有被成功收集到onFulfilledFns数组里  
        没有成功执行自然也没有返回一个myPromise 无法后续调用*/
        onFulfilled = onFulfilled || (value => { return value })
        //这里返回一个myPromise 解决链式调用的问题
        return new myPromise((resolve, reject) => {
            if (this.status === PROMISE_STATUS_FULFILLED){
                execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
            }
            if (this.status === PROMISE_STATUS_REJECTED){
                execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
            }
    
            if(this.status === PROMISE_STATUS_PENDING){
                //将成功和失败的回调收集到数组中
                this.onFulfilledFns.push(() => {
                    execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
                })
                this.onRejectedFns.push(() => {
                    execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
                })
            }
        })
    }
    catch(onRejected){
      return this.then(undefined,onRejected)
    }
    finally(onFinally){
       this.then(onFinally,onFinally)
    }
}
const promise = new myPromise((resolve, reject) => {
    console.log("立即执行")
    resolve("111")
})
promise.then((res) => {
    console.log("res1", res)
    return '222'
}).then((res) => {
    console.log("res2", res)
}).catch(err=>{
    console.log('catch',err);
}).finally((f)=>{
    console.log('finally',f);
})

myPromise 类方法的实现

const PROMISE_STATUS_PENDING = "pending"
const PROMISE_STATUS_FULFILLED = "fulfilled"
const PROMISE_STATUS_REJECTED = "rejected"
function execFunctionWithCatchError(execFn, value, resolve, reject){
    try{
        const result = execFn(value)  //这边还要判断result是不是一个promise或者thenable  如果是应该由result决定promise状态
        resolve(result)
    }catch(err){
        reject(err)
    }
}

class myPromise {
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledFns = []
        this.onRejectedFns = []
        const resolve = (value) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                //添加微任务推迟执行 因为执行resolve或者reject的时候.then方法都还没执行 获取不到传进来的函数
                queueMicrotask(() => {
                    if(this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_FULFILLED
                    this.value = value
                    this.onFulfilledFns.forEach(fn => {
                        fn(this.value)
                    })
                })
            }
        }
        const reject = (reason) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                //添加微任务
                queueMicrotask(() => {
                    if(this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_REJECTED
                    this.reason = reason
                    this.onRejectedFns.forEach(fn => {
                        fn(this.reason)
                    })
                })
            }
        }
        try {
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onFulfilled, onRejected) {
        /*解决.then不写第二个err函数 直接写.catch 
        导致onRejected是undefined没有被成功收集到onRejectedFns数组里 
        我们应该在第一个then没处理的时候 移交给下一个catch或者有err函数的then*/
        onRejected = onRejected || (err => { throw err })
        /* 同样是解决promise先写.catch后  它之后的.then或者是.finally 
        因为onFulfilled是undefined没有被成功收集到onFulfilledFns数组里  
        没有成功执行自然也没有返回一个myPromise 无法后续调用*/
        onFulfilled = onFulfilled || (value => { return value })
        //这里返回一个myPromise 解决链式调用的问题
        return new myPromise((resolve, reject) => {
            if (this.status === PROMISE_STATUS_FULFILLED&&onFulfilled){
                execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
            }
            if (this.status === PROMISE_STATUS_REJECTED&&onRejected){
                execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
            }
            if(this.status === PROMISE_STATUS_PENDING){
                //将成功和失败的回调收集到数组中
                if(onFulfilled)this.onFulfilledFns.push(() => {
                    execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
                })
                if(onRejected)this.onRejectedFns.push(() => {
                    execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
                })
            }
        })
    }
    catch(onRejected){
      return this.then(undefined,onRejected)
    }
    finally(onFinally){
       return this.then(onFinally,onFinally)
    }
    static resolve(value){
        if(typeof(value)==='object'&&typeof(value.then)==='function'&&typeof(value.catch)==='function'){
            return value
        }
        return new myPromise((resolve)=>resolve(value))
    }
    static reject(reason){
        if(typeof(reason)==='object'&&typeof(reason.then)==='function'&&typeof(reason.catch)==='function'){
            return reason
        }
        return new myPromise((resolve,reject)=>reject(reason))
    }
    static all(promises){
        return new myPromise((resolve,reject)=>{
            let value = []
            promises.forEach(promise =>{
                myPromise.resolve(promise).then(res=>{
                    value.push(res)
                    if(promises.length === value.length){
                        resolve(value)
                    }
                },err=>{
                    reject(err)
                })
            })
        })
    }
    static allSettled(promises){
        return new myPromise((resolve,reject) => {
            let value = []
            promises.forEach(promise => {
                myPromise.resolve(promise).then(res=>{
                    value.push({
                        status:'fulfilled',
                        value:res
                    })
                    if(promises.length === value.length){
                        resolve(value)
                    }
                },err=>{
                    value.push({
                        status:'rejected',
                        reason:err
                    })
                    if(promises.length === value.length){
                        resolve(value)
                    }
                })
            })
        })

    }
    static race(promises){
        return new myPromise((resolve,reject)=>{
            promises.forEach(promise =>{
                myPromise.resolve(promise).then(res=>{
                    resolve(res)
                },err=>{
                    resolve(err)
                })
            })
        })
    }
    static any(promises){
        return new myPromise((resolve,reject)=>{
            let reason = []
            promises.forEach(promise=>{
                myPromise.resolve(promise).then(res=>{
                    resolve(res)
                },err=>{
                    reason.push(err)
                    if(reason.length === promises.length){
                        reject(new AggregateError(reason))
                    }
                })
            })
        })
    }
    static retry(fn,times){
        return new myPromise(async (resolve, reject) => {
            let i = 1
            while (times--){
                try{
                    const res = await fn()
                    resolve(res)
                    break
                }catch(error){
                    console.log(`第${i++}次失败`,error);
                    if(!times){
                        reject(error)
                    }
                }
            }
        })
    }
}


function fn(){
    const n = Math.random();
    return new myPromise((resolve, reject) => {
        setTimeout(() => {
            if(n>0.7){
                resolve(n)
            }else{
                reject(n)
            }
        }, 1000);
    })
}
myPromise.retry(fn,3).then(res=>{
    console.log('成功',res);
    return 111
}).catch(err=>{
    console.log('3次失败');
    return 222
}).finally(res=>{
    console.log('finally',res);
})
myPromise.all(['1','2',promise]).then(res=>{
    console.log('all res',res);
}).catch(err=>{
    console.log('all err',err);
})
myPromise.allSettled(['1','2',promise]).then(res=>{
    console.log('allSettled res',res);
}).catch(err=>{
    console.log('allSettled err',err);
})
myPromise.race([promise,'1','2']).then(res=>{
    console.log('race res',res);
}).catch(err=>{
    console.log('race err',err);
})
myPromise.any([promise,promise,promise]).then(res=>{
    console.log('any res',res);
}).catch(err=>{
    console.log('any err',err);
})