promise

术语

  1. thenable 是具有then方法的一个对象
  2. promise 是一个thenable并且遵循PromiseA+规范。代表一个异步操作的最终结果。
  3. value 是promise状态成功(fulfilled)时的结果值,也就是我们调用resolve给到的值。
  4. reason 是promise状态失败(rejected)时的结果值,也就是我们调用reject给到的值。

状态

  1. pending 待处理状态,代表promise执行还未完成。
  2. fulfilled 成功状态,代表promise执行成功,也可以说是resolve调用后的状态。
  3. rejected 失败状态,代表promise执行失败,也可以说是reject调用后的状态。

promise默认是pending状态也就是带处理状态,切promise只能从peding状态转换到fulfilled状态或rejected状态,且promise只能改变一次状态。fulfilled和rejected两个状态不能相互转换。

实现

因为Promise实际是微任务运行,当时浏览器只能通过Promise才能执行微任务,所以我们将以宏任务的方式实现,但整体的promise执行时序还是遵循promise。

状态定义

/**
 * 待处理状态
 * @type {string}
 * @readonly
 */
const PENDING = "pending";
/**
 * 成功状态
 * @type {string}
 * @readonly
 */
const FULFILLED = "fulfilled";
/**
 * 失败状态
 * @type {string}
 * @readonly
 */
const REJECTED = "rejected";

Promise的类结构

/**
 * @class
 */
class Promise {
    /**
     * @param {Function} Promise待执行任务
     * @constructor
     */
    constructor(fn) {
        /**
         * Promise的当前状态
         * @type {string}
         */
        this.status = PENDING;
        /**
         * Promise成功时的结果值
         * @type {any}
         */
        this.value = null;
        /**
         * Promise失败时的结果值
         * @type {any}
         */
        this.reason = null;

        /**
         * 待执行的成功回调(只有状态为fulfilled时才会执行)
         * 至于为什么是一个数组呢?因为通一个promise可以调用多次then方法
         * @type {Function[]}
         */
        this.onFulfilledCallbacks = [];
        /**
         * 待执行的失败回调(只有状态为rejected时才会执行)
         * 数组的原因和onFulfilledCallbacks同理
         * @type {Function[]}
         */
        this.onRejectedCallbacks = [];

        // 成功回调
        const resolve = (value) => {
            ...
        };

        // 失败回调
        const reject = (reason) => {
            ...
        };

        // 执行Promise任务
        try {
            fn(resolve, reject);
        } catch(e) {
            reject(e);
        }
    }

    /**
     * 用来处理promise下一步的逻辑,会根据promise状态执行对应的回调函数
     * @method
     * @param {Function} onFulfilled promise成功时调用的回调
     * @param {Function} onRejected promise失败时调用的回调
     * @return {Promise}
     */
    then(onFulfilled, onRejected) {
        ...
    }
}

resolve和reject的实现以及和then方法的关系

then方法是用来在promies执行完成后获取执行结果,再基于执行结果调用相应的回调函数的方法。并且then方法会返回一个全新的Promise。

问:then方法返回的全新的Promise的状态是基于什么来转换的呢?

答:then方法返回的Promise是基于当前Promise的状态来转换的。如果当前Promise调用的resolve那新创建的Promise也是成功状态,反之亦然。

class Promise {

    constructor() {
        ...

        const resolve = (value) => {
            // 如果resolve的是一个Promise,则需要等待promise执行完成,获取promise的结果返回
            if (value instanceof Promise) {
                value.then(resolve, reject);
                return;
            }

            setTimeout(() => {
                if (this.status === PENDING) {
                    
                    this.status = FULFILLED;
                    this.value = value;

                    for (let callback of this.onFulfilledCallbacks) {
                        callback(value);
                    }

                }
            });
        };

        const reject = (reason) => {
            // reject中如果reason是一个promise,就将promise当作错误原因返回
            setTimeout(() => {
                if (this.status === PENDING) {

                    this.reason = reason;
                    this.status = REJECTED;

                    for (let callback of this.onRejectedCallbacks) {
                        callback(reason);
                    }
                }
            });

        };

        ...
    }

    then(onFulfilled, onRejetect) {
        onFulfilled = typeof onFulfilled !== "function" ? (value) => value : onFulfilled;
        onRejected = typeof onRejected !== "function" ? (err) => { throw err } : onRejected;

        // then方法需要返回一个全新的promise,且全新的promise的状态是依赖当前promise来的
        let promise = new Promise((resolve, reject) => {

            const onFulfilledCallback = (value) => {
                // 用try...catch的原因。因为then方法中给到的代码是用户编写的,所以可能导致出现异常。如果出现异常新创建的promise转换为失败状态
                try {
                    executor(
                        promise,
                        onFulfilled(value),
                        resolve,
                        reject
                    );
                } catch (e) {
                    reject(e);
                }
            };

            const onRejectedCallback = (reason) => {
                try {
                    executor(
                        promise,
                        onRejected(reason),
                        resolve,
                        reject
                    );
                } catch (e) {
                    reject(e);
                }
            };

            if (this.status === PENDING) {
                // 如果当前promise状态是pending,就将其添加到对应的数组,用于等待promise结束后,执行对应的回调
                this.onFulfilledCallbacks.push(onFulfilledCallback);
                this.onRejectedCallbacks.push(onRejectedCallback);
            } else if (this.status === FULFILLED) {
                setTimeout(() => {
                    onFulfilledCallback(this.value);
                });
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    onRejectedCallback(this.reason);
                });
            }
        });

        return promise;

    }
}

