手写Promise| 8月更文挑战

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

promise 可以说是现在面试必出的知识点之一,这不也快到了金九银十的招聘季了嘛,就趁着这段时间,好好的复习一下知识点。

promise

promise 是一个对象,代表了一个异步操作的最终完成或者失败。它的出现使异步操作可以像同步操作一样进行处理。

promise的状态

promise 有三种状态,而且,它的状态必然处于三种状态中的其中一种。

status meaning
pending 初始状态,既没有成功,也没有失败
fulfilled 成功状态
rejected 失败状态

默认情况下,promise 的状态处于 pending,然后根据异步操作的状态来改变当前的状态,一旦状态发生了改变,那么就不会在发生改变了。而且,promise 的状态改变只有以下两种可能:

  1. pending 变为 fulfilled
  2. pending 变为 rejected

Promise的基本用法

在使用 Promise 的时候,我们会在里面传入一个函数,如下:

const promise = new Promise((resolve, reject) => {});
复制代码

这个函数是当 Promise 实例被创建出来后,就立即执行的,同步的哦~

console.log('1');
const promise = new Promise((resolve, reject) => {
    console.log('2');
});
console.log('3');

// 1
// 2
// 3
复制代码

这个函数中,有两个参数,resolvereject

functionName meaning
resolve 是一个函数,当异步操作成功时调用,并且把异步操作成功后的结果作为参数传入 resolve, 此时 Promise 的状态会从 pending 变为 fulfilled
reject 是一个函数,当异步操作失败是调用,并且把失败的错误信息或报错信息作为参数传入 reject,此时 Promise 的状态会从 pending 变为 rejected

手写部分①——控制MyPromise的状态

从上面的理解,我们开始手写自己的 MyPromise

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

class MyPromise{

    status = PENDING; // Promise的默认状态
    value = null; // Promise的默认值

    // fn 就是我们在 new Promise时传入的那个函数,那个函数是立即执行的
    // fn 中有两个函数作为参数,
    constructor(fn) {
        // 成功时调用,并且把状态从 pending 变成 fulfilled
        const resolve = value => {
            // 因为 Promise 只能在状态为 pending 的时候改为 fulfilled 或者是 rejected,并且改完之后,状态永远不能修改,所以只有当前状态为 pending 时才可以继续执行下面的操作。
            if(this.status === PENDING){
                this.status = FULFILLED;
                this.value = value;
            }
        };
        // 失败时调用,并且把状态从 pending 变成 rejected
        const reject = reason => {
            if(this.status === PENDING) {
                this.status = REJECTED;
                this.value = reason;
            }
        };
        fn(resolve, reject);
    }
}
复制代码

测试

// 1. 是否可以改变 MyPromise 的状态
const promise = new MyPromise((resolve, reject) => {
    resolve(123);
});
console.log(promise); // MyPromise {status: "fulfilled", value: 123}
// ---
const promise = new MyPromise((resolve, reject) => {
    reject(123);
});
console.log(promise) // MyPromise {status: "rejected", value: 123}

// 2. 当函数内抛出错误时,能否继续运行
//    Promise当遇到错误后,会继续执行后面的代码,并且把状态改为rejected,
const promise = new MyPromise((resolve, reject) => {
    throw new Error('错误');
});
console.log(promise);
// 直接报错,而且没有输出 promise 的值,这是个问题,我们来修改一下
复制代码
// 省略部分代码...
class MyPromise{
    // 省略部分代码...
    constructor(fn) {
        // 省略部分代码...

        // fn(resolve, reject) 把这行代码,修改成下面这样的
        // 用 try...catch...捕获错误,当有错误时,执行 reject 函数,把 Promise 的状态改为 rejected,并且把错误信息传入 reject 函数中作为参数
        try{
            fn(resolve, reject);
        }catch(err){
            reject(err);
        }
    }
}
复制代码

继续测试

// 当函数内抛出错误时,能否继续运行
const promise = new MyPromise((resolve, reject) => {
    throw new Error('错误');
});
console.log(promise); // MyPromise {status: "rejected", value: Error: 错误 at http://127.0.0.1:5500/demo.js:33:11 at new MyPromise (http://127.0.0.1:5500/de…}
复制代码

现在,就算是执行时抛出了错误,也会继续运行下去。并且状态是为 rejected 的!

Promise.then

then() 方法主要是用于处理 Promise 状态发生改变后的回调函数,并且会返回一个新的 Promise

注:then 里面的函数是异步调用的,不是同步的。

它有两个 可选 参数:

