前言

这篇文章咱们一起来学习如何应用Promise,以及如何实现一个本人的Promise,解说十分分明,全程一步一步往后实现,附带具体正文与原理解说。

如果你觉的这篇文章有帮忙到你,❤️关注+点赞❤️激励一下作者,文章公众号首发,关注 前端南玖 第一工夫获取最新的文章,回复进群,拉你进入前端开发交换群,回复材料,获取海量前端电子书及前端学习视频~

promise是什么?次要用来解决什么问题?

Promise是异步编程的一种解决方案,比传统解决方案--回调函数和事件--更正当更弱小。

Promise特点:

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中),fulfilled(已胜利)和reject(已失败)。只有异步操作的后果,能够决定以后是哪一种状态,任何其它操作都无奈扭转这个状态。这也是Promise(承诺)这个名字的由来。

(2)一旦状态扭转,就不会再变,任何时候都能够失去这个后果。Promise对象的状态扭转,只有两种可能:从pending变为fulfilled和从pending变为rejected

promise次要用来解决:

  • 回调天堂
  • 并发申请
  • 异步计划优化(但它自身不是异步的,new Promise()后,它会立刻执行)

promise根本用法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例

上面代码发明了一个Promise实例

const promise = new Promise(function(resolve,reject){    //...    if(/*异步操作胜利*/){       resolve(value)    }else{        //异步操作失败        reject(error)    }})

Promise构造函数承受一个函数作为参数,该函数的两个参数别离是resolvereject.他们是两个函数,由JavaScript引擎提供,不必本人部署。

resolve函数的作用是,将Promise对象的状态从“未实现”变成“胜利”(即从pending变为resolve),在异步操作胜利时调用,并将异步操作的后果,作为参数传递进来;reject函数的作用是,将Promise对象的状态从“未实现”变成“失败”(即从pending变为rejected),在异步操作失败时调用,并将异步操作报出的谬误,作为参数传递进来。

Promise实例生成当前,能够用then办法别离指定resolved状态和rejected状态的回调函数,或用catch办法指定rejected状态的回调函数。

promise.then(res=>{    //success},error=>{    //error}).catch(err=>{})

then办法能够承受两个回调函数作为参数,第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不肯定要提供。这两个函数都接管Promise对象传出的值作为参数。

Ok,通过上面对promise根本用法的形容,咱们大略晓得了一个promise类外面都应该蕴含哪些内容了:

  • promise状态:pending,fulfilled,rejected
  • promise返回值
  • 执行器:promise执行入口(也就是你传入的那个函数)
  • resolve:扭转promise状态为fulfilled
  • reject:扭转promise状态为rejected
  • then:接管两个回调,onFulfilled, onRejected。别离在promise状态变为fulfiled或rejected后执行
  • catch:承受一个回调,在promise状态变为rejected后执行

简略实现一个promise

咱们晓得了一个promise内容至多蕴含以上那些内容,所以一个简略的promise外部至多是这样的

class myPromise {    static PENDING = 'pending'    static FULFILLEd = 'fulfilled'    static REJECTED = 'rejected'    constructor(init){        this.state = myPromise.PENDING // promise状态        this.promiseRes = null  // promise返回值           const resolve = result=>{        //...        }        const reject = result=>{           //...        }        try{            init(resolve,reject)  // init就是初始化执行器        }catch(err){            reject(err)        }            }    then(onFulfilled,onRejected){       //...    }      catch(onRejected){      //...    }}

OK,大略理解之后,咱们再来一个一个的看外面每个局部的实现以及作用

Promise的执行器

它其实是咱们在new Promise时传入的一个回调函数,这个函数自身是同步的,也就是说在new Promise时它就会执行,这也是咱们操作promise的入口。

class myPromise{  //...  constructor(init){        try{            init(resolve,reject)  // init就是初始化执行器        }catch(err){            reject(err) //这里次要是在init执行器函数出错时,用以让promise状态变为rejected        }     }  //...}

该函数承受两个回调函数(resolve,reject)作为参数,用以扭转Promise的状态

resolve与reject办法

这两个函数作为参数传到执行器函数中,用以后续扭转Promise状态

class myPromise {    static PENDING = 'pending'    static FULFILLEd = 'fulfilled'    static REJECTED = 'rejected'    constructor(init){        this.state = myPromise.PENDING // promise状态        this.promiseRes = null  // promise返回值        this.resolveCallback = [] //胜利回调汇合        this.rejectCallback = [] //失败回调汇合        const resolve = result=>{            // 只有当状态为pending时才扭转,保障状态一旦扭转就不会再变            if(this.state === myPromise.PENDING){                this.state = myPromise.FULFILLEd //扭转状态                this.promiseRes = result //返回值                //顺次调用胜利回调                this.resolveCallback.forEach(fn=>fn())            }        }        const reject = result=>{            // 只有当状态为pending时才扭转,保障状态一旦扭转就不会再变            if(this.state === myPromise.PENDING){                this.state = myPromise.REJECTED //扭转状态                this.promiseRes = result //返回值                // 顺次调用失败回调                this.rejectCallback.forEach(fn=>fn())            }        }        try{            init(resolve,reject)  // 留神this指向        }catch(err){            reject(err)        }            }}

初步then办法

class myPromise {    static PENDING = 'pending'    static FULFILLEd = 'fulfilled'    static REJECTED = 'rejected'    constructor(init){        this.state = myPromise.PENDING // promise状态        this.promiseRes = null  // promise返回值        this.resolveCallback = [] //胜利回调汇合        this.rejectCallback = [] //失败回调汇合        const resolve = result=>{            // 只有当状态为pending时才扭转,保障状态一旦扭转就不会再变            if(this.state === myPromise.PENDING){                this.state = myPromise.FULFILLEd //扭转状态                this.promiseRes = result //返回值                //顺次调用胜利回调                this.resolveCallback.forEach(fn=>fn())            }        }        const reject = result=>{            // 只有当状态为pending时才扭转,保障状态一旦扭转就不会再变            if(this.state === myPromise.PENDING){                this.state = myPromise.REJECTED //扭转状态                this.promiseRes = result //返回值                // 顺次调用失败回调                this.rejectCallback.forEach(fn=>fn())            }        }        try{            init(resolve,reject)  // 留神this指向        }catch(err){            reject(err)        }            }    then(onFulfilled,onRejected){        if(this.state === myPromise.FULFILLEd && typeof onFulfilled === 'function') {            onFulfilled(this.promiseRes)        }        if(this.state === myPromise.REJECTED && typeof onRejected === 'function') {            onRejected(this.promiseRes)        }    }}

写到这里,咱们的promise曾经初步成型了,咱们能够来测试一下:

const res1 = new myPromise((res,rej)=>{    res('胜利啦~')    rej('失败啦~')})res1.then((res)=>{    console.log(res)},err=>{    console.log(err)})// 依照预期,这里应该是只会打印出胜利啦~


从上图看咱们,是不是合乎咱们的预期,并且myPromise外部与原生的Promise也是十分类似的。你们是不是感觉这里曾经没问题了,下面咱们只是测了一下同步办法的执行,但别忘了,Promise次要是来解决异步问题的,咱们再来试一下外面执行异步办法还符不合乎咱们的预期?

const res1 = new myPromise((res,rej)=>{    setTimeout(()=>res('胜利啦~'),1000)    // rej('失败啦~')})res1.then((res)=>{    console.log(res)})

这里咱们预期原本是一秒之后打印胜利啦,但它并没有如咱们所愿,反而是什么也没打印进去,这是因为在setTimeout执行之前(pen ding)这个then办法曾经执行过了,1s后状态变成fulfilled时,then也不会再执行了。

所以咱们须要保障then办法的回调函数在promise状态变成fulfilledrejected时再执行,那么当promise状态为pending时咱们先要把回调存在对应的队列中,等后续状态扭转后再执行

较完整then办法

OK,这里咱们批改一下咱们的then办法,让其保障异步代码执行的正确性 (具体实现微工作咱们能够用 mutationObserver,这里咱们就用setTimeout来模仿一下)

class myPromise {    static PENDING = 'pending'    static FULFILLEd = 'fulfilled'    static REJECTED = 'rejected'    constructor(init){        this.state = myPromise.PENDING // promise状态        this.promiseRes = null  // promise返回值        this.resolveCallback = [] //胜利回调汇合        this.rejectCallback = [] //失败回调汇合        const resolve = result=>{            // 只有当状态为pending时才扭转,保障状态一旦扭转就不会再变            if(this.state === myPromise.PENDING){                this.state = myPromise.FULFILLEd //扭转状态                this.promiseRes = result //返回值                //顺次调用胜利回调                this.resolveCallback.forEach(fn=>fn())            }        }        const reject = result=>{            // 只有当状态为pending时才扭转,保障状态一旦扭转就不会再变            if(this.state === myPromise.PENDING){                this.state = myPromise.REJECTED //扭转状态                this.promiseRes = result //返回值                // 顺次调用失败回调                this.rejectCallback.forEach(fn=>fn())            }        }        try{            init(resolve,reject)  // 留神this指向        }catch(err){            reject(err)        }            }    then(onFulfilled,onRejected){        if(this.state === myPromise.FULFILLEd && typeof onFulfilled === 'function') {            onFulfilled(this.promiseRes)        }        if(this.state === myPromise.REJECTED && typeof onRejected === 'function') {            onRejected(this.promiseRes)        }        if(this.state === myPromise.PENDING){            if(onFulfilled && typeof onFulfilled === 'function'){                this.resolveCallback.push(()=>                // 这里咱们用setTimeout来模仿实现then的微工作                setTimeout(()=>{                    onFulfilled(this.promiseRes)                },0)                )            }            if(onRejected && typeof onRejected === 'function'){                this.rejectCallback.push(()=>                // 这里咱们用setTimeout来模仿实现then的微工作                setTimeout(()=>{                    onRejected(this.promiseRes)                },0)                )            }        }    }}

这里咱们能够再测试一下下面那个异步函数的测试用例,发现它可能正确打印,OK,一个较完整的then办法就算实现了~

then的链式调用

then办法会返回一个新的Promise(⚠️留神:不是原来的那个Promise)所以能够采纳链式调用

采纳链式的then,能够指定一组依照秩序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会期待该Promise对象的状态发生变化,才会被调用。

then(onFulfilled,onRejected){        const {promiseRes,state} = this        let promise = new myPromise((reso,reje)=>{            const resolveMyPromise = promiseRes => {                try{                    if(typeof onFulfilled !== 'function'){                        // 如果then的第一个回调不是一个函数,间接疏忽,返回一个新的promise                        reso(promiseRes)                    }else{                        // 获取第一个回调的执行后果                        const res = onFulfilled(promiseRes)                        // 看该执行后果是否是一个promise                        if(res instanceof myPromise){                            // 是一个promise,等它状态扭转后再扭转then返回的promise状态                            res.then(reso,rej)                         }else{                            // 不是一个promise,将它作为新的promise的resolve                            reso(res)                        }                    }                }catch(err){                    //异样,间接将新的promise状态置为rejected                    reje(err)                }            }            const rejectMyPromise = promiseRes => {                try{                    if(typeof onRejected !== 'function'){                        // 如果then的第二个回调不是一个函数,间接疏忽,返回一个新的promise                        reje(promiseRes)                    }else{                        // 获取第二个回调的执行后果                        const res = onRejected(promiseRes)                        // 看该执行后果是否是一个promise                        if(res instanceof myPromise){                            // 是一个promise,等它状态扭转后再扭转then返回的promise状态                            res.then(reso,rej)                         }else{                            // 不是一个promise,将它作为新的promise的resolve                            reje(res)                        }                    }                                    }catch(err){                    //异样,间接将新的promise状态置为rejected                    reje(err)                }            }            if(state === myPromise.FULFILLEd) {                resolveMyPromise(promiseRes)            }            if(state === myPromise.REJECTED) {                rejectMyPromise(promiseRes)            }            if(state === myPromise.PENDING){                if(onFulfilled && typeof onFulfilled === 'function'){                    this.resolveCallback.push(()=>                    // 这里咱们用setTimeout来模仿实现then的微工作                    setTimeout(()=>{                        resolveMyPromise(this.promiseRes)                    },0)                    )                }                if(onRejected && typeof onRejected === 'function'){                    this.rejectCallback.push(()=>                    // 这里咱们用setTimeout来模仿实现then的微工作                    setTimeout(()=>{                        rejectMyPromise(this.promiseRes)                    },0)                    )                }            }        })        return promise    }

catch办法

咱们晓得then的第二个回调其实与catch办法是一样的,所以catch办法咱们能够这样实现

catch(onRejected) {        return this.then(undefined,onRejected)    }

Promise.resolve

将对象转为一个promise对象,依据参数不通可分为四种状况

  • 参数是一个Promise实例,间接返回该实例
  • 参数是一个thenable对象,将该对象转为Promise对象后,执行该对象的then办法
  • 没有参数,也是返回一个状态为resolved的新的Promise对象
  • 参数是一个一个原始值,返回一个新的Promise对象,状态为resolved

手动实现:

static resolve(v){  //1.参数是一个Promise实例,间接返回  if(v instanceof myPromise){    return v  }  //2.参数是一个thenable对象,转为Promise后执行该对象的then办法  if(typeof v === 'object' && typeof v.then === 'function'){    return new myPromise((res,rej)=>{      v.then(res,rej)    })  }  //3.没有参数,间接返回一个resolved状态的promise  if(!v){    return new myPromise(res=>{      res()    })  }  //4.参数是一个原始值,返回一个新的Promise,状态为resolved  return new myPromise(res=>{    res(v)  })}

Promise.reject

返回一个新的Promise对象,状态为rejected

static reject(v){  return new myPromise((res,rej)=>{    rej(v)  })}

Promise.all

该办法用于将多个Promise实例包装成一个新的Promise实例,如果有不是Promise的项,则让该项间接胜利

用法:

const p = Promise.all([p1,p2,p3])

p的状态由p1p2p3决定,分成两种状况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只有p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Ok,理解完Promise.all咱们入手来实现一遍

手动实现:

static all (promises){        return new myPromise((res,rej)=>{            let count = 0            const result = [];            function addFun(index,resf) {                result[index]=resf // 这里用索引别用push,保障返回的程序                count++                if(count==promises.length) {                    res(result)                }            }            [].forEach.call(promises,(promise,index)=>{                if(promise instanceof myPromise) {                    promise.then(success=>{                        // count ++                        // result.push(success)                        addFun(index,success)                    },err=>{                        rej(err)                    })                }else{                    addFun(index,promise)                }            })        })    }

Promise.race

Promise.race()办法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

用法:

const p = Promise.race([p1, p2, p3]);

下面代码中,只有p1p2p3之中有一个实例率先扭转状态,p的状态就跟着扭转。那个率先扭转的 Promise 实例的返回值,就传递给p的回调函数。

手动实现:

static race(promises) {        return new myPromise((res,rej)=>{            [].forEach.call(promises,promise=>{                if(promise instanceof myPromise){                    promise.then(success=>{                        res(success)                    },error=>{                        rej(error)                    })                }else{                    res(promise)                }             })        })    }

残缺代码

class myPromise {    static PENDING = 'pending'    static FULFILLEd = 'fulfilled'    static REJECTED = 'rejected'    constructor(init){        this.state = myPromise.PENDING // promise状态        this.promiseRes = null  // promise返回值        this.resolveCallback = [] //胜利回调汇合        this.rejectCallback = [] //失败回调汇合        const resolve = result=>{            // 只有当状态为pending时才扭转,保障状态一旦扭转就不会再变            if(this.state === myPromise.PENDING){                this.state = myPromise.FULFILLEd //扭转状态                this.promiseRes = result //返回值                //顺次调用胜利回调                this.resolveCallback.forEach(fn=>fn())            }        }        const reject = result=>{            // 只有当状态为pending时才扭转,保障状态一旦扭转就不会再变            if(this.state === myPromise.PENDING){                this.state = myPromise.REJECTED //扭转状态                this.promiseRes = result //返回值                // 顺次调用失败回调                this.rejectCallback.forEach(fn=>fn())            }        }        try{            init(resolve,reject)  // 留神this指向        }catch(err){            reject(err)        }            }    then(onFulfilled,onRejected){        const {promiseRes,state} = this        let promise = new myPromise((reso,reje)=>{            const resolveMyPromise = promiseRes => {                try{                    if(typeof onFulfilled !== 'function'){                        // 如果then的第一个回调不是一个函数,间接疏忽,返回一个新的promise                        reso(promiseRes)                    }else{                        // 获取第一个回调的执行后果                        const res = onFulfilled(promiseRes)                        // 看该执行后果是否是一个promise                        if(res instanceof myPromise){                            // 是一个promise,等它状态扭转后再扭转then返回的promise状态                            res.then(reso,rej)                         }else{                            // 不是一个promise,将它作为新的promise的resolve                            reso(res)                        }                    }                }catch(err){                    //异样,间接将新的promise状态置为rejected                    reje(err)                }            }            const rejectMyPromise = promiseRes => {                try{                    if(typeof onRejected !== 'function'){                        // 如果then的第二个回调不是一个函数,间接疏忽,返回一个新的promise                        reje(promiseRes)                    }else{                        // 获取第二个回调的执行后果                        const res = onRejected(promiseRes)                        // 看该执行后果是否是一个promise                        if(res instanceof myPromise){                            // 是一个promise,等它状态扭转后再扭转then返回的promise状态                            res.then(reso,rej)                         }else{                            // 不是一个promise,将它作为新的promise的resolve                            reje(res)                        }                    }                                    }catch(err){                    //异样,间接将新的promise状态置为rejected                    reje(err)                }            }            if(state === myPromise.FULFILLEd) {                resolveMyPromise(promiseRes)            }            if(state === myPromise.REJECTED) {                rejectMyPromise(promiseRes)            }            if(state === myPromise.PENDING){                if(onFulfilled && typeof onFulfilled === 'function'){                    this.resolveCallback.push(()=>                    // 这里咱们用setTimeout来模仿实现then的微工作                    setTimeout(()=>{                        resolveMyPromise(this.promiseRes)                    },0)                    )                }                if(onRejected && typeof onRejected === 'function'){                    this.rejectCallback.push(()=>                    // 这里咱们用setTimeout来模仿实现then的微工作                    setTimeout(()=>{                        rejectMyPromise(this.promiseRes)                    },0)                    )                }            }        })        return promise    }    catch(onRejected) {        return this.then(undefined,onRejected)    }    static all (promises){        return new myPromise((res,rej)=>{            let count = 0            const result = [];            function addFun(index,resf) {                result[index]=resf // 这里用索引别用push,保障返回的程序                count++                if(count==promises.length) {                    res(result)                }            }            [].forEach.call(promises,(promise,index)=>{                if(promise instanceof myPromise) {                    promise.then(success=>{                        addFun(index,success)                    },err=>{                        rej(err)                    })                }else{                    addFun(index,promise)                }            })        })    }    static race(promises) {        return new myPromise((res,rej)=>{            [].forEach.call(promises,promise=>{                if(promise instanceof myPromise){                    promise.then(success=>{                        res(success)                    },error=>{                        rej(error)                    })                }else{                    res(promise)                }             })        })    }    static resolve(v){        //1.参数是一个Promise实例,间接返回        if(v instanceof myPromise){            return v        }        //2.参数是一个thenable对象,转为Promise后执行该对象的then办法        if(typeof v === 'object' && typeof v.then === 'function'){            return new myPromise((res,rej)=>{                v.then(res,rej)            })        }        //3.没有参数,间接返回一个resolved状态的promise        if(!v){            return new myPromise(res=>{                res()            })        }        //4.参数是一个原始值,返回一个新的Promise,状态为resolved        return new myPromise(res=>{            res(v)        })    }    static reject(v){        return new myPromise((res,rej)=>{            rej(v)        })    }}

举荐浏览

介绍回流与重绘(Reflow & Repaint),以及如何进行优化?
这些浏览器面试题,看看你能答复几个?
这一次带你彻底理解前端本地存储
面试官:说一说前端路由与后端路由的区别
JavaScript之原型与原型链
Javascript深刻之作用域与闭包
this指向与call,apply,bind

总结

OK,下面跟大家一起过了一遍Promise的用法以及本人入手实现了一遍Promise,想必看完这篇文章,大家对Promise会有一个更加清晰的意识。

我是南玖,感激各位的:「点赞和关注」,咱们下期见!