• 从如何使用到如何实现一个Promise


    前言

    这篇文章我们一起来学习如何使用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)
    })
    // 按照预期,这里应该是只会打印出成功啦~
    

    promise1.png
    从上图看我们,是不是符合我们的预期,并且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)
            })
        }
    }
    

    总结

    OK,上面跟大家一起过了一遍Promise的用法以及自己动手实现了一遍Promise,想必看完这篇文章,大家对Promise会有一个更加清晰的认识。

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

    作者:前端南玖

    -------------------------------------------

    个性签名:智者创造机会,强者把握机会,弱者坐等机会。做一个灵魂有趣的人!

    如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,文章公众号首发,关注 前端南玖 第一时间获取最新的文章~

    欢迎加入前端技术交流群:928029210(QQ)

    扫描下方二维码关注公众号,回复进群,拉你进前端学习交流群(WX),这里有一群志同道合的前端小伙伴,交流技术、生活、内推、面经、摸鱼,这里都有哈,快来加入我们吧~ 回复资料,获取前端大量精选前端电子书及学习视频~

    逐梦wx
  • 相关阅读:
    Python统计nginx日志域名下载量
    如何使用MySQL自动化备份脚本添加备份任务
    迁移数据库报错
    cobbler
    Zabbix添加nginx-php监控
    Zookeeper运维问题集锦
    Jira+Wiki配置手册
    Gitlab安装恢复手册
    Glusterfs配置手册
    k8s的认证-RBAC机制
  • 原文地址:https://www.cnblogs.com/songyao666/p/15783903.html
Copyright © 2020-2023  润新知