乐趣区

关于javascript:从零实现一个Promise

一、Promise/A+ 标准

① Promise 是一个 或者 函数 外部领有 3 个状态 ,别离为pending(期待)fulfilled(执行、实现)rejected(回绝、未实现)
默认为 pending 状态,即 Promise 对象刚创立的时候状态为 pending,并且pending 状态能够转换 fulfilled 或者 rejected
fulfilled 和 rejected 为 最终的状态 一旦变为 fulfilled 或者 rejected,那么将无奈转变为其余状态

② Promise 须要 对外提供一个 then 办法

promise.then(onFulfilled, onRejected)

如果可选参数 onFulfilled 和 onRejected不为函数时应该被疏忽

onFulfilled 和 onRejected 函数都应该是 异步执行 的;

当调用 onFulfilled 函数时,会将以后 Promise 的值作为参数传入,并且 只能调用一次

当调用 onRejected 函数时,会将以后 Promise 的失败起因作为参数传入,并且 只能调用一次

then 函数的返回值依然为 Promise,以便进行链式调用;

resolvePromise
then 办法会创立并返回一个 Promise 对象,then 中注册的回调函数会返回各种值 必须进行校验

then 办法返回的 promise 不能与 then 中回调函数返回值 x 相等,否则 须要抛出谬误

如果是 then 回调函数返回值为一个 非 Promise 对象 ,则间接 用 then 返回的 promise 对象的 resolve 办法 ,resolve(x) 即可。

如果 then 回调函数返回值 x 为一个 Promise 对象 或者一个 带 then 办法的对象或函数 ,那么须要 执行其 then 办法注册回调 拿到 Promise 或类 Promise 对象的值作为 then 返回的 promise 的值 ,如果值依然为 Promise 对象则须要进行 递归操作

二、实现 Promise

① 依据第一条标准,Promise 是一个类或者函数,所以咱们先 将 Promise 定义成一个类 ,同时外部有三个状态,咱们将其 定义为常量

var PENDING = "pending"; // 期待状态
var FULFILLED = "fulfilled"; // 执行、实现状态
var REJECTED = "rejected"; // 回绝、未实现状态
class Promise {constructor() {this.state = PENDING; // Promise 对象创立实现后默认为期待状态}
}

② 咱们在创立 Promise 的时候会 传入一个函数 ,该函数会 在创立 Promise 对象的时候立刻执行 ,并且会 接管两个参数 ,别离用于 执行 回绝 以后 Promise 对象,即 批改以后 Promise 对象的状态 。Promise 是用于解决异步的,所以 在 Promise 状态变为实现的时候可能会接管到异步操作执行的后果 在 Promise 状态变为未实现的时候可能会接管到失败的起因 ,所以 Promise 外部还须要保留 异步操作的后果 value失败的起因 reason

......
class Promise {constructor(executor) { // 传入执行器函数
        ......
        this.value = undefined; // 保留异步操作的后果
        this.reason = undefined; // 保留失败的起因
        const resolve = (value) => {
            this.value = value;
            this.state = FULFILLED; // 将 Promise 对象的状态改为实现状态
        }
        const reject = (reason) => {
            this.reason = reason;
            this.state = REJECTED; // 将 Promise 对象的状态改为未实现状态
        }
        try {executor(resolve, reject); // 执行器由用户传入可能会产生谬误,所以须要进行捕捉
        } catch(e) {reject(e);
        }
    }
}

③ 这里还存在一个问题,就是 Promise 必须是单次执行的,Promise 的状态一旦从 pending 状态批改为 fulfilled 或者 rejected,就不能再发生变化, 从 fulfilled 变为 fulfilled 也不能够 ,也就是说resolve 或者 reject 只能执行一次。所以咱们 须要对 resolve 和 reject 外部进行判断,如果状态曾经变动了则不再执行了,如:

......
class Promise {constructor(executor) { // 传入执行器函数
        ......
        const resolve = (value) => {if (this.state === PENDING) { // 避免用户屡次 resolve,以第一次 resolve 为准
                ......
            }
        }
        const reject = (reason) => {if (this.state === PENDING) { // 避免用户屡次 reject
                ......
            }
        }
        ......
    }
}

④ 给 Promise 增加一个 then 函数,then 函数接管 onFulfilled, onRejected 两个函数作为参数,别离用于解决 Promise 实现时和未实现时的回调函数,如果不是函数,则要进行初始化为一个函数,如:

class Promise {then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => { // 如果 onFulfilled 不是函数,则初始化一个实现处理函数
            return value;
        };
        onRejected = typeof onRejected === "function" ? onRejected : (reason) => { // 如果 onRejected 不是函数,则初始化一个未实现处理函数
            throw reason; // 传什么就抛出什么
        }
    }
}

