200行手写 promise

promise 简化版

promise 关键是状态管理,对应三个状态,等待、成功、失败。

实例化 promise 示例时传入一个函数,这个函数会直接在主线程中执行,该函数会提供两个参数:resolve、reject,也是函数,作用是改变实例化的这个 promise 的状态,resolve 把状态改变成成功,reject 改变成失败。

当存在异步情况时,可以通过这两个函数改变状态,后续的 then 方法会根据 promise 的状态来执行。两个函数都可以传入一个参数,分别作为成功结果和失败原因。

于是有下边代码

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class Promise {
    constructor(executor) {
        this.state = PENDING
        this.value = undefined
        this.reason = undefined

        let resolve = value => {
            this.state = FULFILLED
        }
        let reject = reason => {
            this.state = REJECTED
        }

        try {
            executor(resolve)
        } catch (error) {
            reject(error)
        }
    }
}

promise 实例有一个 then 方法,方法里传入两个回调函数: onFulfill, onReject,分别是该 promise 状态变为成功、失败后触发的函数。

调用 then 方法时该 promise 实例对应上边三个状态中的一个,状态是成功时直接调用 onFulfill(会放到微任务里,不在主线程),失败时调用 onReject,等待时则把回调存下来,当 promise 状态改变,即调用 resolve、reject 两个函数时,再执行相应的回调。

class Promise {
    constructor(executor) {
        this.state = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfillCallbacks = []
        this.onRejectCallbacks = []

        let resolve = value => {
            this.state = FULFILLED
            this.value = value
            this.onFulfillCallbacks.forEach(f => f(this.value))
        }
        let reject = reason => {
            this.state = REJECTED
            this.reason = reason
            this.onRejectCallbacks.forEach(f => f(this.reason))
        }

        try {
            executor(resolve)
        } catch (error) {
            reject(error)
        }
    }

    then(onFulfill, onReject) {
        if (this.state == PENDING) {
            this.onFulfillCallbacks.push(value => onFulfill(value))
            this.onRejectCallbacks.push(reason => onReject(reason))
        }
        if (this.state == FULFILLED) {
            onFulfill(this.value)
        }
        if (this.state == REJECTED) {
            onReject(this.reason)
        }
    }
}

现在这个 promise 示例已经有初步状态管理的意思了,但是 promise 是可以链式调用的,也就是说 then 返回的是 promise,修改 then 方法


class Promise {
    // ...
    then(onFulfill, onReject) {
        let p;
        if (this.state == PENDING) {
            p = new Promise((resolve, reject) => {
                this.onFulfillCallbacks.push(value => onFulfill(value))
                this.onRejectCallbacks.push(reason => onReject(reason))
            })
        }
        if (this.state == FULFILLED) {
            p = new Promise((resolve, reject) => {
                onFulfill(this.value)
            })
        }
        if (this.state == REJECTED) {
            p = new Promise((resolve, reject) => {
                onReject(this.reason)
            })
        }
        return p;
    }
}

现在 then 方法是返回 promise了,看似可以链式调用了,但是这个返回的 promise 并没有状态管理,一直是在 pending 状态,怎么改变它的状态就是难点。

返回的 promise 应当在 then 方法里传入的两个参数 onFulfill、onReject执行时改变状态。 把改变它的状态封装成一个函数 resolvePromise,

onFulfill、onReject 返回的值可能是 promise 或者其他类型的值,onFulfill、onReject执行后说明返回的promise 已经相应,就是需要改变它的状态,也就是resolvePromise 需要接受4个参数, onFulfill/onReject 的结果、返回的promise ,resolve,reject。resolve,reject 函数来改变返回的 promise 的状态。

class Promise {
    // ...

