İlk fark - hızlı başarısız
@ ZzzzBov'un cevabına katılıyorum ama Promise.all'ın "hızlı başarısız" avantajı sadece tek fark değil. Yorumlardaki bazı kullanıcılar, Promise.all dosyasını yalnızca negatif senaryoda daha hızlı olduğunda (bazı görevler başarısız olduğunda) neden kullanacaklarını sorar. Neden sormuyorum? İki bağımsız eşzamansız paralel görevim varsa ve birincisi çok uzun bir sürede çözülüyor ancak ikincisi çok kısa sürede reddediliyorsa, neden "çok kısa bir süre" yerine "çok uzun bir süre" yerine hata mesajını bekletmek? Gerçek hayattaki uygulamalarda olumsuz senaryoyu düşünmeliyiz. Ama Tamam - bu ilk farkta Promise.all ile çoklu alternatifin hangi alternatifi kullanacağınıza karar verebilirsiniz.
İkinci fark - hata işleme
Ancak hata işlemeyi düşünürken Promise.all kullanmalısınız. Birden fazla beklemeyle tetiklenen zaman uyumsuz paralel görevlerin hatalarını doğru işlemek mümkün değildir. Negatif senaryoda her zamanUnhandledPromiseRejectionWarning
ve PromiseRejectionHandledWarning
her yerde dene / yakala seçeneğini kullanırsınız. Bu nedenle Promise.all tasarlandı. Tabii ki birisi bu hataları kullanarak baskılayabileceğimizi söyleyebilir process.on('unhandledRejection', err => {})
ve process.on('rejectionHandled', err => {})
bu iyi bir uygulama değildir. İnternette, iki veya daha fazla bağımsız asenkron paralel görev için hata işlemeyi dikkate almayan veya hiç düşünmeyen ancak yanlış bir şekilde - sadece dene / yakala ve hata yakalamayı umarak kullanmayan birçok örnek buldum. İyi uygulama bulmak neredeyse imkansız. Bu yüzden bu cevabı yazıyorum.
özet
Asla iki veya daha fazla bağımsız zaman uyumsuz paralel görev için birden fazla bekleme kullanmayın, çünkü hataları ciddiye alamazsınız. Bu kullanım durumu için her zaman Promise.all () kullanın.
Async / await, Promises'ın yerini almaz. Bu vaatleri kullanmak için güzel bir yol ... zaman uyumsuz kod yazılmıştır senkronize tarzda ve then
vaatlerde birden fazla önleyebilirsiniz .
Bazı insanlar Promise.all () kullanarak görev hatalarını ayrı ayrı ele alamayacağımızı ancak sadece ilk reddedilen vaatten kaynaklanan hatayı (evet, bazı kullanım durumlarının örneğin günlük kaydı için ayrı işlem gerektirebileceğini) söylüyorlar. Sorun değil - aşağıdaki "Ekleme" başlığına bakın.
Örnekler
Bu zaman uyumsuz görevi düşünün ...
const task = function(taskNum, seconds, negativeScenario) {
return new Promise((resolve, reject) => {
setTimeout(_ => {
if (negativeScenario)
reject(new Error('Task ' + taskNum + ' failed!'));
else
resolve('Task ' + taskNum + ' succeed!');
}, seconds * 1000)
});
};
Olumlu senaryoda görevleri çalıştırdığınızda Promise.all ve birden çok bekleme arasında bir fark yoktur. Her iki örnek de Task 1 succeed! Task 2 succeed!
5 saniye sonra sona erer .
// Promise.all alternative
const run = async function() {
// tasks run immediate in parallel and wait for both results
let [r1, r2] = await Promise.all([
task(1, 5, false),
task(2, 5, false)
]);
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!
// multiple await alternative
const run = async function() {
// tasks run immediate in parallel
let t1 = task(1, 5, false);
let t2 = task(2, 5, false);
// wait for both results
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!
Pozitif senaryoda ilk görev 10 saniye, negatif senaryoda ise görev 5 saniye sürdüğünde, verilen hatalarda farklılıklar vardır.
// Promise.all alternative
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, false),
task(2, 5, true)
]);
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// multiple await alternative
const run = async function() {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
Paralel olarak çoklu beklemeyi kullanırken yanlış bir şey yaptığımızı zaten fark etmeliyiz. Tabii ki hataları önlemek için biz halletmeliyiz! Hadi deneyelim...
// Promise.all alternative
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, false),
task(2, 5, true)
]);
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: Caught error Error: Task 2 failed!
Hatayı başarılı bir şekilde ele alabileceğiniz gibi, run
fonksiyona sadece bir catch eklememiz gerekiyor ve catch mantığı ile kod geri çağırmada ( async stili ). run
İşlevin içindeki tutamaç hatalarına ihtiyacımız yoktur çünkü asenkron işlev otomatik olarak çalışır - task
işlevin reddedileceğine söz verin, işlevin reddedilmesine neden olur run
. Geri aramadan kaçınmak için senkronizasyon stilini (async / await + try / catch) kullanabiliriz, try { await run(); } catch(err) { }
ancak bu örnekte await
ana iş parçacığında kullanamadığımız için mümkün değildir - sadece async işlevinde kullanılabilir (mantıksaldır, çünkü kimse istemez ana ipliği engelle). İşlemin senkronize tarzda çalışıp çalışmadığını test etmek için arayabilirizrun
Başka bir zaman uyumsuz fonksiyonu ya da kullanım hayat (hemen kullanılan fonksiyonu ifade) gelen fonksiyonu: (async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
.
Bu, iki veya daha fazla eşzamansız paralel görevi çalıştırmak ve hataları işlemek için doğru bir yoldur. Aşağıdaki örneklerden kaçınmalısınız.
// multiple await alternative
const run = async function() {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
Kodu birkaç yolla ele almaya çalışabiliriz ...
try { run(); } catch(err) { console.log('Caught error', err); };
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled
... hiçbir şey yakalanmadı çünkü senkronizasyon kodunu run
işliyor
ancak senkronize değil
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... O ne lan? Öncelikle görev 2'deki hatanın ele alınmadığını ve daha sonra yakalandığını görüyoruz. Yanıltıcı ve hala konsoldaki hatalarla dolu. Bu şekilde kullanılamaz.
(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... yukarıdaki ile aynı. User @Qwerty silinen cevabında yakalanmış gibi görünen bu tuhaf davranışı sordu ancak işlenmemiş hatalar da var. Run () anahtar kelimeyi bekleyen satırda reddedildiğinden ve run () çağrılırken try / catch kullanılarak yakalanabildiğinden hatayı yakalarız. Ayrıca, zaman uyumsuz görev işlevini eşzamanlı olarak çağırıyoruz (anahtar sözcüğü beklemeden) ve bu görev run () işlevinin dışında çalışıyor ve dışarıda da başarısız oluyor. SetTimeout'ta kodun hangi bölümünün çalıştığını bazı senkronizasyon işlevini çağırırken try / catch ile hata işleyemediğimizde benzerdir function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }
.
const run = async function() {
try {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
}
catch (err) {
return new Error(err);
}
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... "sadece" iki hata (3. hata eksik) ama hiçbir şey yakalanmadı.
Ekleme (görev hatalarını ayrı olarak ele alma ve ayrıca ilk başarısızlık hatası)
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, true).catch(err => { console.log('Task 1 failed!'); throw err; }),
task(2, 5, true).catch(err => { console.log('Task 2 failed!'); throw err; })
]);
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Run failed (does not matter which task)!'); });
// at 5th sec: Task 2 failed!
// at 5th sec: Run failed (does not matter which task)!
// at 10th sec: Task 1 failed!
... bu örnekte, ne olduğunu daha iyi göstermek için her iki görev için de negatifScenario = true kullandığımı unutmayın ( throw err
son hatayı tetiklemek için kullanılır)