likes
comments
collection
share

深入理解JavaScript Promise:从异步问题到实现原理

作者站长头像
站长
· 阅读数 19

前言

      由于JavaScript是单线程的,这就意味着在任何时间点,JacaScript只能执行一个任务。所以这就会存在异步的问题,对于V8来说,它会优先执行同步代码,再执行异步代码。所以我们应该如何处理异步代码呢?接下来在本文中Virtual09将会解析一种解决异步代码的操作——Promise

正文

解读Promise

      我们先来看一个未处理的简单异步

function a(){
        setTimeout(()=>{
            console.log('a is ok');
        },1000)
}

function b(){
    console.log('b is ok');
}

a()
b()

深入理解JavaScript Promise:从异步问题到实现原理 我们在a()中添加一个定时器,让a()先等待一秒再执行,但是b()不需要花时间等待,可以执行完,所以V8会优先把b()执行完之后才会等待a()的一秒等待完再执行。那么我们使用Promise如何解决呢?这里我们需要的是先打印a(),但是a()是会触发异步的,所以我们在a()中创建一个Promise对象,并retrun返回这个实例对象,new Promise((resolve,reject)=>{})实例对象接收一个回调函数,回调函数中接收两个参数,我们需要注意的是,这两个参数其实也是函数,调用不同的参数,效果会不一样。

function a(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log('a is ok');
            resolve()
        },1000)
    })
}

function b(){
    console.log('b is ok');
}
a().then(()=>{
    b()
}).catch(err=>{
    console.log(err);
})

这里我们需要注意的是假设我们不调用resolve,这个.then就走不下去了,至于是为什么,等下再给各位讲解一番。调用resolve就会触发.then,调用reject就会触发.catch用于捕获代码报错。

深入理解JavaScript Promise:从异步问题到实现原理

从实现基本原理再看看Promise

      从实现原理看Promise我们要注意Promise拥有三种状态:pendingfulfilled(resolved)rejected,分别是,当我们实例化一个Promise对象时,会的带一个状态为pending的实例对象,调用resolve时,实例对象的状态就会变成fulfilled(resolved),同理调用reject实例对象的状态就会变成rejected,也是就是说当该对象中的状态没有变更为fulfilled,then接受到的回调是不会触发的,同理该对象中的状态没有变更为rejected,catch接受到的回调也不会触发. Promise 构造函数接受一个执行器函数作为参数,这个函数接收两个参数:resolvereject,它们都是函数。resolve 用于当异步操作成功时调用,reject 则用于操作失败时调用。

class MyPromise{
    //构造函数
    constructor(executor){
        executor(resolve,reject)
    }
}

前面我们说到了Promise有三种状态,并且创建一个实例化对象时,状态为pending,所以我们使用this.state = 'pending',在Promise构造函数中的this是指向实例对象.resolvereject函数调用时可以接受一个参数的,所以我们也需要为其准备参数。我们再来思考一个问题

深入理解JavaScript Promise:从异步问题到实现原理

假设我们这里的resolve不调用,也是说.then里面的回调函数就不会触发,,但是一旦当我们调用了resolve,.then就需要触发回调函数对吧,所以我们必须把.then里面的回调函数存在来,而且.then后面还一个接.then也就是说会有多个回调函数,所以我们用一个数组来存回调函数。当调用resolve时,我们就把数组中的回调函数触发掉。

class MyPromise{
    //构造函数
    constructor(executor){

        this.state = 'pending'
        this.value = undefined //临时保存resolve
        this.reason = undefined // 临时保存reject的参数
        this.onFullfilledCallbacks = [] //用来装then中的回调

        this.onRejectedCallbacks = [] //用来装catch中的回调
        const resolve = (value)=>{
            if(this.state === 'pending'){
                this.state = 'fulfilled'
                this.value = value
                this.onFullfilledCallbacks.forEach(cb=>cb(value))
            }
        }
        const reject = (reason)=>{
            if(this.state === 'pending'){
                this.state = 'rejected'
                this.reason = reason
                this.onRejectedCallbacks.forEach(ct=>ct(reason))
            }
        }
        // 构造函数接受一个形参函数,执行调形参,并给它两个实参resolve和reject
        executor(resolve,reject)
    }
}

