[JS]源码学习Promise

Posted ouyangshima

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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)
                
            )
        
    )

 

以上是关于[JS]源码学习Promise的主要内容,如果未能解决你的问题,请参考以下文章

学习promise

JS-JS模拟实现Promise-源码解析

js执行顺序/Promise优雅解决回调函数嵌套

Promise入门学习

js异步之Promise源码分析

学习Promise实现原理(附源码)