Katakanlah saya memiliki satu set Promise
s yang membuat permintaan jaringan, yang salah satunya akan gagal:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
Katakanlah saya ingin menunggu sampai semua ini selesai, terlepas dari apakah ada yang gagal. Mungkin ada kesalahan jaringan untuk sumber daya yang saya dapat hidup tanpanya, tetapi jika saya bisa mendapatkannya, saya ingin sebelum melanjutkan. Saya ingin menangani kegagalan jaringan dengan anggun.
Karena Promises.all
tidak meninggalkan ruang untuk ini, apa pola yang direkomendasikan untuk menangani ini, tanpa menggunakan perpustakaan janji?
javascript
promise
es6-promise
Nathan Hagen
sumber
sumber
allSettled
yang memenuhi kebutuhan Anda tanpa Anda harus roll sendiri.Promise.all
akan menolak segera setelah ada satu janji yang menolak, jadi idiom yang Anda usulkan tidak menjamin bahwa semua janji telah terpenuhi.Jawaban:
Perbarui, Anda mungkin ingin menggunakan bawaan bawaan
Promise.allSettled
:Sebagai fakta yang menyenangkan, jawaban di bawah ini merupakan penemuan sebelumnya dalam menambahkan metode itu ke bahasa:]
Tentu, Anda hanya perlu
reflect
:Atau dengan ES5:
Atau dalam contoh Anda:
sumber
reflect
merupakan kata yang umum dalam ilmu komputer? Bisakah Anda tautkan ke tempat ini dijelaskan seperti di wikipedia atau sesuatu. Saya mencari dengan susah payahPromise.all not even first reject
tetapi tidak tahu untuk mencari "Renungkan". Haruskah ES6 memilikiPromise.reflect
yang seperti "Janji. Semua tapi benar-benar semua"?Jawaban serupa, tetapi lebih idiomatis untuk ES6 mungkin:
Bergantung pada jenis nilai yang dikembalikan, kesalahan sering kali dapat dibedakan dengan cukup mudah (mis. Penggunaan
undefined
untuk "tidak peduli",typeof
untuk nilai bukan objek biasaresult.message
,result.toString().startsWith("Error:")
dll.)sumber
.map(p => p.catch(e => e))
bagian mengubah semua penolakan menjadi nilai yang diselesaikan, jadiPromise.all
masih menunggu semuanya selesai apakah fungsi individu menyelesaikan atau menolak, terlepas dari berapa lama waktu yang diperlukan. Cobalah..catch(e => console.log(e));
tidak pernah dipanggil karena ini tidak pernah gagalcatch
umumnya adalah praktik yang baik IMHO .e
dan mengembalikannya sebagai nilai reguler (sukses). Sama sepertip.catch(function(e) { return e; })
hanya lebih pendek.return
tersirat.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.Mengambil langkah ini lebih jauh, Anda dapat menulis penangan tangkapan generik yang terlihat seperti ini:
maka kamu bisa melakukannya
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:
Jadi sekarang Anda bisa melakukan ini:
Kemudian untuk tetap KERING, Anda mendapatkan jawaban Benjamin:
di mana sekarang terlihat seperti
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:
Ini tidak akan ketahuan
a.catch
, jadiTidak 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:
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.
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
.sumber
Promise
s. Sementarareflect
kode meningkatkan Anda digunakan kembali, itu juga menetapkan tingkat abstraksi lain. Karena jawaban Nathan sejauh ini hanya menerima sebagian kecil upvotes dibandingkan dengan jawaban Anda, saya bertanya-tanya apakah ini merupakan indikasi masalah dengan solusinya, yang belum saya kenali.new Promise((res, rej) => res(new Error('Legitimate error'))
tidakkah bisa dibedakannew Promise(((res, rej) => rej(new Error('Illegitimate error'))
? Atau lebih lanjut, Anda tidak akan dapat memfilterx.status
? Saya akan menambahkan poin ini ke jawaban saya sehingga perbedaannya lebih jelasPromise.all()
varian tertentu , itu juga kemudian menjadi kewajiban konsumen Promise untuk mengetahui bahwa janji tertentu tidak akan menolak tetapi akan menelan kesalahan itu. Bahkanreflect()
metode ini dapat dibuat kurang 'abstrak' dan lebih eksplisit dengan menyebutnyaPromiseEvery(promises).then(...)
. Kompleksitas jawaban di atas dibandingkan dengan Benjamin harus mengatakan banyak tentang solusi ini.Ada proposal selesai untuk fungsi yang dapat menyelesaikan ini secara asli, dalam vanilla Javascript
Promise.allSettled
:, yang telah membuatnya ke tahap 4, resmi di ES2020, dan diimplementasikan di semua lingkungan modern . Sangat mirip denganreflect
fungsi dalam jawaban lain ini . Ini sebuah contoh, dari halaman proposal. Sebelumnya, Anda harus melakukan:Menggunakan
Promise.allSettled
sebaliknya, di atas akan setara dengan:Mereka yang menggunakan lingkungan modern akan dapat menggunakan metode ini tanpa perpustakaan . Di dalamnya, cuplikan berikut ini harus dijalankan tanpa masalah:
Keluaran:
Untuk browser yang lebih lama, ada polyfill yang memenuhi spesifikasi di sini .
sumber
Saya benar-benar menyukai jawaban Benjamin, dan bagaimana dia pada dasarnya mengubah semua janji menjadi yang selalu menyelesaikan, tetapi kadang-kadang dengan kesalahan sebagai akibatnya. :)
Ini usaha saya atas permintaan Anda jika Anda mencari alternatif. Metode ini hanya memperlakukan kesalahan sebagai hasil yang valid, dan dikodekan mirip dengan yang
Promise.all
lain:sumber
settle
. Kami memiliki itu juga di bluebird, saya suka mencerminkan lebih baik tetapi ini adalah solusi yang layak untuk ketika Anda memiliki ini untuk sebuah array.Promise
konstruktor dengan benar (dan menghindari hal ituvar resolve
)?The
Promise.all
akan menelan janji ditolak dan menyimpan kesalahan dalam variabel, sehingga akan kembali ketika semua janji-janji telah diselesaikan. Kemudian Anda dapat membuang kesalahan keluar, atau melakukan apa pun. Dengan cara ini, saya kira Anda akan keluar dari penolakan terakhir, bukan yang pertama.sumber
err.push(error)
, jadi semua kesalahan bisa digelembungkan.Saya memiliki masalah yang sama dan telah menyelesaikannya dengan cara berikut:
Dalam hal ini
Promise.all
akan menunggu setiap Janji akan datangresolved
ataurejected
menyatakan.Dan memiliki solusi ini kami "menghentikan
catch
eksekusi" dengan cara yang tidak menghalangi. Faktanya, kami tidak menghentikan apa pun, kami hanya mengembalikan kembaliPromise
dalam keadaan tertunda yang mengembalikan yang lainPromise
ketika diselesaikan setelah batas waktu.sumber
Promise.all
. Saya mencari cara untuk mendengarkan ketika semua janji telah diajukan, tetapi tidak meminta mereka sendiri. Terima kasih.all()
melakukan itu, ia menunggu pemenuhan semua Janji atau penolakan setidaknya satu dari itu..all
semuanya terbakar.then
atau.all
) tetapi mereka berjalan ketika dibuat.Ini harus konsisten dengan bagaimana Q melakukannya :
sumber
Jawaban Benjamin Gruenbaum tentu saja hebat. Tapi saya juga bisa melihat apakah sudut pandang Nathan Hagen dengan tingkat abstraksi tampak tidak jelas. Memiliki properti objek pendek suka
e & v
juga tidak membantu, tapi tentu saja itu bisa diubah.Di Javascript ada objek Kesalahan standar, yang disebut
Error
,. Idealnya Anda selalu melempar turunan / keturunan ini. Keuntungannya adalah Anda bisa melakukannyainstanceof Error
, dan Anda tahu ada sesuatu yang salah.Jadi menggunakan ide ini, inilah pendapat saya tentang masalahnya.
Pada dasarnya menangkap kesalahan, jika kesalahan bukan dari jenis Kesalahan, bungkus kesalahan di dalam objek Kesalahan. Array yang dihasilkan akan memiliki nilai yang diselesaikan, atau objek Kesalahan yang dapat Anda periksa.
Contoh di dalam tangkapan, jika Anda menggunakan beberapa perpustakaan eksternal yang mungkin melakukannya
reject("error")
, bukanreject(new Error("error"))
.Tentu saja Anda dapat memiliki janji-janji Anda menyelesaikan kesalahan, tetapi dalam kasus itu kemungkinan besar masuk akal untuk memperlakukan sebagai kesalahan, seperti contoh terakhir menunjukkan.
Keuntungan lain melakukannya, penghancuran array dibuat sederhana.
Dari pada
Anda bisa berargumen bahwa
!error1
cek itu lebih sederhana daripada contoh, tetapi Anda juga harus merusak keduanyav & e
.sumber
Alih-alih menolak, atasi dengan objek. Anda bisa melakukan sesuatu seperti ini ketika Anda menerapkan janji
sumber
Saya pikir tawaran mengikuti pendekatan yang sedikit berbeda ... dibandingkan
fn_fast_fail()
denganfn_slow_fail()
... meskipun yang terakhir tidak gagal seperti ... Anda dapat memeriksa apakah salah satu atau keduaa
danb
merupakan contoh dariError
danthrow
bahwaError
jika Anda ingin jangkauan yangcatch
blok (misalnyaif (b instanceof Error) { throw b; }
). Lihat jsfiddle .sumber
Ini kebiasaan saya
settledPromiseAll()
Dibandingkan dengan
Promise.all
Jika semua janji terselesaikan, itu berfungsi persis seperti yang standar.
Jika satu dari lebih janji ditolak, itu mengembalikan yang pertama ditolak sama dengan yang standar tapi tidak seperti itu menunggu semua janji untuk diselesaikan / ditolak.
Untuk yang berani kita bisa berubah
Promise.all()
:HATI-HATI . Secara umum kami tidak pernah mengubah built-in, karena mungkin merusak perpustakaan JS lain yang tidak terkait atau berbenturan dengan perubahan di masa depan dengan standar JS.
My
settledPromiseall
backward compatible denganPromise.all
dan memperluas fungsinya.Orang yang mengembangkan standar - mengapa tidak memasukkan ini ke standar Janji baru?
sumber
Promise.all
dengan menggunakanasync/await
pendekatan modernsumber
Saya akan lakukan:
sumber
Anda dapat menjalankan logika Anda secara berurutan melalui nsynjs executor yang sinkron . Ini akan berhenti pada setiap janji, menunggu resolusi / penolakan, dan baik menetapkan hasil tekad ke
data
properti, atau melemparkan pengecualian (untuk menangani bahwa Anda akan perlu mencoba / menangkap blokir). Berikut ini sebuah contoh:sumber
Saya telah menggunakan kode berikut sejak ES5.
Tanda tangan penggunaannya seperti
Promise.all
. Perbedaan utama adalah bahwaPromise.wait
akan menunggu semua janji untuk menyelesaikan pekerjaan mereka.sumber
Saya tahu bahwa pertanyaan ini memiliki banyak jawaban, dan saya yakin harus (jika tidak semua) benar. Namun sangat sulit bagi saya untuk memahami logika / aliran jawaban ini.
Jadi saya melihat Implementasi Asli pada
Promise.all()
, dan saya mencoba untuk meniru logika itu - dengan pengecualian tidak menghentikan eksekusi jika satu Janji gagal.Penjelasan:
- Ulangi input
promisesList
dan jalankan setiap Janji.- Tidak masalah jika Janji terselesaikan atau ditolak: simpan hasil Janji dalam
result
array sesuai denganindex
. Simpan juga status tekad / tolak (isSuccess
).- Setelah semua Janji selesai, kembalikan satu Janji dengan hasil dari semua yang lain.
Contoh penggunaan:
sumber
Promise.all
diri Anda, ada terlalu banyak hal yang salah. Versi Anda tidak menangani input kosong misalnya.Saya tidak tahu perpustakaan janji mana yang Anda gunakan, tetapi sebagian besar memiliki sesuatu seperti allSettled .
Sunting: Oke karena Anda ingin menggunakan ES6 biasa tanpa pustaka eksternal, tidak ada metode seperti itu.
Dengan kata lain: Anda harus mengulangi janji Anda secara manual dan menyelesaikan janji gabungan baru segera setelah semua janji diselesaikan.
sumber