手寫promise思路

之前寫過一篇關(guān)于promise的一些常規(guī)用法,以及promise與async/await關(guān)系的文章。但是我們知道,要想把一個(gè)知識(shí)點(diǎn)完全掌握,知其然而不知其所以然是遠(yuǎn)遠(yuǎn)不夠的,那么接下來將要來探討promise的原理,來分析分析promise的這種規(guī)則機(jī)制是如何實(shí)現(xiàn)的。我們通過手寫promise的方式達(dá)到這個(gè)目的,同時(shí)來加深對primise的理解。

思路

手寫promise之前,我們先來回憶一下promise的使用方式,便于整理思路。

const p1 = new Promise((resolve, reject)=>{
  console.log('此處同步執(zhí)行');
  const a = 2;
  setTimeout(()=>{
  // 此處異步執(zhí)行
  if(a>0){
    resolve('成功');
  }else{
    reject('失敗原因');
  }
  },1000)
});
p1.then((res)=>{
  console.log(res);
},(err)=>{
  console.log(err);
})

從上面的代碼可以看出,要想使用promise需要先通過new Promise(fn)生成一個(gè)實(shí)例p1,然后通過實(shí)例p1調(diào)用then()catch()方法。因此可以得出以下幾點(diǎn)結(jié)論:

  1. Promise本身是一個(gè)構(gòu)造函數(shù),其參數(shù)fn是一個(gè)同步執(zhí)行的回調(diào)函數(shù),該函數(shù)執(zhí)行的參數(shù)也是兩個(gè)函數(shù)resolvereject。這兩個(gè)參數(shù)的作用是等異步操作執(zhí)行完成后,為后續(xù)方法的執(zhí)行傳參,如:then()catch()。
  2. then()用兩個(gè)函數(shù)作為參數(shù),在實(shí)例p1中的resolvereject方法中分別觸發(fā)對應(yīng)的函數(shù),并把異步操作執(zhí)行的結(jié)果傳遞給對應(yīng)的函數(shù)。
  3. Promise有三種狀態(tài):pending、rejectedresolved,同步回調(diào)函數(shù)fn開始執(zhí)行時(shí)狀態(tài)為pending,執(zhí)行了resolvereject后會(huì)將其狀態(tài)改為resolvedrejected。resolved和rejected只能執(zhí)行一次,且Promise狀態(tài)一旦被確定下來,那么就是不可更改的(鎖定)。

通過觀察Promise的使用方式得出的幾點(diǎn)結(jié)論,書寫promise的思路大致可以通過下面幾個(gè)方面來完成:

  1. 定義Promise的三種狀態(tài);
  2. 創(chuàng)建構(gòu)造函數(shù),并為構(gòu)造函數(shù)定義一個(gè)回調(diào)函數(shù)作為參數(shù);
  3. 在構(gòu)造函數(shù)內(nèi)定義變量來保存Promise的狀態(tài),定義兩個(gè)函數(shù)resolvereject,并在構(gòu)造函數(shù)中執(zhí)行回調(diào)函數(shù)的時(shí)候?qū)⒋藗魅耄?/li>
  4. 函數(shù)resolvereject目前的作用是改變Promise的狀態(tài),保存異步操作返回的值或者失敗的原因;
  5. 為構(gòu)造函數(shù)創(chuàng)建then()方法,then()方法的參數(shù)是兩個(gè)函數(shù)onResolvedonRejected,這兩個(gè)函數(shù)將被傳入構(gòu)造函數(shù)內(nèi)定義的resolvereject方法中執(zhí)行。此時(shí)函數(shù)resolvereject發(fā)揮了它的第二個(gè)作用,就是執(zhí)行then()方法傳遞過來的回調(diào)函數(shù)。

實(shí)現(xiàn)

