Apakah ada perbedaan antara:
const [result1, result2] = await Promise.all([task1(), task2()]);
dan
const t1 = task1();
const t2 = task2();
const result1 = await t1;
const result2 = await t2;
dan
const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];
javascript
async-await
Tersembunyi
sumber
sumber
Jawaban:
Untuk keperluan jawaban ini saya akan menggunakan beberapa metode contoh:
res(ms)
adalah fungsi yang mengambil bilangan bulat milidetik dan mengembalikan janji yang diselesaikan setelah banyak milidetik.rej(ms)
adalah fungsi yang mengambil bilangan bulat milidetik dan mengembalikan janji yang menolak setelah itu banyak milidetik.Panggilan
Contoh 1res
memulai penghitung waktu. MenggunakanPromise.all
untuk menunggu beberapa penundaan akan menyelesaikan setelah semua penundaan selesai, tetapi ingat mereka mengeksekusi pada saat yang sama:Tampilkan cuplikan kode
Ini berarti bahwa
Promise.all
akan menyelesaikan dengan data dari janji-janji batin setelah 3 detik.Tetapi,
Contoh # 2Promise.all
memiliki perilaku "gagal cepat" :Tampilkan cuplikan kode
Jika Anda menggunakan
Contoh # 3async-await
sebagai gantinya, Anda harus menunggu setiap janji untuk diselesaikan secara berurutan, yang mungkin tidak seefisien:Tampilkan cuplikan kode
sumber
unhandledrejection
kesalahan. Anda tidak akan pernah ingin menggunakan ini. Harap tambahkan ini ke jawaban Anda.Perbedaan pertama - gagal cepat
Saya setuju dengan jawaban @ zzzzBov tetapi keuntungan "gagal cepat" dari Promise.all bukan hanya satu-satunya perbedaan. Beberapa pengguna dalam komentar bertanya mengapa harus menggunakan Promise.all ketika itu hanya lebih cepat dalam skenario negatif (ketika beberapa tugas gagal). Dan saya bertanya mengapa tidak? Jika saya memiliki dua tugas paralel async independen dan yang pertama diselesaikan dalam waktu yang sangat lama tetapi yang kedua ditolak dalam waktu yang sangat singkat mengapa meninggalkan pengguna menunggu pesan kesalahan "waktu sangat lama" dan bukan "waktu yang sangat singkat"? Dalam aplikasi kehidupan nyata kita harus mempertimbangkan skenario negatif. Tapi OK - dalam perbedaan pertama ini Anda dapat memutuskan alternatif mana yang akan digunakan untuk Promise.all vs. beberapa menunggu.
Perbedaan kedua - penanganan kesalahan
Tetapi ketika mempertimbangkan penanganan kesalahan ANDA HARUS menggunakan Promise.all. Tidak mungkin menangani kesalahan tugas paralel async yang dipicu dengan beberapa penantian dengan benar. Dalam skenario negatif Anda akan selalu berakhir dengan
UnhandledPromiseRejectionWarning
danPromiseRejectionHandledWarning
meskipun Anda menggunakan coba / tangkap di mana saja. Karena itulah Promise.all dirancang. Tentu saja seseorang bisa mengatakan bahwa kita dapat menekan kesalahan itu menggunakanprocess.on('unhandledRejection', err => {})
danprocess.on('rejectionHandled', err => {})
tetapi itu bukan praktik yang baik. Saya menemukan banyak contoh di internet yang tidak mempertimbangkan penanganan kesalahan untuk dua atau lebih tugas paralel async independen sama sekali atau menganggapnya tetapi dengan cara yang salah - hanya menggunakan try / catch dan berharap itu akan menangkap kesalahan. Hampir tidak mungkin menemukan latihan yang baik. Itu sebabnya saya menulis jawaban ini.Ringkasan
Jangan pernah menggunakan beberapa penantian untuk dua atau lebih tugas paralel async independen karena Anda tidak akan dapat menangani kesalahan dengan serius. Selalu gunakan Promise.all () untuk kasus penggunaan ini. Async / menunggu bukan pengganti untuk Janji. Ini hanya cara bagaimana menggunakan janji ... kode async ditulis dalam gaya sinkronisasi dan kita dapat menghindari banyak
then
janji.Beberapa orang mengatakan bahwa menggunakan Promise.all () kami tidak dapat menangani kesalahan tugas secara terpisah tetapi hanya kesalahan dari janji yang ditolak pertama (ya, beberapa kasus penggunaan mungkin memerlukan penanganan terpisah misalnya untuk penebangan). Itu bukan masalah - lihat "Tambahan" menuju ke bawah.
Contohnya
Pertimbangkan tugas async ini ...
Saat Anda menjalankan tugas dalam skenario positif, tidak ada perbedaan antara Promise.all dan beberapa menunggu. Kedua contoh berakhir dengan
Task 1 succeed! Task 2 succeed!
setelah 5 detik.Ketika tugas pertama mengambil 10 detik dalam skenario positif dan tugas detik membutuhkan 5 detik dalam skenario negatif ada perbedaan dalam kesalahan yang dikeluarkan.
Kita seharusnya sudah memperhatikan di sini bahwa kita melakukan sesuatu yang salah ketika menggunakan banyak menunggu secara paralel. Tentu saja untuk menghindari kesalahan, kita harus menanganinya! Mari mencoba...
Seperti yang Anda lihat berhasil menangani kesalahan, kita perlu menambahkan hanya satu tangkap ke
run
fungsi dan kode dengan tangkap logika dalam panggilan balik ( gaya async ). Kita tidak perlu menangani kesalahan di dalamrun
fungsi karena fungsi async berfungsi secara otomatis - janji penolakantask
fungsi menyebabkan penolakanrun
fungsi. Untuk menghindari panggilan balik, kita dapat menggunakan gaya sinkronisasi (async / menunggu + coba / tangkap)try { await run(); } catch(err) { }
tetapi dalam contoh ini tidak mungkin karena kita tidak dapat menggunakanawait
di utas utama - itu hanya dapat digunakan dalam fungsi async (logis karena tidak ada yang ingin blokir utas). Untuk menguji apakah penanganan berfungsi dalam gaya sinkronisasi, kami dapat meneleponrun
fungsi dari fungsi async lain atau penggunaan Iife (Segera Dipanggil Fungsi Ekspresi):(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
.Ini hanya satu cara yang benar bagaimana menjalankan dua atau lebih tugas paralel async dan menangani kesalahan. Anda harus menghindari contoh di bawah ini.
Kita dapat mencoba menangani kode di atas beberapa cara ...
... tidak ada yang tertangkap karena menangani kode sinkronisasi tetapi
run
async... Apa? Kami melihat pertama bahwa kesalahan untuk tugas 2 tidak ditangani dan kemudian yang tertangkap. Menyesatkan dan masih penuh kesalahan di konsol. Tidak bisa digunakan dengan cara ini.
... sama seperti di atas. Pengguna @Qwerty dalam jawaban yang dihapusnya bertanya tentang perilaku aneh yang tampaknya ditangkap tetapi ada juga kesalahan yang tidak ditangani. Kami menangkap kesalahan karena run () ditolak secara online dengan kata kunci yang menunggu dan dapat ditangkap menggunakan try / catch saat memanggil run (). Kami juga mendapatkan kesalahan tidak tertangani karena kami memanggil fungsi tugas async secara sinkron (tanpa menunggu kata kunci) dan tugas ini berjalan di luar fungsi run () dan juga gagal di luar. Hal ini mirip ketika kita tidak mampu menangani kesalahan dengan mencoba / menangkap saat memanggil beberapa fungsi sync bagian mana dari berjalan kode setTimeout ...
function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }
.... "hanya" dua kesalahan (yang ketiga hilang) tetapi tidak ada yang tertangkap.
Penambahan (menangani kesalahan tugas secara terpisah dan juga kesalahan gagal pertama)
... perhatikan bahwa dalam contoh ini saya menggunakan negativeScenario = true untuk kedua tugas untuk demonstrasi yang lebih baik apa yang terjadi (
throw err
digunakan untuk memecat kesalahan terakhir)sumber
Secara umum, menggunakan
Promise.all()
permintaan berjalan "async" secara paralel. Menggunakanawait
dapat berjalan secara paralel ATAU menjadi "sinkronisasi" pemblokiran.fungsi test1 dan test2 di bawah ini menunjukkan cara
await
menjalankan async atau sinkronisasi.test3 menunjukkan
Promise.all()
itu async.jsfiddle dengan hasil waktunya - buka konsol browser untuk melihat hasil tes
Perilaku sinkronisasi . TIDAK berjalan secara paralel, membutuhkan waktu ~ 1800 ms :
Perilaku async . Berjalan secara paralel, membutuhkan ~ 600 ms :
Perilaku async . Berjalan secara paralel, membutuhkan ~ 600 ms :
TLDR; Jika Anda menggunakannya
Promise.all()
juga akan "gagal cepat" - berhenti berjalan pada saat kegagalan pertama dari salah satu fungsi yang disertakan.sumber
Anda dapat memeriksa sendiri.
Dalam biola ini , saya menjalankan tes untuk menunjukkan sifat pemblokiran
await
, yang bertentangan denganPromise.all
yang akan memulai semua janji dan sementara satu menunggu itu akan berlanjut dengan yang lain.sumber
t1 = task1(); t2 = task2()
dan kemudian menggunakanawait
setelahnya untuk keduanyaresult1 = await t1; result2 = await t2;
seperti dalam pertanyaannya, sebagai lawan dari apa yang Anda uji yang menggunakanawait
pada panggilan asli sepertiresult1 = await task1(); result2 = await task2();
. Kode dalam pertanyaannya memang memulai semua janji sekaligus. Perbedaannya, seperti yang ditunjukkan oleh jawabannya, adalah bahwa kegagalan akan dilaporkan lebih cepat denganPromise.all
caranya.Dalam hal menunggu Promise.all ([task1 (), task2 ()]); "task1 ()" dan "task2 ()" akan berjalan paralel dan akan menunggu sampai kedua janji selesai (baik diselesaikan atau ditolak). Sedangkan dalam hal
t2 hanya akan berjalan setelah t1 selesai dieksekusi (telah diselesaikan atau ditolak). Baik t1 dan t2 tidak akan berjalan paralel.
sumber