Promise及其API源码的实现思路详解

前端 潘老师 1周前 (04-14) 15 ℃ (0) 扫码查看

Promise是js处理异步操作的重要工具,它有效解决了异步回调地狱的问题。通过Promise,我们能够更清晰、简洁地处理异步任务,实现代码的链式调用。下面,我们将逐步深入探讨Promise及其API源码的实现思考过程。

一、Promise与then方法的实现

Promise有三个状态:pending(进行中)、fulfilled(已完成)和rejected(已失败),并且状态一旦改变,就不会再变动。基于这些特性,我们来实现Promise的基本功能。

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED ='rejected';

class myPromise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        // 存储then方法中成功回调函数的数组,初始注释掉,后续根据需求启用
        // this.onFulfilledCbs = [];
        // 存储then方法中失败回调函数的数组,初始注释掉,后续根据需求启用
        // this.onRejectedCbs = [];

        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value;
                // 当状态变为已完成时,执行存储的成功回调函数,初始注释掉,后续根据需求启用
                // this.onFulfilledCbs.forEach(cb => cb(value));
            }
        };

        const reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;
                // 当状态变为已失败时,执行存储的失败回调函数,初始注释掉,后续根据需求启用
                // this.onRejectedCbs.forEach(cb => cb(reason));
            }
        };

        // 执行传入的executor函数,并传递resolve和reject函数
        executor(resolve, reject);
    }

    then(onFulfilled, onRejected) {
        return new myPromise((resolve, reject) => {
            let result = undefined;
            if (this.status === FULFILLED) {
                result = onFulfilled(this.value);
                resolve(result);
            }
            if (this.status === REJECTED) {
                result = onRejected(this.reason);
                reject(result);
            }
            // 如果当前状态还是pending,说明异步操作还未完成,需要存储回调函数,初始注释掉,后续根据需求启用
            // if (this.status === PENDING) {
            //     this.onFulfilledCbs.push(onFulfilled);
            //     this.onRejectedCbs.push(onRejected);
            // }
        });
    }
}

// 测试示例
new myPromise((resolve, reject) => {
    // setTimeout(() => {
        resolve(111);
    // }, 1000);
}).then(res => {
    console.log('res', res);
});

上述代码初步实现了Promise的链式调用。不过,resolve接收的参数实际上有4种情况,目前代码只处理了基础值这一种。resolve的参数还可能是promisethenable对象或者 ,并且resolve出的值不能是promise自身,否则会导致无限循环引用。为了兼容这些情况,我们进一步完善代码,并使用setTimeout模拟异步操作:

// promise有三种状态,分别是pending、FULFILLED 、rejected,pending是初始状态,FULFILLED 是成功状态,rejected是失败状态
// 状态一旦改变,不会再变
const PENDING = "pending";
const FULFILLED = "FULFILLED ";
const REJECTED = "rejected";