有了大致的思路,那么接下來就是如何去實(shí)現(xiàn)它。

  1. Promise構(gòu)造函數(shù)的設(shè)計(jì),對應(yīng)思路1、2、3、4
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';
class myPromise {
    // * 記錄狀態(tài)
    constructor(executor) {
        // * 保存Promise的狀態(tài)
        this.status = PROMISE_STATUS_PENDING;
        // * 保存?zhèn)魅氲闹?        this.value = undefined;
        this.reason = undefined;
        const resolve = value => {
            if (this.status == PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_FULFILLED;
                this.value = value;
                console.log('resolve被調(diào)用');
            }
        };
        const reject = reason => {
            if (this.status == PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_REJECTED;
                this.reason = reason;
                console.log('reject被調(diào)用');
            }
        };
        executor(resolve, reject);
    }
}
  1. 定義好構(gòu)造函數(shù),接下來的任務(wù)就是書寫構(gòu)造函數(shù)的方法了,對應(yīng)5。修改上面的代碼如下:
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';
class myPromise {
    // * 記錄狀態(tài)
    constructor(executor) {
        // * 保存Promise的狀態(tài)
        this.status = PROMISE_STATUS_PENDING;
        // * 保存?zhèn)魅氲闹?        this.value = undefined;
        this.reason = undefined;
        const resolve = value => {
            if (this.status == PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_FULFILLED;
                //* 定時(shí)器是一個(gè)宏任務(wù),會(huì)放在下一次事件循環(huán)時(shí)使用
                queueMicrotask(() => {
                    this.value = value;
                    console.log('resolve被調(diào)用', this.value);
                    // * 執(zhí)行then傳入進(jìn)來的第一個(gè)回調(diào)函數(shù)
                    this.onResolved(this.value);
                });
            }
        };
        const reject = reason => {
            if (this.status == PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_REJECTED;
                queueMicrotask(() => {
                    this.reason = reason;
                    console.log('reject被調(diào)用', this.reason);
                    // * 執(zhí)行then傳入進(jìn)來的第二個(gè)回調(diào)函數(shù)
                    this.onRejected(this.reason);
                });
            }
        };
        executor(resolve, reject);
    }
    // then方法
    then(onResolved, onRejected) {
        this.onResolved = onResolved;
        this.onRejected = onRejected;
    }
}

優(yōu)化

完成以上代碼已經(jīng)搭建了一個(gè)具備基本功能的Promise,不防試一下,相信它會(huì)帶給你滿意的結(jié)果。

const promise = new myPromise((resolve, reject) => {
    console.log('狀態(tài)pending');
    resolve('1111');
});
promise.then(
    res => {
        console.log('res:', res);
    },
    err => {
        console.log('err:', err);
    },
);

