promise源码详解

promise源码详解

promise对象是ES6提出来的,用来解决回调地狱问题的。今天我就来给大家讲解一下如何手写核心的promise代码。

回调地狱

首先我们来了解一下什么是回调地狱,回调地狱指的是,多级异步嵌套调用问题。举个例子:

回调地狱例子
上述图片中,第二次queryData调用会依赖第一次的结果,所以必须在第一次异步回调里面嵌套调用第二次异步方法;以此类推,多层级异步调用嵌套,就会带来回调地狱问题,会造成代码可阅读性很差,后期不好维护等问题。

promise介绍

promise对象就是用来解决回调地狱问题的,可以将异步操作以同步流程的方式表达出来,避免了层层嵌套回调函数的方式。

promise的特点

promise的状态

promise有三个状态,分别是:

  1. pending: 初始状态,不是成功或失败状态。
  2. fulfilled: 意味着操作成功完成。
  3. rejected: 意味着操作失败。

只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

一旦状态改变,就不会再变,状态不可逆,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。

then链式调用

promise.then().then()可以链式调用,因为promise.then()方法会返回一个新的promise,新的promise可以继续执行then方法。

promise的优缺点

promise的优点

promise可以将异步操作以同步的方式表达出来,避免了层层嵌套回调函数的方式。

Promise缺点

首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

class实现手写promise

首先,我们来回顾一下原生的promise是怎么使用的?

原生promise用法
能看出来promise是通过构造函数实例化一个对象的,构造函数里面传入一个fn函数,fn立即执行,初始化状态为pending,fn函数有resolve和reject两个参数,这两个参数也是函数,resolve调用之后pending改变为fulfilled状态,reject调用后pending改为rejected状态。

所以我就用class来实现手写的promise,用静态属性来定义状态,在回调函数resolve和reject中判断,pending状态的时候才可以更改状态,更改状态的同时,将回调函数中成功的结果或者失败的原因都保存在对应的属性result中,方便以后来获取。代码如下:

class实现promise基本结构
为什么会报错呢?因为this指向问题,new一个实例,this指向实例,而实例p上面没有state属性,所以提示undefined。
所以就用bind来改变一下this的指向问题。

this指向修改

异步

测试一下异步操作,发现,输出结果错误,没有异步,还是同步。

异步代码测试
resolve,reject也是放在事件循环的最后执行,所以将then,resove,reject都加上setTimeout方法。代码如下:

setTimeout异步

then实现

当Promise的状态改变之后,不管成功还是失败,都会触发then回调函数。因此,then有两个参数,就是根据状态的不同,来调用不同处理终值的函数。

then
当then里面函数运行时,resolve由于是异步执行的,还没有来得及修改state,此时还是PENDING状态;因此我们需要对异步的情况做一下处理。参考发布订阅模式,在执行then方法的时候,如果还是PENDING状态,就把回调函数存到一个数组中,当状态发生改变时,去数组中取出回调函数依次执行。

then异步实现

注意一下,因为then的两个参数不一定是函数,所以要做一下判断,不是函数的话,赋值一个空函数。

判断是否为函数
另外,因为then支持链式调用,就在return 一个手写的promise就行,就能保证每次返回一个全新的promise,从而实现链式调用。
then实现

小细节

直接抛出Error的处理

new promise 传入的立即执行的函数,也会可能存在异常,因此需要通过try/catch来捕获一下异常情况,否则会直接抛出异常。
try catch

整体核心代码

到这里,手写promise的核心代码大体上完成了。当然实际上原生promise的代码比这个要复杂多了,它还涉及到Promise.all,Promise.race,Promise.prototype.catch等方法。代码如下:

class MyPromise {
    static Pending = "待定";
    static FulFilled = "成功";
    static Rejected = "失败";
    constructor(func) {
        this.state = MyPromise.Pending;
        this.result = null;
        this.fulfilledCallbacks = [];
        this.rejectedCallbacks = [];
        try {
            func(this.resolve.bind(this), this.reject.bind(this))
        } catch (e) {
            this.reject(e)
        }

    }
    resolve (res) {
        setTimeout(() => {
            if (this.state == MyPromise.Pending) {
                this.state = MyPromise.FulFilled;
                this.result = res;
                this.fulfilledCallbacks.forEach(callback => {
                    callback(res)
                })
            }
        })
    }
    reject (reason) {
        setTimeout(() => {
            if (this.state == MyPromise.Pending) {
                this.state = MyPromise.Rejected;
                this.result = reason;
                this.rejectedCallbacks.forEach(callback => {
                    callback(reason)
                })
            }
        })
    }
    then (onFullFilled, onRejected) {
        return new MyPromise((resolve, reject) => {
            onFullFilled = typeof (onFullFilled) == 'function' ? onFullFilled : () => { };
            onRejected = typeof (onRejected) == 'function' ? onRejected : () => { };
            if (this.state == MyPromise.Pending) {
                this.fulfilledCallbacks.push(onFullFilled)
                this.rejectedCallbacks.push(onRejected)
            }
            if (this.state == MyPromise.FulFilled) {
                setTimeout(() => {
                    onFullFilled(this.result)
                })
            }
            if (this.state == MyPromise.Rejected) {
                setTimeout(() => {
                    onRejected(this.result)
                })
            }
        })
    }
}

如果写的有问题的,欢迎大家指正,一起讨论。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值