functionName meaning
onFulfilled Promise 的状态变成 fulfilled 时调用该函数。该函数有一个参数,用于接受成功后的结果;如果这个参数不是一个函数,则会在内部被替换为 x => x,就是原样的返回 Promise 的最终结果。
onRejected Promise 的状态变成 rejected 时调用,该函数有一个参数,用于接受失败的原因;如果这个参数不是一个函数,则会在内部替换为 throw 函数。

then() 返回的值规则如下:

要看更详细的,请点击promise.prototype.then()

  • 返回的是一个值的话,那么 then 返回的 Promise 会变为 fulfilled,并且将返回值作为 onFulfilled 的参数
  • 没有返回任何值的话,那么 then 返回的 Promise 会变为 fulfilled,并且将 undefined 作为 onFulfilled 的参数
  • 抛出一个错的话,那么 then 的返回的 Promise 会变为 rejected,并且抛出的错误信息作为 onRejected 的参数
  • 返回一个 Promise 的话,那么 then 返回的 Promise 状态是根据那个 Promise 的状态决定的,并且调用的函数参数和那个 Promise 变为最终状态后的回调函数的参数是一样的。

Promise.then() 的基本用法

const promise = new Promise((resolve, reject) => {
    resolve(123);
});
promise.then(res => {
    console.log(res); // 123
});

// -----

const promise = new Promise((resolve, reject) => {
    reject(234);
});
promise.then(res => {
    console.log('成功', res);
}, err => {
    console.log('失败', err); // 失败 234
});

// -----

const promise = new Promise((resolve, reject) => {
    resolve(123);
}).then(res => {
    console.log(1, res);
}).then(res => {
    console.log(2, res);
}, err => {
    console.log(1, err);
}).then(res => {
    console.log(3, res);
});

// 1 123
// 2 undefined
// 3 undefined

// -----

const promise = new Promise((resolve, reject) => {
    resolve(123);
}).then(res => {
    return new Promise((resolve, reject) => {
        resolve(345)
    })
}).then(res => {
    console.log(res); // 345
})

复制代码

手写部分②——实现 then 方法

//...

class MyPromise{
    //...
    thenables = [];  // 存放成功的回调函数
    catchables = []; // 存放失败的回调函数
    constructor(fn){
        const resolve = value => {
            if (this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value;
                // 当回调数组的长度大于0时,则循环数组,执行里面的每一个回调函数
                this.thenables.length > 0 && this.thenables.forEach(handle => handle(value));
            }
        };
        const reject = reason => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.value = reason;
                this.catchables.length > 0 && this.catchables.forEach(handle => handle(reason));
            }
        };
        // ...
    }

    // 如果当前的Promise已经是 fulfilled 或者是 rejected 状态了,那么就直接执行 onFulfilled 或者 onRejected
    then(onFulfilled, onRejected) {
        // 当没有传递处理的回调函数时,需要做一些处理
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

        // 返回一个新的promise
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    // 用于捕获错误
                    try {
                        // 得到回调函数执行的结果,然后传入 ExecCb 函数
                        let x = onFulfilled(this.value);
                        this.ExecCb(x, promise2, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                }, 0);
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.value);
                        this.ExecCb(x, promise2, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                }, 0)
            } else {
                // 当前状态是pending时,需要把回调函数存放进数组中 

                this.thenables.push(() => {
                    try {
                        let x = onFulfilled(this.value);
                        this.ExecCb(x, promise2, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                });
                this.catchables.push(() => {
                    try {
                        let x = onRejected(this.value);
                        this.ExecCb(x, promise2, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                });
            }
        });
        return promise2;
    }

    ExecCb(x, promise, resolve, reject) {
        // 当返回自己,那么就抛出错误信息
        if (x === promise) {
            return reject(new Error('Chaining cycle detected for promise #<Promise>'))
        }
        // 如果 x 是一个 promise对象的话,那么执行就执行then方法
        if (x instanceof MyPromise) {
            x.then(data => resolve(data), err => reject(err))
        } else {
            // 如果不是的话,则把值作为 resolve 的参数
            resolve(x);
        }
    }
}
复制代码

测试:

// 模拟异步情况
const promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 3000)
}).then().then().then(res => console.log(res));
// 3秒钟之后输出 1

// ---
// 返回 Promise 的情况
const promise = new MyPromise((resolve, reject) => {
    resolve(123);
}).then(res => {
    return new MyPromise((resolve, reject) => {
        resolve(345)
    })
}).then(res => {
    console.log(res); // 345
});
复制代码

到这,Promise 比较核心的功能我们都已经实现的差不多了,是不是对它更加的了解了呢?

完整代码:codesandbox.io/s/hopeful-w…

代码有些冗余,请谅解一下。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享