運(yùn)行以上代碼,命令行會(huì)相繼輸出狀態(tài)pending、resolve被調(diào)用 1111、res: 1111等,這代表著最基礎(chǔ)版的Promise已經(jīng)完成了。但是它仍然有很多問題,比如then()方法無法多次調(diào)用和鏈?zhǔn)秸{(diào)用、沒有catch()方法等,所以接下來我們就要優(yōu)化上面基礎(chǔ)版的Promise,使它具備和官方基本一致的功能。

  1. 實(shí)現(xiàn)then()的多次調(diào)用
    then()多次調(diào)用就需要在構(gòu)造函數(shù)里定義兩個(gè)數(shù)組保存then()方法中傳進(jìn)來的回調(diào),然后遍歷這個(gè)數(shù)組,執(zhí)行數(shù)組里的所有回調(diào)函數(shù),修改代碼如下:
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';
class myPromise {
    // * 記錄狀態(tài)
    constructor(executor) {
        // * 保存Promise的狀態(tài)
        this.status = PROMISE_STATUS_PENDING;
        // * 保存?zhèn)魅氲闹?        this.value = undefined;
        this.reason = undefined;
        this.onResolvedFns = [];
        this.onRejectedFns = [];
        const resolve = value => {
            if (this.status == PROMISE_STATUS_PENDING) {
                //* 定時(shí)器是一個(gè)宏任務(wù),會(huì)放在下一次事件循環(huán)時(shí)使用
                queueMicrotask(() => {
                    this.status = PROMISE_STATUS_FULFILLED;
                    this.value = value;
                    console.log('resolve被調(diào)用', this.value);
                    // * 執(zhí)行then傳入進(jìn)來的第一個(gè)回調(diào)函數(shù)
                    this.onResolvedFns.forEach(Fn => {
                        Fn(this.value);
                    });
                });
            }
        };
        const reject = reason => {
            if (this.status == PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    this.status = PROMISE_STATUS_REJECTED;
                    this.reason = reason;
                    console.log('reject被調(diào)用', this.reason);
                    // * 執(zhí)行then傳入進(jìn)來的第二個(gè)回調(diào)函數(shù)
                    this.onRejectedFns.forEach(Fn => {
                        Fn(this.reason);
                    });
                });
            }
        };
        executor(resolve, reject);
    }
    // then方法
    then(onResolved, onRejected) {
        console.log(this.status);
        // 如果then方法調(diào)用的時(shí)候,狀態(tài)已經(jīng)確定下來了,應(yīng)該直接執(zhí)行的
        if (this.status === PROMISE_STATUS_FULFILLED && onResolved) {
            onResolved(this.value);
        } else if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
            onRejected(this.reason);
        } else {
            // 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
            this.onResolvedFns.push(onResolved);
            this.onRejectedFns.push(onRejected);
        }
    }
}
  1. 實(shí)現(xiàn)then()的鏈?zhǔn)秸{(diào)用
    從上面的代碼中可以清楚的看到then()方法是掛載在構(gòu)造函數(shù)myPromise上的,所以為了實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,需要在then()方法里返回一個(gè)新的Promise對象,然后使用新的Promise對象的resolve方法去處理對應(yīng)的回調(diào)函數(shù)的返回值。從代碼的簡潔度考慮,我們需要封裝一個(gè)工具函數(shù),用來處理異常和回調(diào)函數(shù)。
    與此同時(shí),當(dāng)回調(diào)函數(shù)executor的執(zhí)行發(fā)生異常時(shí),也許有執(zhí)行reject|函數(shù)。因此,我們把代碼調(diào)整如下:
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';

// 工具函數(shù)
function execFunctionWithCatchError(exeFn, value, resolve, reject) {
    try {
        let result = exeFn(value);
        resolve(result);
    } catch (err) {
        reject(err);
    }
}

class myPromise {
    // * 記錄狀態(tài)
    constructor(executor) {
        // * 保存Promise的狀態(tài)
        this.status = PROMISE_STATUS_PENDING;
        // * 保存?zhèn)魅氲闹?        this.value = undefined;
        this.reason = undefined;
        this.onResolvedFns = [];
        this.onRejectedFns = [];
        const resolve = value => {
            if (this.status == PROMISE_STATUS_PENDING) {
                // * 添加微任務(wù)
                //* 定時(shí)器是一個(gè)宏任務(wù),會(huì)放在下一次事件循環(huán)時(shí)使用
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_FULFILLED;
                    this.value = value;
                    // console.log('resolve被調(diào)用', this.value);
                    // * 執(zhí)行then傳入進(jìn)來的第一個(gè)回調(diào)函數(shù)
                    this.onResolvedFns.forEach(Fn => {
                        Fn(this.value);
                    });
                });
            }
        };
        const reject = reason => {
            if (this.status == PROMISE_STATUS_PENDING) {
                //  * 添加微任務(wù)
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_REJECTED;
                    this.reason = reason;
                    // console.log('reject被調(diào)用', this.reason);
                    // * 執(zhí)行then傳入進(jìn)來的第二個(gè)回調(diào)函數(shù)
                    this.onRejectedFns.forEach(Fn => {
                        Fn(this.reason);
                    });
                });
            }
        };
        // * 在調(diào)用executor時(shí)判斷里面是否拋出異常
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }
    // then方法
    then(onResolved, onRejected) {
        return new myPromise((resolve, reject) => {
            // 如果then方法調(diào)用的時(shí)候,狀態(tài)已經(jīng)確定下來了,應(yīng)該直接執(zhí)行的
            if (this.status === PROMISE_STATUS_FULFILLED && onResolved) {
                // onResolved(this.value);
                execFunctionWithCatchError(onResolved, this.value, resolve, reject);
            } else if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
                // onRejected(this.reason);
                execFunctionWithCatchError(
                    onRejected,
                    this.reason,
                    resolve,
                    reject,
                );
            } else {
                // 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
                this.onResolvedFns.push(() => {
                    execFunctionWithCatchError(
                        onResolved,
                        this.value,
                        resolve,
                        reject,
                    );
                });
                this.onRejectedFns.push(() => {
                    execFunctionWithCatchError(
                        onRejected,
                        this.reason,
                        resolve,
                        reject,
                    );
                });
            }
        });
    }
}
  1. catch()方法的實(shí)現(xiàn)
    我們知道,在官方提供的Promise中可不止then()一種方法,其中最常用的便是catch()方法了。
    catch()then()方法不同,它只會(huì)接受一個(gè)onRejected的回調(diào)函數(shù),catch()方法執(zhí)行的其實(shí)是then()方法第二個(gè)參數(shù)的工作,then(null, function() {})就等同于catch(function() {})。但是用this.then(undefined,onRejected);來實(shí)現(xiàn)catch()方法顯然是不可以的,因?yàn)檫@樣做的話,catch()方法是針對新的Promiserejected的狀態(tài),我們要解決的問題就是如何讓catch()方法捕獲原Promise對象的rejected狀態(tài)。
    所以我們要對then()方法做一些改動(dòng),在方法內(nèi)部前面判斷第二個(gè)參數(shù)是否有值,如果沒有值,就重新賦值為一個(gè)函數(shù),函數(shù)內(nèi)部拋出一個(gè)異常。這樣在新的Promise就能捕獲到原來的promiserejected的狀態(tài)了。具體實(shí)現(xiàn)方式如下:
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';

