Jawaban Benjamin menawarkan abstraksi yang bagus untuk menyelesaikan masalah ini, tetapi saya berharap untuk solusi yang kurang abstrak. Cara eksplisit untuk mengatasi masalah ini adalah dengan hanya memanggil .catch
janji internal, dan mengembalikan kesalahan dari panggilan balik mereka.
let a = new Promise((res, rej) => res('Resolved!')),
b = new Promise((res, rej) => rej('Rejected!')),
c = a.catch(e => { console.log('"a" failed.'); return e; }),
d = b.catch(e => { console.log('"b" failed.'); return e; });
Promise.all([c, d])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Promise.all([a.catch(e => e), b.catch(e => e)])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Mengambil langkah ini lebih jauh, Anda dapat menulis penangan tangkapan generik yang terlihat seperti ini:
const catchHandler = error => ({ payload: error, resolved: false });
maka kamu bisa melakukannya
> Promise.all([a, b].map(promise => promise.catch(catchHandler))
.then(results => console.log(results))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!', { payload: Promise, resolved: false } ]
Masalah dengan ini adalah bahwa nilai-nilai yang ditangkap akan memiliki antarmuka yang berbeda dari nilai-nilai yang tidak tertangkap, jadi untuk membersihkan ini Anda mungkin melakukan sesuatu seperti:
const successHandler = result => ({ payload: result, resolved: true });
Jadi sekarang Anda bisa melakukan ini:
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
Kemudian untuk tetap KERING, Anda mendapatkan jawaban Benjamin:
const reflect = promise => promise
.then(successHandler)
.catch(catchHander)
di mana sekarang terlihat seperti
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
Manfaat dari solusi kedua adalah abstrak dan KERING. Kelemahannya adalah Anda memiliki lebih banyak kode, dan Anda harus ingat untuk mencerminkan semua janji Anda untuk membuat semuanya konsisten.
Saya akan mencirikan solusi saya sebagai eksplisit dan KISS, tetapi memang kurang kuat. Antarmuka tidak menjamin bahwa Anda tahu persis apakah janji itu berhasil atau gagal.
Misalnya, Anda mungkin memiliki ini:
const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));
Ini tidak akan ketahuan a.catch
, jadi
> Promise.all([a, b].map(promise => promise.catch(e => e))
.then(results => console.log(results))
< [ Error, Error ]
Tidak ada cara untuk mengetahui mana yang fatal dan mana yang tidak. Jika itu penting maka Anda akan ingin menegakkan dan antarmuka yang melacak apakah itu berhasil atau tidak (yang reflect
tidak).
Jika Anda hanya ingin menangani kesalahan dengan anggun, maka Anda bisa memperlakukan kesalahan sebagai nilai yang tidak ditentukan:
> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
.then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]
Dalam kasus saya, saya tidak perlu tahu kesalahannya atau bagaimana kegagalannya - saya hanya peduli apakah saya memiliki nilai atau tidak. Saya akan membiarkan fungsi yang menghasilkan janji khawatir tentang pendataan kesalahan spesifik.
const apiMethod = () => fetch()
.catch(error => {
console.log(error.message);
throw error;
});
Dengan begitu, sisa aplikasi dapat mengabaikan kesalahannya jika diinginkan, dan memperlakukannya sebagai nilai yang tidak ditentukan jika diinginkan.
Saya ingin fungsi tingkat tinggi saya gagal dengan aman dan tidak khawatir tentang detail mengapa dependensinya gagal, dan saya juga lebih suka KISS daripada KERING ketika saya harus melakukan tradeoff - yang pada akhirnya mengapa saya memilih untuk tidak menggunakan reflect
.