现在我们就差.then部分的实现了,其实.then是可以接收两个函数的,接收的第二个函数就是等同于reject.首先我们必须判断一下.then接收的是否是两个函数

onFulfilled = typeof onFulfilled === 'function' 
        ? onFulfilled : value =>value;
        onRejected = typeof onRejected === 'function' 
        ? onRejected :reason =>{throw reason};

对于onFulfilled,我们先判断它是不是一个函数,如果不是它就会被替代成一个简单的箭头函数。而onRejected会直接抛出错误。 现在我们需要对于实例对象的状态进行一个判断,有三种情况,一:实例对象本身状态就是fullfilled,也就是then前面的那个哥们已经执行完了,所以我们需要自己把.then执行完,而且.then本身是一个异步,还是微任务的一个函数,但是模拟微任务难度有点大,我们就使用setTime()来模拟就好了

if(this.state === 'fullfilled'){
                setTimeout(()=>{
                   try {
                        const res = onFulfilled(this.value)
                        resolve(res)
                   } catch (error) {
                        reject(error)
                   }
                })
            }

this.value是因为回调函数会接受参数,所以我们这里也需要接受参数,本身也是说resolve自己也会接受参数,假设resolve自己接收了一个参数,并自己先执行完成了,我们的this.value=value,会把resolve的参数赋值给this.value,所以不用担心this.value为空。同理,如果实例对象的状态是rejected,我们也需要自己去执行掉catch,当实例对象的状态为pending,这个时候我们就需要把回调函数分别存入onFullfilledCallbacks,以及onRejectedCallbacks.

if(this.state === 'pending'){
                this.onFullfilledCallbacks.push((value)=>{
                    setTimeout(()=>{
                        onFulfilled(value)
                    })
                })
                this.onRejectedCallbacks.push((reason)=>{
                    setTimeout(()=>{
                        onRejected(reason)
                    })
                })
            }

这样我们就基本模拟了Promise的实现原理了。 源码:

class MyPromise{
    //构造函数
    constructor(executor){

        this.state = 'pending'
        this.value = undefined //临时保存resolve
        this.reason = undefined // 临时保存reject的参数
        this.onFullfilledCallbacks = [] //用来装then中的回调

        this.onRejectedCallbacks = [] //用来装catch中的回调
        const resolve = (value)=>{
            if(this.state === 'pending'){
                this.state = 'fulfilled'
                this.value = value
                this.onFullfilledCallbacks.forEach(cb=>cb(value))
            }
        }
        const reject = (reason)=>{
            if(this.state === 'pending'){
                this.state = 'rejected'
                this.reason = reason
                this.onRejectedCallbacks.forEach(ct=>ct(reason))
            }
        }
        executor(resolve,reject)

    }

    then(onFulfilled,onRejected){
        onFulfilled = typeof onFulfilled === 'function' 
        ? onFulfilled : value =>value;
        onRejected = typeof onRejected === 'function' 
        ? onRejected :reason =>{throw reason};

      
        return new MyPromise((resolve,reject)=>{
            if(this.state === 'fullfilled'){
                setTimeout(()=>{
                   try {
                        const res = onFulfilled(this.value)
                        resolve(res)
                   } catch (error) {
                        reject(error)
                   }
                })

            if(this.state ==='rejected'){
                setTimeout(()=>{
                    try {
                         const res = onRejected(this.reason)
                         resolve(res)
                    } catch (error) {
                         reject(error)
                    }
                 })
            }

            if(this.state === 'pending'){
                this.onFullfilledCallbacks.push((value)=>{
                    setTimeout(()=>{
                        onFulfilled(value)
                    })
                })
                this.onRejectedCallbacks.push((reason)=>{
                    setTimeout(()=>{
                        onRejected(reason)
                    })
                })
            }
        })
    }
}

那么,本文也到此结束了,感谢大家阅读,若有不足恳请指出!!

转载自:https://juejin.cn/post/7392898777056346153
评论
请登录