// 工具函數(shù)
function execFunctionWithCatchError(exeFn, value, resolve, reject) {
    try {
        let result = exeFn(value);
        resolve(result);
    } catch (err) {
        reject(err);
    }
}

class myPromise {
    // * 記錄狀態(tài)
    constructor(executor) {
        // * 保存Promise的狀態(tài)
        this.status = PROMISE_STATUS_PENDING;
        // * 保存?zhèn)魅氲闹?        this.value = undefined;
        this.reason = undefined;
        this.onResolvedFns = [];
        this.onRejectedFns = [];
        const resolve = value => {
            if (this.status == PROMISE_STATUS_PENDING) {
                // * 添加微任務(wù)
                //* 定時(shí)器是一個(gè)宏任務(wù),會(huì)放在下一次事件循環(huán)時(shí)使用
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_FULFILLED;
                    this.value = value;
                    // console.log('resolve被調(diào)用', this.value);
                    // * 執(zhí)行then傳入進(jìn)來的第一個(gè)回調(diào)函數(shù)
                    this.onResolvedFns.forEach(Fn => {
                        Fn(this.value);
                    });
                });
            }
        };
        const reject = reason => {
            if (this.status == PROMISE_STATUS_PENDING) {
                //  * 添加微任務(wù)
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_REJECTED;
                    this.reason = reason;
                    // console.log('reject被調(diào)用', this.reason);
                    // * 執(zhí)行then傳入進(jìn)來的第二個(gè)回調(diào)函數(shù)
                    this.onRejectedFns.forEach(Fn => {
                        Fn(this.reason);
                    });
                });
            }
        };
        // * 在調(diào)用executor時(shí)判斷里面是否拋出異常
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }
    // then方法
    then(onResolved, onRejected) {
        onRejected =
            onRejected ||
            (err => {
                throw err;
            });
        return new myPromise((resolve, reject) => {
            // 如果then方法調(diào)用的時(shí)候,狀態(tài)已經(jīng)確定下來了,應(yīng)該直接執(zhí)行的
            if (this.status === PROMISE_STATUS_FULFILLED && onResolved) {
                // onResolved(this.value);
                execFunctionWithCatchError(onResolved, this.value, resolve, reject);
            } else if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
                // onRejected(this.reason);
                execFunctionWithCatchError(
                    onRejected,
                    this.reason,
                    resolve,
                    reject,
                );
            } else {
                // 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
                if (onResolved)
                    this.onResolvedFns.push(() => {
                        execFunctionWithCatchError(
                            onResolved,
                            this.value,
                            resolve,
                            reject,
                        );
                    });
                if (onRejected)
                    this.onRejectedFns.push(() => {
                        execFunctionWithCatchError(
                            onRejected,
                            this.reason,
                            resolve,
                            reject,
                        );
                    });
            }
        });
    }
    // * catch方法
    catch(onRejected) {
        this.then(undefined, onRejected);
    }
}
  1. finally()方法的實(shí)現(xiàn)
    前面講到的then()catch()都是Promise的實(shí)例方法,也可以稱為對象方法,除此之外,Promise還有一個(gè)實(shí)例方法,那就是finally()finally()方法大致可以概括如下:
  • finally是ES9(ES2018)新增的一個(gè)特性:表示無論Promise的狀態(tài)變?yōu)?code>resolved還是reject,最終都會(huì)被執(zhí)行的代碼。
  • finally方法是不接收參數(shù)的,因?yàn)闊o論前面是resolved狀態(tài),還是reject狀態(tài),它都會(huì)執(zhí)行。
  • finally其實(shí)也是返回一個(gè)Promise對象,但是其實(shí)很少人會(huì)用它。
    因此,finally方法的實(shí)現(xiàn)也很簡單,只需要在Promise構(gòu)造函數(shù)中定義一個(gè)方法,無論promise的狀態(tài)是什么。代碼實(shí)現(xiàn)如下:
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';