module.exports = Promise;

executor实现

then方法中我们调用了executor这个函数,这个函数是干什么的?

是用来处理回调函数返回结果的。用来处理一些特殊情况。

例:嵌套Promise

let promise1 = new Promise((resolve, reject) => {
    let promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2)
        });
    })
    resolve(promise2);
});

这个示例中最终promise1的结果值是2,印务resolve的逻辑通过exector等待了嵌套了的promise2。

例:循环Promise

let promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(promise3);
    });
});

结果是抛出一个异常。

Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

exetor函数的目的就是为了解决类似这样的问题

/**
 * @param {Promise} promise 新创建的Promise
 * @param {any} x 成功或失败回调函数的返回值
 * @param {Function} resolve 新建Promise的resolve
 * @param {Function} reject 新建Promise的reject
 */
function executor(promise, x, resolve, reject) {

    // 如果是循环引用就需要抛出异常
    if (promise === x) {
        throw new TypeError("Chaining cycle detected for promise #<Promise>");
    }

    // 只允许调用一次,因为不能确定第三方实现是否会多次调用,所以需要标识一下代表执行过。
    let called = false;

    // 如果返回值是一个Promise,就等待返回的Promise的结果。
    if (x instanceof Promise) {
        x.then(
            (y) => {
                // 递归处理结果
                executor(promise, y, resolve, reject);
            },
            (err) => {
                reject(err);
            }
        );
    } else if (x && (typeof x === "object" || typeof x === "function")) {
        try {
            var then = x.then;
            if (typeof then === "function") {
                // 如果返回结果是一个thenable可能是其他实现的Promise,尝试调用其他实现的then来等待结果
                then.call(
                    x,
                    (y) => {
                        if (called) {
                            return;
                        }
                        called = true;
                        executor(promise, y, resolve, reject);
                    },
                    (err) => {
                        if (called) {
                            return;
                        }
                        called = true;
                        reject(err);
                    }
                );
            } else {
                // 如果返回结果是一个非thenable,就当作一个结果值
                resolve(x);
            }
        } catch (e) {
            // 如果其他实现出现异常就转换微失败状态
            if (called) {
                return;
            }
            called = true;
            reject(e);
        }
    } else {
        // 不是对象也不是Promise那就是一些基本数据类型,当作结果值返回
        resolve(x);
    }
}

Promise基础功能的完整实现

/**
 * 待处理状态
 * @type {string}
 * @readonly
 */
const PENDING = "pending";
/**
 * 成功状态
 * @type {string}
 * @readonly
 */
const FULFILLED = "fulfilled";
/**
 * 失败状态
 * @type {string}
 * @readonly
 */
const REJECTED = "rejected";

/**
 * @param {Promise} promise 新创建的Promise
 * @param {any} x 成功或失败回调函数的返回值
 * @param {Function} resolve 新建Promise的resolve
 * @param {Function} reject 新建Promise的reject
 */
function executor(promise, x, resolve, reject) {

    // 如果是循环引用就需要抛出异常
    if (promise === x) {
        throw new TypeError("Chaining cycle detected for promise #<Promise>");
    }

    // 只允许调用一次,因为不能确定第三方实现是否会多次调用,所以需要标识一下代表执行过。
    let called = false;

    // 如果返回值是一个Promise,就等待返回的Promise的结果。
    if (x instanceof Promise) {
        x.then(
            (y) => {
                // 递归处理结果
                executor(promise, y, resolve, reject);
            },
            (err) => {
                reject(err);
            }
        );
    } else if (x && (typeof x === "object" || typeof x === "function")) {
        try {
            var then = x.then;
            if (typeof then === "function") {
                // 如果返回结果是一个thenable可能是其他实现的Promise,尝试调用其他实现的then来等待结果
                then.call(
                    x,
                    (y) => {
                        if (called) {
                            return;
                        }
                        called = true;
                        executor(promise, y, resolve, reject);
                    },
                    (err) => {
                        if (called) {
                            return;
                        }
                        called = true;
                        reject(err);
                    }
                );
            } else {
                // 如果返回结果是一个非thenable,就当作一个结果值
                resolve(x);
            }
        } catch (e) {
            // 如果其他实现出现异常就转换微失败状态
            if (called) {
                return;
            }
            called = true;
            reject(e);
        }
    } else {
        // 不是对象也不是Promise那就是一些基本数据类型,当作结果值返回
        resolve(x);
    }
}

/**
 * @class
 */
