討論 Promise all 如何執行完不要被 catch 中斷

Node.js

今天來分享一個之前同事問我的問題,因為我們工作上很常用到的是 fetch 或是 request,這種需要多發 request 的情境,但發 request 有一個問題就是 request 發送有機會不成功,那這樣使用 Promise.all 就會跑到一半停止讓整個流程變得很難處理。所以就來說明我們應該怎麼解決這個問題。

Promise 失敗彈出 catch

首先我們先用 setTimeout 來製作一個需要等待 1 sec,但是會發生 catch 的例子來模擬情境:

const wait = new Promise((resolve, reject) => {
        setTimeout(() => {
                reject('reject err');
        }, 1000)
});

function promiseAll(promises) {
        return Promise.all(promises).then(res => {
                console.log(`res: ${res}`);
        }).catch(err => {
                console.log(`err: ${err}`);
        });
};

const waitArray = [];
waitArray.push(wait);
waitArray.push(wait);

promiseAll(waitArray);

從上面範例可以看到,我製作了一個叫做 waitPromise function,使用 waitArray 把它裝起來,放入 promiseAl,這樣寫是希望想要得到執行兩次 wait function 的結果,那我們來執行一下:

clarence ~ $ node index.js
err: reject err

很明顯我們原本希望可以執行兩次 wait function,但是因為 catch 的關係 Promise.all 直接吐出 catch 中斷了第二個 wait function

解決 Promise 失敗被 catch 的問題

OK! 我們現在清楚我們要解決的問題了,那我們來把 wait function 包一層起來讓它不要噴出 catch 感覺就可以解決這個問題了。

一開始先處理單個,包完應該會如下:

const wait = new Promise((resolve, reject) => {
        setTimeout(() => {
                reject('reject err');
        }, 1000)
})

const catchPromise = wait.then(res => res).catch(err => err);

catchPromise.then(res => {
        console.log(`res: ${res}`);
}).catch(err => {
        console.log(`err: ${err}`);
});

一樣我們來執行一下!

clarence ~ $ node index.js
res: reject err

OK! 跟預期的依樣,我們讓他在執行 Promise 的時候不要彈到 catch ,剩下就是把它改成多個的情況。

解決 Promise.all 失敗被 catch 的問題

const wait = new Promise((resolve, reject) => {
        setTimeout(() => {
                reject('reject err');
        }, 1000)
})

const waitArray = [];
waitArray.push(wait);
waitArray.push(wait);

const catchPromiseMap = (promises) => {
        return promises.map(promise => {
                return promise.then(res => res).catch(err => err);
        });
}

const promiseArray = catchPromiseMap(waitArray);
Promise.all(promiseArray).then(res => {
        console.log(`res: ${res}`);
}).catch(err => {
        console.log(`err: ${err}`);
});

來執行一下吧!

clarence ~ $ node index.js
res: reject err,reject err

OK! 太棒了!成功拉 ~ 我們終於實現一個使用 Promise all,但是不會因為 catch 抓到而失敗的方法了!

參考資料