200行手写 promise

news/2024/7/19 16:39:16 标签: js, javascript

promise 简化版

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

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

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

于是有下边代码

js">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 两个函数时,再执行相应的回调。

js">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 方法

js">
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 的状态。

js">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 方法拿到结果值,递归调用该方法,

js">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)
    }
}

目前代码如下

js">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 包测试。

js">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)
    }
}

http://www.niftyadmin.cn/n/1110346.html

相关文章

用rsync修复不完整的Linux光盘映像文件(转)

用rsync修复不完整的Linux光盘映像文件(转)[more]当我们下载Linux安装盘的映像文件file.iso时,有时会出现光盘映像下载完成后,与官方提供的验证码不一致;还可能本来是3G大小的文件,下载下来却是2G。如果我们一切从头开始下载&…

SpringMVC源码解析- HandlerAdapter - ModelFactory(转)

ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1. 初始化model 1.1 解析类上使用的sessionAttributres,将获取参数合并到mavContainer中 1.2 执行注解了ModelAttribute…

SDUT_2121数据结构实验之链表六:有序链表的建立 (对建立的单项链表结构进行排序)...

点击打开链接 数据结构实验之链表六:有序链表的建立 Time Limit: 1000MS Memory Limit: 65536KBSubmit Statistic DiscussProblem Description 输入N个无序的整数,建立一个有序链表,链表中的结点按照数值非降序排列,输出该有序链表…

Spring Cloud Eureka(一): 开篇说明及目录汇总

开篇简述 基于Spring Boot 和 Spring Cloud 的微服务应用,本人在工作中已经使用两年有余了,伴随着个人学习计划的实施,希望借助博文的方式,将工作中使用到的技术点体系化的总结出来,以加深个人的知识沉淀。 开篇之前&a…

【面试题】this 指向

一道有意思的面试题,考查 this 指向,如下。 var out 25 var inner {out: 20,func: function () {var out 30return this.out} }; console.log((inner.func, inner.func)()) console.log(inner.func()) console.log((inner.func)()) console.log((inn…

Unix/Linux系统下轻量级Shell工具(转)

Unix/Linux系统下轻量级Shell工具(转)[more]  一,前言 随着互联网的发展,使用Unix/Linux系统的越来越多,而入侵一台Unix/Linux系统也不再是什么难事了。通常,在入侵成功之后,都会留一个或几个后门,以便再…

MySQL千万级数据分区存储及查询优化

作为传统的关系型数据库,MySQL因其体积小、速度快、总体拥有成本低受到中小企业的热捧,但是对于大数据量(百万级以上)的操作显得有些力不从心,这里我结合之前开发的一个web系统来介绍一下MySQL数据库在千万级数据量的情况下如何优化提升查询速…

c语言:用函数的嵌套调用,找4个整数中最大的数。(递推)

用函数的嵌套调用&#xff0c;找4个整数中最大的数。&#xff08;递推&#xff09;解&#xff1a;程序&#xff1a;#include<stdio.h>int max2(int a,int b){return (a > b ? a : b);}int max4(int a, int b,int c,int d){int max2(int a, int b);return max2(max2(m…