class Promise {
    /**
     * @param {Function} Promise待执行任务
     * @constructor
     */
    constructor(fn) {
        /**
         * Promise的当前状态
         * @type {string}
         */
        this.status = PENDING;
        /**
         * Promise成功时的结果值
         * @type {any}
         */
        this.value = null;
        /**
         * Promise失败时的结果值
         * @type {any}
         */
        this.reason = null;

        /**
         * 待执行的成功回调(只有状态为fulfilled时才会执行)
         * 至于为什么是一个数组呢?因为通一个promise可以调用多次then方法
         * @type {Function[]}
         */
        this.onFulfilledCallbacks = [];
        /**
         * 待执行的失败回调(只有状态为rejected时才会执行)
         * 数组的原因和onFulfilledCallbacks同理
         * @type {Function[]}
         */
        this.onRejectedCallbacks = [];

        // 成功回调
        const resolve = (value) => {
            // 如果resolve的是一个Promise,则需要等待promise执行完成,获取promise的结果返回
            if (value instanceof Promise) {
                value.then(resolve, reject);
                return;
            }

            setTimeout(() => {
                if (this.status === PENDING) {
                    
                    this.status = FULFILLED;
                    this.value = value;

                    for (let callback of this.onFulfilledCallbacks) {
                        callback(value);
                    }

                }
            });
        };

        // 失败回调
        const reject = (reason) => {
            // reject中如果reason是一个promise,就将promise当作错误原因返回
            setTimeout(() => {
                if (this.status === PENDING) {

                    this.reason = reason;
                    this.status = REJECTED;

                    for (let callback of this.onRejectedCallbacks) {
                        callback(reason);
                    }
                }
            });
        };

        // 执行Promise任务
        try {
            fn(resolve, reject);
        } catch(e) {
            reject(e);
        }
    }

    /**
     * 用来处理promise下一步的逻辑,会根据promise状态执行对应的回调函数
     * @method
     * @param {Function} onFulfilled promise成功时调用的回调
     * @param {Function} onRejected promise失败时调用的回调
     * @return {Promise}
     */
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled !== "function" ? (value) => value : onFulfilled;
        onRejected = typeof onRejected !== "function" ? (err) => { throw err } : onRejected;

        // then方法需要返回一个全新的promise,且全新的promise的状态是依赖当前promise来的
        let promise = new Promise((resolve, reject) => {

            const onFulfilledCallback = (value) => {
                // 用try...catch的原因。因为then方法中给到的代码是用户编写的,所以可能导致出现异常。如果出现异常新创建的promise转换为失败状态
                try {
                    executor(
                        promise,
                        onFulfilled(value),
                        resolve,
                        reject
                    );
                } catch (e) {
                    reject(e);
                }
            };

            const onRejectedCallback = (reason) => {
                try {
                    executor(
                        promise,
                        onRejected(reason),
                        resolve,
                        reject
                    );
                } catch (e) {
                    reject(e);
                }
            };

            if (this.status === PENDING) {
                // 如果当前promise状态是pending,就将其添加到对应的数组,用于等待promise结束后,执行对应的回调
                this.onFulfilledCallbacks.push(onFulfilledCallback);
                this.onRejectedCallbacks.push(onRejectedCallback);
            } else if (this.status === FULFILLED) {
                setTimeout(() => {
                    onFulfilledCallback(this.value);
                });
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    onRejectedCallback(this.reason);
                });
            }
        });

        return promise;
    }
}

module.exports = Promise;

扩展功能

deferred函数实现

有时候Promise的等待结果可能不是在传入的函数中就能够实现的,比如一个模态窗口的确认和取消。

这种情况我们可以将Promise中的resolve和reject函数放到外部来。让后其他地方控制Promise是否完成。这种功能也叫做deffered函数所实现的功能。

class Promise {
    ...
    /**
     * 让外部转换Promise的状态
     * @static
     * @method
     * @return {Promise & { resolve: Function, reject: Function }}
     */
    static deferred() {
        let resolve = null;
        let reject = null;
        
        let promise = new Promise((rev, rej) => {
            resolve = rev;
            reject = rej;
        });
        promise.resolve = resolve;
        promise.reject = reject;
        promise.promise = promise;
        return promise;
    }
}

单元测试

单元测试我们通过 promises-aplus-tests 这个npm包来进行单元测试

安装promises-aplus-tests

yarn add promises-aplus-tests

写下测试脚本到package.json

{
    "scripts": {
        "test": "promises-aplus-tests ./promise.js"
    }
}

运行测试

yarn run test

运行结果最终如下就代表成功了

    ...
      ✓ eventually-rejected
    The value is `1` with `Number.prototype` modified to have a `then` method
      ✓ already-fulfilled
      ✓ immediately-fulfilled
      ✓ eventually-fulfilled
      ✓ already-rejected
      ✓ immediately-rejected
      ✓ eventually-rejected


  872 passing (16s)

参考

[1] Promise A+规范

posted @ 2023-02-17 17:01  八百逗比奔北坡  阅读(81)  评论(0)    收藏  举报