⑤ then 办法其实就是一个 注册回调的过程 ,当调用 then 的这个 Promise 对象的状态变为实现状态就能够执行 onFulfilled 回调函数,当 Promise 对象的状态变为回绝状态就能够执行 onRejected 回调函数了。所以 回调函数的执行依赖于调用 then 的 Promise 的状态。同时为了反对链式调用,then 办法还须要返回一个 Promise 对象。依据后面的 Promise 标准,传入的回调函数必须异步执行,这里用 setTimeout 进行模仿。

class Promise {then(onFulfilled, onRejected) {
        ......
        let promise;
        switch(this.state) {
            case FULFILLED: // 调用 then 办法的时候,以后 Promise 状态曾经变成实现状态,则可用立刻执行实现的回调函数
                promise = new Promise((resolve, reject) => {setTimeout(() => {
                        try {let x = onFulfilled(this.value);
                        } catch(e) {console.log(e); // 打印错误信息
                            reject(e);
                        }
                    });
                });
                break;
            case REJECTED:
                promise = new Promise((resolve, reject) => {setTimeout(() => {
                        try {let x = onRejected(this.reason);
                        } catch(e) {reject(e);
                        }
                    });
                }
                break;
            case PENDING:
                promise = new Promise((resolve, reject) => {// TODO});
                break;
        }
        return promise;
    }
}

⑥ 当调用 then 的 Promise 对象处于 pending 状态的时候,此时通过 then 注册的回调函数不能立刻执行,必须期待 Promise 的状态变为最终状态能力执行注册的回调函数 。这里就波及到了一个 公布订阅模式 。咱们能够 先将回调函数保存起来 ,那么什么时候 Promise 才会变成最终状态呢? 那就是调用 resolve 或 reject 的时候 ,所以咱们能够 在调用 resolve 或 reject 的时候,取出注册的回调函数而后执行 即可。

class Promise {constructor(executor) {const resolve = (value) => {if (this.state === PENDING) { // 避免用户屡次 resolve,以第一次 resolve 为准
                ......
                this.onFulfilleds.forEach(fn => fn()); // 取出 then 中注册的实现回调函数并执行
            }
        };
        const reject = (reason) => {if (this.state === PENDING) { // 避免用户屡次 reject
                ......
                this.onRejecteds.forEach(fn => fn()); // 取出 then 中注册的回绝回调函数并执行
            }
        };
    }
    then(onFulfilled, onRejected) {
        ......
        switch(this.state) {
            case PENDING:
                promise = new Promise((resolve, reject) => {this.onFulfilleds.push(() => {
                        try {let x = onFulfilled(this.value);
                        } catch(e) {console.log(e); // 打印错误信息
                            reject(e);
                        }
                    });
                    this.onRejecteds.push(() => {
                        try {let x = onRejected(this.reason);
                        } catch(e) {reject(e);
                        }
                    });
                });
                break;
        }
    }
}

⑦ 接下来就是要 解决 then 注册的回调函数的返回值 了,因为回调函数的返回值可能是各种各样的状况,可能是一般的值,可能是 Promise 对象,也可能是带 then 办法的对象,所以咱们要一一进行解决。这里咱们应用一个独自的办法 resolvePromise()进行各种状况的解决,如:

// 传入 then()办法中创立的 Promise 对象,回调函数的返回值 x,then()办法中创立的 Promise 的 resolve、reject
const resolvePromise = function(promise, x, resolve, reject) {// TODO}
class Promise {constructor(executor) { // 传入执行器函数
        ......
    }
    then(onFulfilled, onRejected) {
        case FULFILLED:
            promise = new Promise((resolve, reject) => {
                ......
                let x = onFulfilled(this.value);
                resolvePromise(promise, x, resolve, reject); // 解决回调函数的返回值
            });
        case REJECTED:
            promise = new Promise((resolve, reject) => {
                ......
                let x = onRejected(this.reason);
                resolvePromise(promise, x, resolve, reject); // 解决回调函数的返回值
            });
        case PENDING:
            this.onFulfilleds.push(() => {let x = onFulfilled(this.value);
                resolvePromise(promise, x, resolve, reject); // 解决回调函数的返回值
            });
            this.onRejecteds.push(() => {let x = onRejected(this.reason);
                resolvePromise(promise, x, resolve, reject); // 解决回调函数的返回值
            });
    }
}

三、实现 resolvePromise

① 如果回调函数返回值与 then()办法中创立的 Promise 对象雷同则抛出谬误,这相当于是本人等本人会进入死循环

let p1 = new Promise((resolve, reject) => {resolve(1);
})
let p2 = p1.then((value) => { // p2 即 then 办法内创立 Promise 对象
    return p2;
});
// 后果抛出谬误,显示 Chaining cycle detected for promise #<Promise>
const resolvePromise = function(promise, x, resolve, reject) {if (promise === x) { // 禁止 resolve 本人
        throw new Error("Chaining cycle detected for promise #<Promise>");
    }
}

② 如果回调函数返回的是一个 Promise 对象 或者 带 then 办法的类 Promise 对象 ,又或者一个函数,因为 函数上也可能有 then 办法 ,那么咱们须要 取出 then 办法并执行 ,对于 Promise 对象而言,then 办法的执行就会注册相应的回调函数 等 Promise 状态变为最终状态后就会执行对应的回调函数 ,回调函数执行后就能够拿到 Promise 对象的 value 值,而后 将该 value 值作为调用 then 办法创立的 Promise 的对象的 value 值

const resolvePromise = function(promise, x, resolve, reject) {
    ......
    if ((x && typeof x === "object") || typeof x === "function") { // 如果是对象或者函数,函数也可能有 then 办法
        let executed;
        try {
            let then = x.then; // 尝试取出 then 办法
            if (typeof then === "function") { // 如果该对象上存在 then 办法,那么是个 Promise 对象或者蕴含 then 办法的对象
                then.call(x, function (y) { // 执行 then 办法,对于真正的 Promise 对象,则会注册回调,等到状态变动后,回调函数会执行,回调中能接管到 Promise 的 value 值
                    if (executed) return;
                    executed = true; // 注册的回调函数只能执行一次
                    resolvePromise(promise, y, resolve, reject); // 返回值还可能是一个 Promise 对象,故须要递归直到变为一般值为止
                }, function (e) {if (executed) return;
                    executed = true;
                    reject(e);
                });
            } else { // 不蕴含 then 办法的一般对象,间接 resolve 即可
                resolve(x);      
            }
        } catch(e) {if (executed) return;
            executed = true;
            reject(e);
        }
    } else {resolve(x);
    }
}

四、实现 catch

catch 能够看做是一个 非凡的 then 办法 其外部会调用 then()办法 ,然而 仅注册回绝的回调函数 ,这也就是 then(onFulfilled, onRejected) 和 then(onFulfilled).catch(onRejected)的区别,如果将 onRejected 写到 then 中,那么 当 then 的 onFulfilled 产生谬误的时候 onRejected 就无奈捕捉到其中的谬误,而写到 catch 中,那么就 相当于是下一个 then()办法 ,故能捕捉到上一个 then() 办法中产生的谬误。

class Promise {catch(onRejected) {return this.then(null, onRejected); // 仅注册回绝的回调函数
    }
}

五、总结

Promise 其实就 是一个类 ,外部有 state、value、reason 等属性,别离用于存储 以后 Promise 的状态 执行胜利后的返回值 执行失败的起因 ,同时外部还提供了 resolve、reject 两个办法, 这两个办法会以参数的模式传递给执行器 ,即 传递到内部 ,以便批改 Promise 的状态。
Promise 还提供了一个then 办法用于注册回调函数,注册回调的时候 与以后 Promise 的状态无关 ,如果是最终状态,则立刻执行,如果是 期待状态 ,则 先保存起来 等到调用 resolve 或 reject 办法的时候再取出回调并执行 。注册的回调函数可能会返回各种各样的值:
如果返回的是一般值,那么间接用 then 返回的 Promise 的 resolve 办法 resolve 即可;
如果返回的是 Promise 对象 或者是 带 then 办法的对象或函数 ,那么须要 调用其 then 办法并注册一个自定义回调用于接管以后 Promise 的值 ,等该 Promise 变为最终状态后会执行回调就能够拿到其 value,最初将其作为 then 返回的 Promise 的 value,即 resolve(x)。
残缺源码如下:

var PENDING = "pending"; // 期待状态
var FULFILLED = "fulfilled"; // 执行、实现状态
var REJECTED = "rejected"; // 回绝、未实现状态
// 传入 then()办法中创立的 Promise 对象,回调函数的返回值 x,then()办法中创立的 Promise 的 resolve、reject
const resolvePromise = function(promise, x, resolve, reject) {if (promise === x) { // 禁止 resolve 本人
        throw new Error("Chaining cycle detected for promise #<Promise>");
    }
    if ((x && typeof x === "object") || typeof x === "function") { // 如果是对象或者函数,函数也可能有 then 办法
        let executed;
        try {
            let then = x.then; // 尝试取出 then 办法
            if (typeof then === "function") { // 如果该对象上存在 then 办法,那么是个 Promise 对象或者蕴含 then 办法的对象
                then.call(x, function (y) { // 执行 then 办法,对于真正的 Promise 对象,则会注册回调,等到状态变动后,回调函数会执行,回调中能接管到 Promise 的 value 值
                    if (executed) return;
                    executed = true; // 注册的回调函数只能执行一次
                    resolvePromise(promise, y, resolve, reject); // 返回值还可能是一个 Promise 对象,故须要递归直到变为一般值为止
                }, function (e) {if (executed) return;
                    executed = true;
                    reject(e);
                });
            } else { // 不蕴含 then 办法的一般对象,间接 resolve 即可
                resolve(x);
            }
        } catch(e) {if (executed) return;
            executed = true;
            reject(e);
        }
    } else {resolve(x);
    }
}
class Promise {constructor(executor) { // 传入执行器函数
        this.state = PENDING; // Promise 对象创立实现后默认为期待状态
        this.value = undefined; // 保留异步操作的后果
        this.reason = undefined; // 保留失败的起因
        this.onFulfilleds = []; // 保留 then 中注册的实现回调函数
        this.onRejecteds = []; // 保留 then 中注册的回绝回调函数
        const resolve = (value) => {if (this.state === PENDING) { // 避免用户屡次 resolve,以第一次 resolve 为准
                this.value = value;
                this.state = FULFILLED; // 将 Promise 对象的状态改为实现状态
                this.onFulfilleds.forEach(fn => fn()); // 取出 then 中注册的实现回调函数并执行
            }
        };
        const reject = (reason) => {if (this.state === PENDING) { // 避免用户屡次 reject
                this.reason = reason;
                this.state = REJECTED; // 将 Promise 对象的状态改为未实现状态
                this.onRejecteds.forEach(fn => fn()); // 取出 then 中注册的回绝回调函数并执行
            }
        };
        try {executor(resolve, reject); // 执行器由用户传入可能会产生谬误,所以须要进行捕捉
        } catch(e) {reject(e);
        }
    }
    then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => { // 如果 onFulfilled 不是函数,则初始化一个实现处理函数
            return value;
        };
        onRejected = typeof onRejected === "function" ? onRejected : (reason) => { // 如果 onRejected 不是函数,则初始化一个未实现处理函数
            throw reason; // 传什么就抛出什么
        }
        let promise;
        switch(this.state) {
            case FULFILLED: // 调用 then 办法的时候,以后 Promise 状态曾经变成实现状态,则可用立刻执行实现的回调函数
                promise = new Promise((resolve, reject) => {setTimeout(() => {
                        try {let x = onFulfilled(this.value);
                            resolvePromise(promise, x, resolve, reject);
                        } catch(e) {console.log(e);
                            reject(e);
                        }
                    });
                });
                break;
             case REJECTED:
                promise = new Promise((resolve, reject) => {setTimeout(() => {
                        try {let x = onRejected(this.reason);
                            resolvePromise(promise, x, resolve, reject);
                        } catch(e) {reject(e);
                        }
                    });
                });
                break;
            case PENDING:
                promise = new Promise((resolve, reject) => {this.onFulfilleds.push(() => {
                        try {let x = onFulfilled(this.value);
                            resolvePromise(promise, x, resolve, reject);
                        } catch(e) {reject(e);
                        }
                    });
                    this.onRejecteds.push(() => {
                        try {let x = onRejected(this.reason);
                            resolvePromise(promise, x, resolve, reject);
                        } catch(e) {reject(e);
                        }
                    });
                });
                break;
            }
        return promise;
    }
    catch(onRejected) {return this.then(null, onRejected); // 仅注册回绝的回调函数
    }
}
退出移动版