// 工具函數(shù)
function execFunctionWithCatchError(exeFn, value, resolve, reject) {
    try {
        let result = exeFn(value);
        resolve(result);
    } catch (err) {
        reject(err);
    }
}

class myPromise {
    // * 記錄狀態(tài)
    constructor(executor) {
        // * 保存Promise的狀態(tài)
        this.status = PROMISE_STATUS_PENDING;
        // * 保存?zhèn)魅氲闹?        this.value = undefined;
        this.reason = undefined;
        this.onResolvedFns = [];
        this.onRejectedFns = [];
        const resolve = value => {
            if (this.status == PROMISE_STATUS_PENDING) {
                // * 添加微任務(wù)
                //* 定時(shí)器是一個(gè)宏任務(wù),會(huì)放在下一次事件循環(huán)時(shí)使用
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_FULFILLED;
                    this.value = value;
                    // console.log('resolve被調(diào)用', this.value);
                    // * 執(zhí)行then傳入進(jìn)來的第一個(gè)回調(diào)函數(shù)
                    this.onResolvedFns.forEach(Fn => {
                        Fn(this.value);
                    });
                });
            }
        };
        const reject = reason => {
            if (this.status == PROMISE_STATUS_PENDING) {
                //  * 添加微任務(wù)
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_REJECTED;
                    this.reason = reason;
                    // console.log('reject被調(diào)用', this.reason);
                    // * 執(zhí)行then傳入進(jìn)來的第二個(gè)回調(diào)函數(shù)
                    this.onRejectedFns.forEach(Fn => {
                        Fn(this.reason);
                    });
                });
            }
        };
        // * 在調(diào)用executor時(shí)判斷里面是否拋出異常
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }
    // then方法
    then(onResolved, onRejected) {
        onRejected =
            onRejected ||
            (err => {
                throw err;
            });
        return new myPromise((resolve, reject) => {
            // 如果then方法調(diào)用的時(shí)候,狀態(tài)已經(jīng)確定下來了,應(yīng)該直接執(zhí)行的
            if (this.status === PROMISE_STATUS_FULFILLED && onResolved) {
                // onResolved(this.value);
                execFunctionWithCatchError(onResolved, this.value, resolve, reject);
            } else if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
                // onRejected(this.reason);
                execFunctionWithCatchError(
                    onRejected,
                    this.reason,
                    resolve,
                    reject,
                );
            } else {
                // 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
                if (onResolved)
                    this.onResolvedFns.push(() => {
                        execFunctionWithCatchError(
                            onResolved,
                            this.value,
                            resolve,
                            reject,
                        );
                    });
                if (onRejected)
                    this.onRejectedFns.push(() => {
                        execFunctionWithCatchError(
                            onRejected,
                            this.reason,
                            resolve,
                            reject,
                        );
                    });
            }
        });
    }
    // * catch方法
    catch(onRejected) {
        this.then(undefined, onRejected);
    }
    // finally方法
    finally(onFinally) {
        return this.then(
            () => {
                onFinally();
            },
            () => {
                onFinally();
            },
        );
    }
}
  1. Promise的類方法的實(shí)現(xiàn)
    前面說到的thencatch、finally方法都屬于Promise的實(shí)例方法,都是存放在Promiseprototype上的。下面我們將學(xué)習(xí)類方法。
  • Promise.resolve()方法:有時(shí)候我們已經(jīng)有一個(gè)現(xiàn)成的內(nèi)容,希望將其轉(zhuǎn)成Promise來使用,這個(gè)時(shí)候我們可以使用Promise.resolve()方法來完成,所以Promise.resolve()相當(dāng)于new Promise,并且執(zhí)行resolve操作。
  • Promise.reject()方法:reject方法類似于resolve方法,只是會(huì)將Promise對象的狀態(tài)設(shè)置為rejected狀態(tài),所以Promise.reject無論傳過來的參數(shù)是什么狀態(tài),都會(huì)直接作為reject的參數(shù)傳遞到catch的。
  • Promise.all()方法:Promise.all()我們在上一篇中講過,對于用法這里不再多做闡述,大致可以歸納為只有當(dāng)所有的promise都變成resolved狀態(tài)時(shí),原promise才會(huì)變成resolved狀態(tài),相反當(dāng)任意一個(gè)promise變成rejected狀態(tài)時(shí),原promise就會(huì)變成rejected狀態(tài),并且仍然處于pending狀態(tài)的promise將不會(huì)獲取到結(jié)果。不明白的同學(xué)請自行查閱Promise 與async/await。
  • Promise.allSettled()方法:Promise.allSettled是ES11(ES2020)中新添加的API,它用于解決Promise.all()方法的一個(gè)缺陷(也是其特征):當(dāng)有一個(gè)Promise變成rejected狀態(tài)時(shí),新Promise就會(huì)立即變成對應(yīng)的rejected狀態(tài)。 Promise.allSettled方法會(huì)在所有的Promise都有結(jié)果時(shí),無論是resolved,還是rejected,才會(huì)有最終的狀態(tài),并且這個(gè)Promise的結(jié)果一定是resolved`。
  • Promise.race()方法:Promise.race()方法同樣在上一篇中講過,大致可以歸納為:數(shù)組中的其中一個(gè)promise返回狀態(tài)時(shí),無論此狀態(tài)是resolved或者rejected,它都將會(huì)成為原promise的狀態(tài),即先到先得。
  • Promise.any()方法:Promise.any()方法是ES12中新增的方法,和Promise.race()方法是類似的。Promise.any()方法會(huì)等到一個(gè)resolved狀態(tài),才會(huì)決定新Promise的狀態(tài),就算所有的Promise都是rejected的,那么也會(huì)等到所有的Promise都變成rejected狀態(tài),err信息為:AggregateError: All promises were rejected。
    看完所有Promise類方法的使用,接下來我們就開下怎么來實(shí)現(xiàn)他們吧。
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';

// 工具函數(shù)
function execFunctionWithCatchError(exeFn, value, resolve, reject) {
    try {
        const result = exeFn(value);
        resolve(result);
    } catch (err) {
        reject(err);
    }
}

class myPromise {
    // * 記錄狀態(tài)
    constructor(executor) {
        // * 保存Promise的狀態(tài)
        this.status = PROMISE_STATUS_PENDING;
        // * 保存?zhèn)魅氲闹?        this.value = undefined;
        this.reason = undefined;
        this.onResolvedFns = [];
        this.onRejectedFns = [];
        const resolve = value => {
            if (this.status == PROMISE_STATUS_PENDING) {
                // * 添加微任務(wù)
                //* 定時(shí)器是一個(gè)宏任務(wù),會(huì)放在下一次事件循環(huán)時(shí)使用
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_FULFILLED;
                    this.value = value;
                    // console.log('resolve被調(diào)用', this.value);
                    // * 執(zhí)行then傳入進(jìn)來的第一個(gè)回調(diào)函數(shù)
                    this.onResolvedFns.forEach(Fn => {
                        Fn(this.value);
                    });
                });
            }
        };
        const reject = reason => {
            if (this.status == PROMISE_STATUS_PENDING) {
                //  * 添加微任務(wù)
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_REJECTED;
                    this.reason = reason;
                    // console.log('reject被調(diào)用', this.reason);
                    // * 執(zhí)行then傳入進(jìn)來的第二個(gè)回調(diào)函數(shù)
                    this.onRejectedFns.forEach(Fn => {
                        Fn(this.reason);
                    });
                });
            }
        };
        // * 在調(diào)用executor時(shí)判斷里面是否拋出異常
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }
    // then方法
    then(onResolved, onRejected) {
        onRejected =
            onRejected ||
            (err => {
                throw err;
            });
        return new myPromise((resolve, reject) => {
            // 如果then方法調(diào)用的時(shí)候,狀態(tài)已經(jīng)確定下來了,應(yīng)該直接執(zhí)行的
            if (this.status === PROMISE_STATUS_FULFILLED && onResolved) {
                // onResolved(this.value);
                execFunctionWithCatchError(onResolved, this.value, resolve, reject);
            } else if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
                // onRejected(this.reason);
                execFunctionWithCatchError(
                    onRejected,
                    this.reason,
                    resolve,
                    reject,
                );
            } else {
                // 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
                if (onResolved)
                    this.onResolvedFns.push(() => {
                        execFunctionWithCatchError(
                            onResolved,
                            this.value,
                            resolve,
                            reject,
                        );
                    });
                if (onRejected)
                    this.onRejectedFns.push(() => {
                        execFunctionWithCatchError(
                            onRejected,
                            this.reason,
                            resolve,
                            reject,
                        );
                    });
            }
        });
    }
    // * catch方法
    catch(onRejected) {
        this.then(undefined, onRejected);
    }
    // finally方法
    finally(onFinally) {
        return this.then(
            () => {
                onFinally();
            },
            () => {
                onFinally();
            },
        );
    }
    // 類方法resolve
    static resolve(value) {
        return new myPromise((resolve, reject) => {
            resolve(value);
        });
    }
    // 類方法reject
    static reject(reason) {
        return new myPromise((resolve, reject) => {
            reject(reason);
        });
    }
    // 類方法all
    static all(promises) {
        // * 問題關(guān)鍵:什么時(shí)候執(zhí)行resolve,什么時(shí)候執(zhí)行reject
        return new myPromise((resolve, reject) => {
            let values = [];
            promises.forEach(promise => {
                promise
                    .then(res => {
                        values.push(res);
                        if (values.length == promises.length) resolve(values);
                    })
                    .catch(err => {
                        reject(err);
                    });
            });
        });
    }
    // 類方法allSettled
    static allSettled(promises) {
        return new myPromise((resolve, reject) => {
            let results = [];
            promises.forEach(promise => {
                promise
                    .then(res => {
                        results.push({ status: PROMISE_STATUS_FULFILLED, value: res });
                        if (results.length == promises.length) resolve(results);
                    })
                    .catch(err => {
                        results.push({ status: PROMISE_STATUS_REJECTED, value: err });
                        if (results.length == promises.length) resolve(results);
                    });
            });
        });
    }
    // 類方法race
    static race(promises) {
        return new myPromise((resolve, reject) => {
            promises.forEach(promise => {
                // promise.then(res=>{
                //  resolve(res);
                // }).catch(err=>{
                //  reject(err);
                // })
                promise.then(resolve, reject);
            });
        });
    }
    // 類方法any
    static any(promises) {
        // * resolve 必須等待有一個(gè)成功的結(jié)果
        // * reject 所有的都失敗才執(zhí)行 reject
        return new myPromise((resolve, reject) => {
            let reasons = [];
            promises.forEach(promise => {
                promise
                    .then(res => {
                        resolve(res);
                    })
                    .catch(err => {
                        reasons.push(err);
                        if (reasons.length == promises.length) {
                            reject(
                                new AggregateError(
                                    reasons,
                                    ' AggregateError: All promises were rejected',
                                ),
                            );
                        }
                    });
            });
        });
    }
}

說了這么多,最后放上一張Promise的知識(shí)點(diǎn)關(guān)系圖作為結(jié)束。

promise.png

參考文獻(xiàn):http://www.itdecent.cn/p/e91ce318d7ad

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 首先我們得先理解Promise工作的機(jī)制,其實(shí)Promise就是一個(gè)構(gòu)造函數(shù) 它的內(nèi)部有三個(gè)狀態(tài): pending...
    Poppy11閱讀 11,229評論 0 6
  • 1. promise要解決的問題: 腦筋急轉(zhuǎn)彎:把牛關(guān)進(jìn)冰箱里,要分幾步? 很顯然,這三個(gè)操作不能顛倒順序,否則任...
    月上秦少閱讀 1,713評論 0 3
  • Promise概念 Promise是異步編程的一種解決方案,將異步操作以同步的流程表達(dá)出來,避免了層層嵌套的回調(diào)函...
    fanlelee閱讀 1,036評論 0 2
  • 1. 實(shí)例對象與函數(shù)對象的區(qū)別 函數(shù)對象:將函數(shù)作為對象使用時(shí),簡稱為函數(shù)對象 實(shí)例對象:new 函數(shù)產(chǎn)生的對象,...
    雨溪灘閱讀 315評論 0 0
  • 通過前面幾節(jié)課的學(xué)習(xí),我們認(rèn)識(shí)到:想優(yōu)雅地進(jìn)行異步操作,必須要熟識(shí)一個(gè)極其重要的概念 —— Promise。它是取...
    ikonan閱讀 621評論 0 1

友情鏈接更多精彩內(nèi)容