理解并实现 Promise

·  阅读 402
理解并实现 Promise

Promise 的基本原理

  1. Promise 是一个类,在执行这个类的时候会传入一个函数,这个函数会立即执行
  2. Promise 有三种状态
    • Pending 等待
    • Fulfilled 成功
    • Rejected 失败
  3. 状态的改变只能有两种
    • 从 Pending 到 Fulfilled
    • 从 Pending 到 Rejected
    • 上面状态改变只能二选一,状态一旦发生改变就不会再修改
    • Promise 中 resolve 和 reject 两个函数用来更改状态
  4. then 通过状态执行函数
    • 如果状态是成功,执行成功回调函数
    • 如果状态是失败,执行失败回调函数

先确定原生 promise 的实现功能,要了解原生代码 🌰 如下:

const promise = new Promise((resolve, reject) => {
    resolve('yes');
    reject('on');
})
promise.then(value => {
    console.log('resolve:', value);
}, error => {
    console.log('reject:', error);
})

// 输出 resolve: yes
复制代码

Promise 的实现

1. 新建 MyPromise 类,传入函数 executor,executor 传入 resolve 和 reject 方法

// 新建 MyPromise 类
class MyPromise {
    constructor(executor) {
        executor(); // executor 进入会立即执行
        executor(this.resolve, this.reject); // 并传入resolve和reject方法
    }
    // 用箭头函数 this 指向当前实例对象
    resolve =()=> {}
    reject =()=> {}
}
复制代码

2. 增加状态管理

// 三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

// 新建 MyPromise 类
class MyPromise {
    constructor(executor) {
        this.status = PENDING; // 储存状态
        this.value = null; // 成功之后的值
        this.reason = null; // 失败之后的值
        executor(this.resolve, this.reject); // 立即执行并传入resolve和reject方法
    }

    // 用箭头函数 this 指向当前实例对象
    resolve =(value)=> {
        if(this.status === PENDING) { // 只有状态是等待,才执行状态修改
            this.status = FULFILLED;
            this.value = value;
        }
    }

    reject =(reason)=> {
        if(this.status === PENDING) {
            this.status = REJECTED;
            this.reason = reason;
        }
    }
}
复制代码

3. then 方法实现

then 方法接受两个参数 onFulfilled、onRejected,它们分别在状态由 PENDING 改变为FULFILLED、REJECTED后调用

MyPromise.prototype.then = function(onFulfilled, onRejected)
    if (this.status === FULFILLED) {
        onFulfilled(this.value); // 调用成功回调,并且把值返回
    } else if (this.status === REJECTED) {
        onRejected(this.reason); // 调用失败回调,并且把原因返回
    }
}
复制代码

4. 执行代码,验证结果

const promise = new MyPromise((resolve, reject) => {
    resolve('yes')
    reject('no')
})

promise.then(value => {
    console.log('resolve', value);
}, reason => {
    console.log('reject', reason);
})
// resolve yes
复制代码

执行结果符合预期,此处应该开心一下 😁

5. 加入异步逻辑

引入 setTimeout, setTimeout 会加入到异步队列,then 会在 setTimeout 之前执行,then 方法中的状态为 pending,没有对 pending 状态做处理,因此没有输出。

const promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('yes')
    }, 2000);
})

promise.then(value => {
    console.log('resolve', value);
}, reason => {
    console.log('reject', reason);
})
// 没有任何输出
复制代码

6. 处理 pending 逻辑以及 then 链式调用

class MyPromise{
    constructor(executor) {
        ...
        this.onFulfilledCallbacks = []; // 存储成功回调函数
        this.onRejectedCallbacks = []; // 存储失败回调函数
        ...
    }
    
    resolve =(value)=> {
        if(this.status === PENDING) {
            ...
                this.onFulfilledCallbacks && this.onFulfilledCallbacks.forEach((onFulfilledCallback) => {
                onFulfilledCallback(value);
            });
        }
    }

    reject =(reason)=> {
        if(this.status === PENDING) {
            ...
            this.onRejectedCallbacks && this.onRejectedCallbacks((onRejectedCallback) => {
               onRejectedCallback(reason);
            });
        }    
    }
}

MyPromise.prototype.then = function(onFulfilled, onRejected)
    if (this.status === FULFILLED) {
        ...
    } else if (this.status === REJECTED) {
        ...
    } else if(this.status === PENDING) {
       this.onFulfilledCallback = onFulfilled;
       this.onRejectedCallback = onRejected;
    }
}

const promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('yes')
    }, 2000);
})

promise.then(value => {
    console.log(1)
    console.log('resolve', value);
})

promise.then(value => {
    console.log(2)
    console.log('resolve', value)
})
// 1
// resolve yes
// 2
// resolve yes
复制代码

7. 优化 then 异步 以及 链式调用

虽然 resolve 是同步执行的,必须保证 then 是异步调用的,用 setTimeout 来模拟异步调用。 保证链式调用,即 then 方法中要返回一个新的 promise,并将 then 方法的返回值进行 resolve。

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
        // 判断状态
        if (this.status === FULFILLED) {
            setTimeout(() => {
                try {
                    const x = onFulfilled(this.value); // 调用成功回调,并且把值返回
                    resolve(x);
                } catch (reason) {
                    reject(reason);
                }
            }, 0);
        } else if (this.status === REJECTED) {
            setTimeout(() => {
                try {
                    const x = onRejected(this.reason); // 调用失败回调,并且把原因返回
                    resolve(x);
                } catch (reason) {
                    reject(reason);
                }
            }, 0);
        } else if(this.status === PENDING) {
            this.onFulfilledCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value); // 调用成功回调,并且把值返回
                        resolve(x);
                    } catch (reason) {
                        reject(reason);
                    }
                }, 0);
            });
            this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.reason); // 调用失败回调,并且把原因返回
                        resolve(x);
                    } catch (reason) {
                        reject(reason);
                    }
                }, 0);
            });
        }
    })
}

new MyPromise((resolve, reject) => {
    resolve('yes1');
    console.log(1);
}).then(value => {
    console.log(2);
}).then(value => {
    console.log(3);
});
// 1 2 3
复制代码

上面 promise 、then 方法链式调用已基本实现,开心一下吧 😁

8. 实现 all 方法

Promise.all 方法可以将多个 Promise 实例包装成一个新的 Promise 实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值。all 方法接受一个 promise 数组,当所有 promise 状态 resolve 后,执行 resolve。

MyPromise.prototype.all = function(promises) {
    return new MyPromise((resolve, reject) => {
        if(promises.length === 0) {
            resolve([]);
        } else {
            let result = [];
            let index = 0;
            for(let i=0; i<result.length; i++) {
                promises[i].then(value => {
                    result[i] = value;
                    index ++;
                    if(index === promises.length) {
                        resolve(result);
                    }
                }, err => {
                    reject(err);
                    return
                });
            }
        }
    });
};
复制代码

9. 实现 race 方法

race 是赛跑的意思,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。接受一个 promise 数组,当有一个 promise 状态 resolve 后,执行 resolve。

MyPromise.prototype.race = function (promises) {
    return new Promise((resolve, reject) => {
        if (promises.length === 0) {
            resolve();
        } else {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(data => {
                    resolve(data);
                }, err => {
                    reject(err);
                    return;
                });
            }
        }
    });
}
复制代码
分类:
前端
收藏成功!
已添加到「」, 点击更改