手寫promise思路

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

思路

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

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

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

  1. Promise本身是一個構造函數,其參數fn是一個同步執行的回調函數,該函數執行的參數也是兩個函數resolvereject。這兩個參數的作用是等異步操作執行完成后,為后續方法的執行傳參,如:then()catch()
  2. then()用兩個函數作為參數,在實例p1中的resolvereject方法中分別觸發對應的函數,并把異步操作執行的結果傳遞給對應的函數。
  3. Promise有三種狀態:pendingrejectedresolved,同步回調函數fn開始執行時狀態為pending,執行了resolvereject后會將其狀態改為resolvedrejected。resolved和rejected只能執行一次,且Promise狀態一旦被確定下來,那么就是不可更改的(鎖定)。

通過觀察Promise的使用方式得出的幾點結論,書寫promise的思路大致可以通過下面幾個方面來完成:

  1. 定義Promise的三種狀態;
  2. 創建構造函數,并為構造函數定義一個回調函數作為參數;
  3. 在構造函數內定義變量來保存Promise的狀態,定義兩個函數resolvereject,并在構造函數中執行回調函數的時候將此傳入;
  4. 函數resolvereject目前的作用是改變Promise的狀態,保存異步操作返回的值或者失敗的原因;
  5. 為構造函數創建then()方法,then()方法的參數是兩個函數onResolvedonRejected,這兩個函數將被傳入構造函數內定義的resolvereject方法中執行。此時函數resolvereject發揮了它的第二個作用,就是執行then()方法傳遞過來的回調函數。

實現

有了大致的思路,那么接下來就是如何去實現它。

  1. Promise構造函數的設計,對應思路1、2、3、4
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';
class myPromise {
    // * 記錄狀態
    constructor(executor) {
        // * 保存Promise的狀態
        this.status = PROMISE_STATUS_PENDING;
        // * 保存傳入的值
        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被調用');
            }
        };
        const reject = reason => {
            if (this.status == PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_REJECTED;
                this.reason = reason;
                console.log('reject被調用');
            }
        };
        executor(resolve, reject);
    }
}
  1. 定義好構造函數,接下來的任務就是書寫構造函數的方法了,對應5。修改上面的代碼如下:
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';
class myPromise {
    // * 記錄狀態
    constructor(executor) {
        // * 保存Promise的狀態
        this.status = PROMISE_STATUS_PENDING;
        // * 保存傳入的值
        this.value = undefined;
        this.reason = undefined;
        const resolve = value => {
            if (this.status == PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_FULFILLED;
                //* 定時器是一個宏任務,會放在下一次事件循環時使用
                queueMicrotask(() => {
                    this.value = value;
                    console.log('resolve被調用', this.value);
                    // * 執行then傳入進來的第一個回調函數
                    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被調用', this.reason);
                    // * 執行then傳入進來的第二個回調函數
                    this.onRejected(this.reason);
                });
            }
        };
        executor(resolve, reject);
    }
    // then方法
    then(onResolved, onRejected) {
        this.onResolved = onResolved;
        this.onRejected = onRejected;
    }
}

優化

完成以上代碼已經搭建了一個具備基本功能的Promise,不防試一下,相信它會帶給你滿意的結果。

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

運行以上代碼,命令行會相繼輸出狀態pending、resolve被調用 1111、res: 1111等,這代表著最基礎版的Promise已經完成了。但是它仍然有很多問題,比如then()方法無法多次調用和鏈式調用、沒有catch()方法等,所以接下來我們就要優化上面基礎版的Promise,使它具備和官方基本一致的功能。

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

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

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

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

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

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

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

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

class myPromise {
    // * 記錄狀態
    constructor(executor) {
        // * 保存Promise的狀態
        this.status = PROMISE_STATUS_PENDING;
        // * 保存傳入的值
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedFns = [];
        this.onRejectedFns = [];
        const resolve = value => {
            if (this.status == PROMISE_STATUS_PENDING) {
                // * 添加微任務
                //* 定時器是一個宏任務,會放在下一次事件循環時使用
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_FULFILLED;
                    this.value = value;
                    // console.log('resolve被調用', this.value);
                    // * 執行then傳入進來的第一個回調函數
                    this.onResolvedFns.forEach(Fn => {
                        Fn(this.value);
                    });
                });
            }
        };
        const reject = reason => {
            if (this.status == PROMISE_STATUS_PENDING) {
                //  * 添加微任務
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return;
                    this.status = PROMISE_STATUS_REJECTED;
                    this.reason = reason;
                    // console.log('reject被調用', this.reason);
                    // * 執行then傳入進來的第二個回調函數
                    this.onRejectedFns.forEach(Fn => {
                        Fn(this.reason);
                    });
                });
            }
        };
        // * 在調用executor時判斷里面是否拋出異常
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }
    // then方法
    then(onResolved, onRejected) {
        onRejected =
            onRejected ||
            (err => {
                throw err;
            });
        return new myPromise((resolve, reject) => {
            // 如果then方法調用的時候,狀態已經確定下來了,應該直接執行的
            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 {
                // 將成功回調和失敗回調添加到數組中
                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) {
        // * 問題關鍵:什么時候執行resolve,什么時候執行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 必須等待有一個成功的結果
        // * reject 所有的都失敗才執行 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的知識點關系圖作為結束。

promise.png

參考文獻:http://www.lxweimin.com/p/e91ce318d7ad

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容