class Promise1 {
    constructor(excutor) {
        // 实际上resolve,reject这两个函数不是外部传来的,外部只是调用内部的方法并传入值
        this.status = PENDING;
        this.value = undefined;
        this.error = undefined;
        // 存储then方法中onFULFILLED的回调函数
        this.onFulfilledCallbacks = [];
        // 存储then方法中onRejected的回调函数
        this.onRejectedCallbacks = [];

        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value;
                this.onFulfilledCallbacks.forEach((callback) => callback());
            }
        };

        const reject = (error) => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.error = error;
                this.onRejectedCallbacks.forEach((callback) => callback());
            }
        };

        try {
            excutor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFulfilled, onRejected) {
        // 处理onFULFILLED不是函数的情况,如果不是函数,就返回传入的值
        onFulfilled = typeof onFulfilled === "function"? onFulfilled : (value) => value;
        // 处理onRejected不是函数的情况,如果不是函数,就抛出错误
        onRejected = typeof onRejected === "function"? onRejected : (error) => {
            throw error;
        };

        // then最终还是会return一个promise
        const promise = new Promise((resolve, reject) => {
            const handleFulfilled = () => {
                try {
                    const result = onFulfilled(this.value);
                    resolvePromise(promise, result, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            };

            const handleRejected = () => {
                try {
                    const result = onRejected(this.error);
                    resolvePromise(promise, result, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            };

            if (this.status === FULFILLED) {
                // 这里使用setTimeout模拟promise是异步的
                setTimeout(handleFulfilled, 0);
            } else if (this.status === REJECTED) {
                setTimeout(handleRejected, 0);
            } else {
                this.onFulfilledCallbacks.push(() => setTimeout(handleFulfilled, 0));
                this.onRejectedCallbacks.push(() => setTimeout(handleRejected, 0));
            }
        });

        return promise;
    }
}

function resolvePromise(promise, result, resolve, reject) {
    // 如果promise和result是同一个对象,则抛出TypeError
    if (promise === result) {
        return reject(new TypeError("Chaining cycle detected for promise"));
    }
    // 如果result是thenable对象
    if (result && (typeof result === "object" || typeof result === "function")) {
        try {
            const then = result.then;
            if (typeof then === "function") {
                // then就是接收两个参数:resolve或reject,然后出结果的函数,直接执行它,但注意this指向,并传参
                then.call(
                    this,
                    (value) => {
                        resolvePromise(promise, value, resolve, reject);
                    },
                    (reason) => reject(reason)
                );
            } else {
                resolve(result);
            }
        } catch (error) {
            reject(error);
        }
    } else {
        // 否则直接调用resolve
        resolve(result);
    }
}

在这段代码中,resolvePromise函数用于处理resolve参数的各种情况,确保代码的健壮性。

二、catch方法的实现

catch方法用于捕获Promise中的错误,它实际上等同于then方法中的第二个参数。因此,我们可以通过调用then方法,并将catch中的回调函数传递给then的第二个参数来实现catch方法:

// 声明在Promise类中
catch(cb) {
    return this.then(_, cb);
}

三、finally方法的实现

finally方法无论Promise成功还是失败都会执行。我们可以利用then方法来实现finally,无论成功或失败,都通过resolve返回一个promise,这样catch后面也能继续进行链式调用:

// 声明在Promise类中
finally(cb) {
    return this.then(
        value => Promise1.resolve(cb()).then(() => value),
        reason => Promise1.resolve(cb()).then(() => reason)
    );
}

四、Promise类方法的实现

(一)resolve方法

Promise.resolve(value) 等同于 new Promise((resolve) => { resolve(value) })。在实现时,我们需要进行判断,如果传入的值本身就是Promise实例,直接返回该实例;否则,创建并返回一个新的Promise实例:

static resolve(value) {
    if (value instanceof Promise1) {
        return value;
    }
    return new Promise1(() => resolve(value));
}

(二)reject方法

reject方法与resolve方法类似,Promise.reject('err') 等同于 new Promise1((_, reject) => reject('err')),实现代码如下:

static reject(reason) {
    return new Promise1((_, reject) => reject(reason));
}

(三)race方法

race方法用于处理多个Promise实例,当其中一个Promise最先执行完毕时,整个操作的结果就是这个最先完成的Promise的状态。它接收一个数组作为参数,我们遍历这个数组,通过Promise.resolve包装每个Promise实例,并在then回调中处理结果:

static race(arr) {
    return new Promise1((resolve, reject) => {
        arr.forEach(pro => {
            Promise1.resolve(pro).then((value) => {
                resolve(value);
            }, reason => {
                reject(reason);
            });
        });
    });
}

(四)all方法

all方法要求所有Promise实例都成功,整个操作才成功;只要有一个失败,结果就是失败。实现时,我们需要计算成功的次数,当成功的个数等于数组长度时,resolve结果数组:

static all(arr) {
    return new Promise1((resolve, reject) => {
        let res = [];
        let completeCount = 0;
        if (arr.length === 0) {
            resolve(res);
            return;
        }
        arr.forEach((pro) => {
            Promise1.resolve(pro).then(
                (value) => {
                    res[completeCount] = value;
                    completeCount++;
                    if (completeCount === arr.length) {
                        resolve(res);
                    }
                },
                (err) => {
                    reject(err);
                }
            );
        });
    });
}

(五)allSettled方法

allSettled方法不管Promise实例是成功还是失败,都会返回结果。结果数组中的每个对象包含status(表示状态,fulfilled为成功,rejected为失败)和对应的结果值。当所有Promise实例都执行完毕后,resolve结果数组:

static allSettled(promises) {
    return new Promise1((resolve, reject) => {
        const results = [];
        let completedCount = 0;
        if (promises.length === 0) {
            resolve(results);
            return;
        }
        promises.forEach((promise, index) => {
            Promise1.resolve(promise).then(
                (value) => {
                    results[index] = {
                        status: "fulfilled",
                        value,
                    };
                    completedCount++;
                    if (completedCount === promises.length) {
                        resolve(results);
                    }
                },
                (reason) => {
                    results[index] = {
                        status: "rejected",
                        reason,
                    };
                    completedCount++;
                    if (completedCount === promises.length) {
                        resolve(results);
                    }
                }
            );
        });
    });
}

最后,给大家留一个作业:实现any方法。这个方法要求只要有一个Promise实例成功,包装实例就返回fulfilled状态;只有当所有Promise实例都失败时,结果才是rejected状态。希望大家通过实践,进一步加深对Promise的理解。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/front/17224.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】