    then(onFulfill, onReject) {
        let p;
        if (this.state == PENDING) {
            p = new Promise((resolve, reject) => {
                this.onFulfillCallbacks.push(value => {
                    try {
                        let data = onFulfill(value)
                        resolvePromise(data, p, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                })
                this.onRejectCallbacks.push(reason => {
                    try {
                        let data = onReject(reason)
                        resolvePromise(data, p, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
        }
        if (this.state == FULFILLED) {
            p = new Promise((resolve, reject) => {
                try {
                    let value = onFulfill(this.value)
                    resolvePromise(value, p, resolve, reject);
                } catch (error) {
                    reject(error)
                }
            })
        }
        if (this.state == REJECTED) {
            p = new Promise((resolve, reject) => {
                try {
                    let reason = onReject(this.reason)
                    resolvePromise(reason, p, resolve, reject)
                } catch (error) {
                    reject(error)
                }
            })
        }
        return p;
    }
}

然后是 resolvePromise 的实现,如果 onFulfill/onReject 返回的是正常的值,就调用 resolve 方法改变返回的 promise 的状态,报错了就 调用 reject

如果返回的是 promise,则调用 then 方法拿到结果值,递归调用该方法,

function resolvePromise(result, p, resolve, reject) {
    // 根据规范 onFulfill/onReject 返回的不能是 then 返回的 promise 本身。
    if (result === p) {
        throw new TypeError('cycle')
    }

    if (result && result.then && typeof result.then == 'function') {
        // 结果是一个 promise
        result.then(value => {
            resolvePromise(value, p, resolve, reject)
        }, reason => {
            reject(reason)
        })
    } else {
        resolve(result)
    }
}

目前代码如下

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class Promise {
    constructor(executor) {
        this.state = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfillCallbacks = []
        this.onRejectCallbacks = []

        let resolve = value => {
            this.state = FULFILLED
            this.value = value
            this.onFulfillCallbacks.forEach(f => f(this.value))
        }
        let reject = reason => {
            this.state = REJECTED
            this.reason = reason
            this.onRejectCallbacks.forEach(f => f(this.reason))
        }

        try {
            executor(resolve)
        } catch (error) {
            reject(error)
        }
    }

    then(onFulfill, onReject) {
        let p;
        if (this.state == PENDING) {
            p = new Promise((resolve, reject) => {
                this.onFulfillCallbacks.push(value => {
                    try {
                        let data = onFulfill(value)
                        resolvePromise(data, p, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                })
                this.onRejectCallbacks.push(reason => {
                    try {
                        let data = onReject(reason)
                        resolvePromise(data, p, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
        }
        if (this.state == FULFILLED) {
            p = new Promise((resolve, reject) => {
                try {
                    let value = onFulfill(this.value)
                    resolvePromise(value, p, resolve, reject);
                } catch (error) {
                    reject(error)
                }
            })
        }
        if (this.state == REJECTED) {
            p = new Promise((resolve, reject) => {
                try {
                    let reason = onReject(this.reason)
                    resolvePromise(reason, p, resolve, reject)
                } catch (error) {
                    reject(error)
                }
            })
        }
        return p;
    }
}
function resolvePromise(result, p, resolve, reject) {
    // 根据规范 onFulfill/onReject 返回的不能是 then 返回的 promise 本身。
    if (result === p) {
        throw new TypeError('cycle')
    }

    if (result && result.then && typeof result.then == 'function') {
        // 结果是一个 promise
        result.then(value => {
            resolvePromise(value, p, resolve, reject)
        }, reason => {
            reject(reason)
        })
    } else {
        resolve(result)
    }
}

promise 完整版

这里,基本一个 promise 应有的功能就完成了。这里只是按照思路写了下来,通过 promises-aplus-tests 测试,还有很多不符合规范的地方,比如

  • then 里的两个回调是应放在微任务里,现在可能在主进程执行
  • 没处理 then 传入的参数不是函数的情况
  • 没有考虑 resolve/reject 两个函数重复调用的问题
  • resolvePromise 判断结果值是不是 promise 不准确

完善代码后如下,以下代码经过 promises-aplus-tests 包测试。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 有三种状态 resolve 会把状态改成fulfilled reject 会把状态改成rejected
class Promise {
    constructor(executor) {
        this.state = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onFulfillCallbacks = [];
        this.onRejectCallbacks = []
        let isCalled = false;

        let resolve = (value) => {
            if (isCalled) return;
            isCalled = true;
            this.state = FULFILLED;
            this.value = value
            this.onFulfillCallbacks.forEach(c => {
                if (isFunction(c)) c(this.value);
            })
            // 要执行then方法参数里的成功回调
        }

        let reject = reason => {
            if (isCalled) return;
            isCalled = true;
            this.state = REJECTED
            this.reason = reason
            this.onRejectCallbacks.forEach(c => {
                isFunction(c) && c(reason)
            })
        }

        try {
            executor(resolve, reject)
        } catch (error) {
            reject(error)
        }
    }

    then(onFulfill, onReject) {
        let p;
        // 这两个值不是函数 则忽略 传给下一个then
        if (!isFunction(onFulfill)) {
            onFulfill = r => r
        }
        if (!isFunction(onReject)) {
            onReject = r => {
                // 不是函数的话把错误留给下一个,不能直接返回r 不然状态就是fulfill的了
                throw r
            } 
        }
        // 判断当前状态
        if (this.state == PENDING) {
            // 这里的promise是要返回的 但是then里的回调在this里,不在p
            // 当旧的promise resovle时,执行onFulfill
            p = new Promise((resolve, reject) => {
                // ?onFulfillCallback是刚初始的那个吗 是的
                // 因为知不道之前promise 执行resolve后返回的什么值,所以需要判断一下返回的是promise则需要等待完成,完成后调用这个promise的resolve
                this.onFulfillCallbacks.push(() => {
                    // 调用resolve后 then里的函数是微任务 只能用settimeout宏任务模拟了
                    setTimeout(() => {
                        // 当之前的promise resolve后,会调用这里的方法
                        // then里的onFulfill 传的参数为当前结果值 this.value 在执行到时才确定值
                        try {
                            let result = onFulfill(this.value);
                            resolvePromise(result, p, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    }, 0);
                });
                this.onRejectCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let reason = onReject(this.reason);
                            resolvePromise(reason, p, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
            })
        }
        if (this.state == FULFILLED) {
            p = new Promise((resolve, reject) => {
                // 直接resolve后的then函数也是微任务 延迟执行 不加settimeout 这个函数会在主线程里执行 
                setTimeout(() => {
                    // onFulfill onReject 报错的话 该promise 就变成reject状态
                    try {
                        let result = onFulfill(this.value)
                        resolvePromise(result, p, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0);
            })
        }
        if (this.state == REJECTED) {
            p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let reason = onReject(this.reason);
                        resolvePromise(reason, p, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0);
            })
        }
        return p;
    }

    catch (onRejected) {
        // catch 方法就是then方法没有成功的简写
        return this.then(null, onRejected);
    }
}
Promise.resolve = function(value) {
    return new Promise(resolve => {
        resolve(value);
    })
}
Promise.reject = function(reason) {
    return new Promise((resolve, reject) => {
        reject(reason);
    })
}
Promise.all = function(promises) {
    if (!Array.isArray(promises)) return;
    let currTimes = 0;
    let allTimes = promises.length;
    let results = [];
    return new Promise((resolve, reject) => {
        promises.forEach((p, i) => {
            p.then(data => {
                currTimes++;
                results[i] = data;
                if (currTimes == allTimes) {
                    resolve(results)
                }
            }, reject)
        })
    })
}
Promise.race = function(promises) {
    return new Promise((resolve, reject) => {
        promises.forEach(p => {
            // 不用担心重复调用,因为resolve只会调用一次
            p.then(resolve, reject)
        })
    })
}
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}

function isFunction(p) {
    return typeof p == 'function';
}

/**
 * 拿到上个promise的结果,判断各个可能的类型,然后执行新的promise的resolve或者rejected
 * @param {any} result 上个promise resolve的结果
 * @param {promise} p 新的promise
 * @param {function} resolve 上一个promise完成后 紧接着就调用resolve或者reject
 * @param {function} reject 
 */
function resolvePromise(result, p, resolve, reject) {
    if (result === p) {
        throw new TypeError('recyle promise')
    }

    // then 方法可能是自己创建的对象 里边第一个参数可能执行多次,即onFulfill方法,规范里只能执行一次,需要防止。 如 {then(res) {res();res();}}
    let isCalled = false;
    try {
        // 不能这么写 if (result) { 这样写 其他的基本类型如果有then方法,就会走进去,实际不应该走下去 比如Bollean.prototype.then=y=>y;传入个true会走进if里
        if ((typeof result == 'function' || typeof result == 'object') && result !== null) {
            // 测试里result.then只能get一次
            let then = result.then;
            if (isFunction(then)) {
                // 返回的是promise 就需要拿到该promise的返回值,知道不是promise,promise 完成后会调resolve方法,所以在then里拿到可以拿到结果
                then.call(result, value => {
                    if (isCalled) return;
                    isCalled = true;
                    // 拿到的value可能还是promise 递归调用直到不是promise
                    resolvePromise(value, p, resolve, reject)
                }, reason => {
                    // 失败了就直接reject
                    if (isCalled) return;
                    isCalled = true;
                    reject(reason)
                })
            } else {
                resolve(result)
            }
        } else {
            // 普通值
            resolve(result)
        }
    } catch (error) {
        if (isCalled) return;
        isCalled = true;
        reject(error)
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值