模拟实现promise

Posted by CodingWithAlice on December 5, 2024

模拟实现promise

明确 Promise 的几个基本信息: 1、状态凝固 默认pending/resolved/rejected,改变一次后无法再变更 2、传入的执行器有两个函数参数 resolve,reject,调用这两个函数触发状态变更 3、执行器是同步执行的 4、then 微任务执行,两个函数入参 onResolved,onRejected - catch语法糖,resolve抛出的值/reject抛出的错误原因是两个函数的参数 5、then 返回的就是Promise,可以链式调用 6、finally 不论返回什么值,只要改变了状态就执行 7、Promise.resolve()\Promise.reject() 可以直接返回状态凝固的Promise 8、Promise.all()/Promise.race()

常见错误:

  • 1、如果不使用 self,在 resolve和reject定义时使用『箭头函数』保存上下文(保存在this上)

image-20241205201152107

image-20241207115858868

image-20241214160634119

定义时注意!promise 的状态改变后不可逆!一定要判断当前状态是 pending 才能切换!!

image-20241221200031090

  • 2、Promise.resolve - 传入的如果是一个 promise,会把状态和值传递下去

    image-20241207115957175

  • 3、Promise.race() - promise 不需要执行

    image-20241207120142997

    image-20241210123040158

  • 4、Promise.then() - 为了链式调用返回的新的promise的this === 原本的this(箭头函数) + pending状态下推入队列的应该是一个可执行的函数

    image-20241210122822660

    image-20241214160919480

  • 5、Promise.finally() - 需要将 value 值传递

    image-20241210122803383

  • 6、Promise.all() - 记得要给 then传递两个函数作为参数 + pre在reduce主体函数中返回

    image-20241221195842755

let Status = {
    resolve: 'fulfilled',
    reject: 'rejected',
    pending: 'pending'
}
function MyPromise(executor) {
    let self = this; // 如果不使用 self,在 resolve和reject定义时使用『箭头函数』保存上下文
    self.status = Status.pending;
    self.value = null;
    self.reason = null;
    self.resolvedCallbacks = []; // 在 resolve 函数中被执行
    self.rejectedCallbacks = [];

    function resolve(value) {
        if (self.status === Status.pending) {
            self.status = Status.resolve;
            self.value = value;
            self.resolvedCallbacks.forEach(it => it(self.value)); // 不用setTimeout,投入该队列的都是 setTimeout 包裹的函数,模拟微任务,在同步任务执行完之后执行
        }
    }
    function reject(reason) {
        if (self.status === Status.pending) {
            self.status = Status.reject;
            self.reason = reason;
            self.rejectedCallbacks.forEach(it => it(self.reason));
        }
    }

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

// 5
MyPromise.prototype.then = function (onResolved, onRejected) {
    // 兼容 then 的两个参数都是函数 - reject-> throw reason
    onResolved = typeof onResolved === 'function' ? onResolved : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };

    // 链式调用
    return new MyPromise((resolve, reject) => {
        // Promise 可以绑定多个 then 方法 
        // -> 状态已改变的promise调用then-立即执行
        // -> 状态未改变的promise调用then-异步执行 - 通过setTimeout来模拟异步调用
        const resolveFake = () => {
            try {
                const res = onResolved(this.value); // 这里的 this 因为 return 时使用箭头函数,this 指向的就是外部的 this
                // 不是执行所有回调,而是把当前传入回调函数执行 - 『注意这里要实时更新的value』
                resolve(res);
            } catch (reason) {
                reject(reason)
            }
        }
        const rejectFake = () => {
            try {
                const res = onRejected(this.reason);
                resolve(res);
            } catch (reason) {
                reject(reason)
            }
        }
        switch (this.status) {
            case Status.resolve:
                setTimeout(resolveFake, 0); // 模拟微任务,在同步任务执行完后执行
                break;
            case Status.reject:
                setTimeout(rejectFake, 0); // 模拟微任务,在同步任务执行完后执行
                break;
            case Status.pending:
                this.resolvedCallbacks.push(() => setTimeout(resolveFake, 0));
                this.rejectedCallbacks.push(() => setTimeout(rejectFake, 0));
                break;
        }
    })
}

// 4
MyPromise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
}

// 6 - 『状态是父期约的传递』,返回值被忽略(『onFinally无参数』)
MyPromise.prototype.finally = function (callback) {
    return this.then(value => {callback(); return value;}, 
                     reason => {callback(); throw reason;}
                    );
}

// 注意:then\catch\finally 定义在 prototype 上,依赖于实例本身的状态,操作单个实例
// resolve\reject\all\race 不依赖于实例,而是工具方法,作为静态方法定义

// 7 - 返回一个『新的』状态已定的期约
MyPromise.resolve = function (value) {
    return new MyPromise((resolve, reject) => {
        if (value instanceof MyPromise) {
            // 传入的如果是一个 promise,会把状态和值传递下去
            value.then(resolve, reject)
        } else {
            resolve(value)
        }
    })
}
MyPromise.reject = function (reason) {
    return new MyPromise((resolve, reject) => reject(reason))
}

// 8 - 返回一个 promise,都resolve则返回结果数组;有一个reject,则返回reject原因
MyPromise.all = function (arr) {
    return new MyPromise((resolve, reject) => {
        arr.reduce((pre, cur, index) => {
            cur.then((res) => {
                pre.push(res);
                index + 1 === arr.length && resolve(pre)
            }, reject);
            return pre;
        }, []);
    });
}

MyPromise.race = function (arr) {
    return new MyPromise((resolve, reject) => {
        arr.forEach(it => MyPromise1.resolve(it).then(resolve, reject))
    });
}
// 测试then方法
let promise1 = new MyPromise((resolve, reject) => {
    resolve(10);
});
promise1.then(value => {
    console.log('then success:', value);
}).catch(reason => {
    console.log('then catch:', reason);
});

// 测试catch方法
let promise2 = new MyPromise((resolve, reject) => {
    reject('error');
});
promise2.catch(reason => {
    console.log('catch:', reason);
});

// 测试finally方法
let promise3 = new MyPromise((resolve, reject) => {
    resolve(20);
});
promise3.finally(() => {
    console.log('finally called');
}).then(value => {
    console.log('finally then success:', value);
});

// 测试all方法
let promise4 = MyPromise.resolve(30);
let promise5 = MyPromise.resolve(40);
MyPromise.all([promise4, promise5]).then(values => {
    console.log('all success:', values);
}).catch(reason => {
    console.log('all catch:', reason);
});

// 测试race方法
let promise6 = MyPromise.resolve(50);
let promise7 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        reject('race error');
    }, 1000);
});
MyPromise.race([promise6, promise7]).then(value => {
    console.log('race success:', value);
}).catch(reason => {
    console.log('race catch:', reason);
});

// 测试reject方法
MyPromise.reject('reject reason').catch(reason => {
    console.log('reject catch:', reason);
});

// 测试resolve方法
MyPromise.resolve('resolved value').then(value => {
    console.log('resolve success:', value);
});