[JS]源码学习Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、resolved/fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。Promise也有一些缺点

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    // 构造方法接收一个回调
    constructor(executor) {
        this._status = PENDING  // Promise状态
        this._value = undefined // 储存then回调return的值
        this._resolveQueue = [] // 成功队列, resolve时触发
        this._rejectQueue = []  // 失败队列, reject时触发

        // 由于resolve/reject是在executor内部被调用, 因此需要使用箭头函数固定this指向, 
        //否则找不到this._resolveQueue
        let _resolve = (val) => {
            //把resolve执行回调的操作封装成一个函数,放进setTimeout里,
            //以兼容executor是同步代码的情况
            const run = () => {
                if (this._status !== PENDING) return  // 状态只能由pending到fulfilled或rejected
                this._status = FULFILLED              // 变更状态
                this._value = val                     // 储存当前value

                // 这里之所以使用一个队列来储存回调,是为了实现"then方法可以被同一个promise调用多次"
                // 如果使用一个变量而非队列来储存回调,那么即使多次p1.then()也只会执行一次回调
                while (this._resolveQueue.length) {
                    const callback = this._resolveQueue.shift()
                    callback(val)
                }
            }
            setTimeout(run)
        }
        // 实现同resolve
        let _reject = (val) => {
            const run = () => {
                if (this._status !== PENDING) return  // 状态只能由pending到fulfilled或rejected
                this._status = REJECTED               // 变更状态
                this._value = val                     // 储存当前value
                while (this._rejectQueue.length) {
                    const callback = this._rejectQueue.shift()
                    callback(val)
                }
            }
            setTimeout(run)
        }
        // new Promise()时立即执行executor,并传入resolve和reject
        executor(_resolve, _reject)
    }

    // then方法,接收一个成功的回调和一个失败的回调
    then(resolveFn, rejectFn) {
        //如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
        typeof resolveFn !== 'function' ? resolveFn = value => value : null
        typeof rejectFn !== 'function' ? rejectFn = reason => {
            throw new Error(reason instanceof Error ? reason.message : reason);
        } : null

        // return一个新的promise
        return new MyPromise((resolve, reject) => {
            // 把resolveFn重新包装一下,再push进resolve执行队列,
            //这是为了能够获取回调的返回值进行分类讨论
            const fulfilledFn = value => {
                try {
                    // 执行第一个(当前的)Promise的成功回调,并获取返回值
                    let x = resolveFn(value)
                    // 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
                    x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
                } catch (error) {
                    reject(error)
                }
            }

            // reject同理
            const rejectedFn = error => {
                try {
                    let x = rejectFn(error)
                    x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
                } catch (error) {
                    reject(error)
                }
            }

            switch (this._status) {
                // 当状态为pending时,把then回调push进resolve/reject执行队列,等待执行
                case PENDING:
                    this._resolveQueue.push(fulfilledFn)
                    this._rejectQueue.push(rejectedFn)
                    break;
                // 当状态已经变为resolve/reject时,直接执行then回调
                case FULFILLED:
                    fulfilledFn(this._value)    // this._value是上一个then回调return的值
                    break;
                case REJECTED:
                    rejectedFn(this._value)
                    break;
            }
        })
    }
}

Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise实例添加状态改变时的回调函数then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

catch、finally对象方法

//catch方法其实就是执行一下then的第二个回调
//catch()方法返回一个Promise,并且处理拒绝的情况。
//它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。
catch(rejectFn) {
    return this.then(undefined, rejectFn)
}

//finally方法
//finally()方法返回一个Promise。
//在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。
//在finally之后,还可以继续then。并且会将值原封不动的传递给后面的then
finally(callback) {
    return this.then(
        //执行回调,并returnvalue传递给后面的then
        value => MyPromise.resolve(callback()).then(() => value),
        reason => MyPromise.resolve(callback()).then(() => { throw reason })  //reject同理
    )
}

resolve、reject、all、race静态方法

//静态的resolve方法
//Promise.resolve(value)方法返回一个以给定值解析后的Promise对象。
//如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法)),
//返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
//否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
static resolve(value) {
    //根据规范, 如果参数是Promise实例, 直接return这个实例
    if (value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
}

//静态的reject方法
//Promise.reject()方法返回一个带有拒绝原因的Promise对象。
static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason))
}

//静态的all方法
//Promise.all(iterable)方法返回一个 Promise 实例,
//此实例在 iterable 参数内所有的 promise 都“完成(resolved)”
//或参数中不包含 promise 时回调完成(resolve);
//如果参数中  promise 有一个失败(rejected),此实例回调失败(reject),
//失败原因的是第一个失败 promise 的结果。
static all(promiseArr) {
    let index = 0
    let result = []
    return new MyPromise((resolve, reject) => {
        promiseArr.forEach((p, i) => {
            //Promise.resolve(p)用于处理传入值不为Promise的情况
            MyPromise.resolve(p).then(
                val => {
                    index++
                    result[i] = val
                    if (index === promiseArr.length) {
                        resolve(result)
                    }
                },
                err => {
                    reject(err)
                }
            )
        })
    })
}

//静态的race方法
//Promise.race(iterable)方法返回一个 promise,
//一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
static race(promiseArr) {
    return new MyPromise((resolve, reject) => {
        //同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态
        for (let p of promiseArr) {
            //Promise.resolve(p)用于处理传入值不为Promise的情况
            MyPromise.resolve(p).then(
                value => {
                    resolve(value)      //注意这个resolve是上边new MyPromise的
                },
                err => {
                    reject(err)
                }
            )
        }
    })
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值