简析Promise的链式调用

799 阅读3分钟

关于Promise

在日常工作中 Promise 可以说是随处可见,和 async/await 配合使用更是如虎添翼,能让代码具有更好的可读性,理解Promise的运行机制对前端来说能帮我们写出更加优雅的代码。

Promise 的用法

const promise = new Promise(function(resolve, reject) {
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});
  1. Promise 的构造函数接受一个函数作为参数,这个函数接受两个参数resolve 和 reject,执行resolve 会将promise 置为fulfilled(成功)状态,reject 会将promise 置为rejected(失败)状态。
  2. then 函数接受两个参数:then(onResolved,onRejected), onResolved 是Promise 成功的回调,onReject 是Promise 失败时的回调。

Promise 对象上不仅有 then 还有catch finally

这几个函数有几个特点

  1. then/catch/finally 均返回一个新的Promise对象,这也就是promise可以链式调用的原因
  2. 前一个then/catch/finally,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数then/catch,就会等待该Promise对象的状态发生变化,才会被调用。
  3. catch 只接受一个参数,等效与 then(undefined,onRejected)
  4. finally 只是负责透传上一个promise的值,除非finally 中的代码报错

接下来我们看几个例子从而加深对这几句话的理解

const promise1 = new Promise(()=>{
	throw new Error('初始化Promise时报错')
})

const promise2 = promise1.then(()=>{
	console.log('then1 触发');
})

const promise3 = promise2.then(()=>{
	console.log('then2 触发')
}).catch((error)=>{
	console.log(error);
})

// 更加常用的写法法是直接连接多个then,这里为了后续说明问题方便采用拆开的方式书写

执行上述代码最终输出

Error: 初始化Promise时报错
    at <anonymous>:2:8
    at new Promise (<anonymous>)
    at <anonymous>:1:1

如果讲上述代码用一张图表示可以表示为下面这张图

实现一个Promise

如何实现一个Promise 就不过多赘述了,可以跟着这边文章手写一遍,能够帮助我们更好的理解Promise的外在表现

9k字 | Promise/async/Generator实现原理解析 - 掘金 (juejin.cn)

这里贴上Promise的实现代码

function microtask(fn) {
    return (...args) => {
        let counter = 1;
        const observer = new MutationObserver(() => {
            fn(...args);
        });
        const textNode = document.createTextNode(String(counter));
        observer.observe(textNode, {
            characterData: true,
        });
        timerFunc = () => {
            counter = (counter + 1) % 2;
            textNode.data = String(counter);
        };
        timerFunc();
    };
}
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
export default class MyPromise {
    _status = PENDING;
    _value = undefined;
    _reason = undefined;
    _resolveQueue = [];
    _rejectQueue = [];
    constructor(executor) {
        // 构造resolve
        const _resolve = microtask((value) => {
            if (this._status !== PENDING) return;
            this._status = FULFILLED;
            this._value = value;
            while (this._resolveQueue.length) {
                const callback = this._resolveQueue.shift();
                callback(value);
            }
        });
        // 构造reject
        const _reject = microtask((reason) => {
            if (this._status !== PENDING) return;
            this._status = REJECTED;
            this._reason = reason;
            while (this._rejectQueue.length) {
                const callback = this._rejectQueue.shift();
                callback(reason);
            }
        });
        try {
            executor(_resolve, _reject);
        } catch (error) {
            console.log('executor err', error);
            _reject(error);
        }
    }
    then(onFulfilled, onRejected) {
        // 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
        if (typeof onFulfilled !== 'function') {
            onFulfilled = (value) => value;
        }
        // 需要把reject 透传下去
        if (typeof onRejected !== 'function') {
            onRejected = (reason) => {
                throw new Error(
                    reason instanceof Error ? reason.message : reason
                );
            };
        }

        return new MyPromise((resolve, reject) => {
            const createCallback = (callback, next) => (value) => {
                try {
                    const result = callback(value);
                    if (result instanceof MyPromise) {
                        result.then(next, reject);
                    } else {
                        debugger;
                        next(result);
                    }
                } catch (error) {
                    debugger;
                    reject(error);
                }
            };
            const resolveFn = createCallback(onFulfilled, resolve);
            const rejectFn = createCallback(onRejected, reject);
            switch (this._status) {
                case PENDING:
                    this._resolveQueue.push(resolveFn);
                    this._rejectQueue.push(rejectFn);
                    break;
                case FULFILLED:
                    resolveFn(this._value);
                    break;
                case REJECTED:
                    rejectFn(this._reason);
                    break;
            }
        });
    }
    catch(onRejected) {
        return this.then(undefined, onRejected);
    }
    finally(callback) {
        const newPromise = this.then(
            (value) => {
                return MyPromise.resolve(callback()).then(
                    () => value,
                    (err) => {
                        throw err;
                    }
                );
            },
            (reason) => {
                debugger;
                return MyPromise.resolve(callback()).then(() => {
                    throw reason;
                });
            }
        );
        return newPromise;
    }
    static resolve(value) {
        if (value instanceof MyPromise) {
            return value;
        }
        return new MyPromise((resolve) => resolve(value));
    }
    static reject(reason) {
        if (reason instanceof MyPromise) {
            return reason;
        }
        return new MyPromise((resolve, reject) => reject(reason));
    }
}

Promise 链式调用的原理

我们简单分析一下promise的链式调用的原理以及状态是如何一步步透传的。

如果将上述代码用一张图表示可以表示为:

Untitled.png

每一次执行then 都会产生一个新的Promise,onFulfilled是promise成功的回调,onRejected 是执行失败的回调,当内部的/前一个Promise的状态发生改变时会通知外部Promise 以此类推,从而实现链式调用并且结果以此向外传递。

在上一个例子中promise1 的error 可以传递到最后一个是因为,onRejected 的默认值是

onRejected = (reason) => {
       throw new Error(
       reason instanceof Error ? reason.message : reason
	 );
 };

所以会将错误一直向外传递直到被捕获。