पहला अंतर - तेजी से विफल
मैं @ zzzzBov के उत्तर से सहमत हूं लेकिन Promise.all का "तेजी से विफल" लाभ केवल एक अंतर नहीं है। टिप्पणियों में कुछ उपयोगकर्ता पूछते हैं कि क्यों Promise.all का उपयोग करें जब यह केवल नकारात्मक परिदृश्य में तेज होता है (जब कुछ कार्य विफल हो जाता है)। और मैं पूछता हूं क्यों नहीं? अगर मेरे पास दो स्वतंत्र एसिंक्स समानांतर कार्य हैं और पहले एक को बहुत लंबे समय में हल किया जाता है, लेकिन दूसरे को बहुत कम समय में खारिज कर दिया जाता है तो उपयोगकर्ता को "बहुत कम समय" के बजाय "बहुत लंबे समय" त्रुटि के लिए इंतजार करना क्यों छोड़ना है? वास्तविक जीवन के अनुप्रयोगों में हमें नकारात्मक परिदृश्य पर विचार करना चाहिए। लेकिन ठीक है - इस पहले अंतर में आप यह तय कर सकते हैं कि Promise.all बनाम मल्टीपल वेट का उपयोग करने के लिए कौन सा विकल्प है।
दूसरा अंतर - त्रुटि से निपटने
लेकिन जब त्रुटि से निपटने पर विचार करना होगा तो आप Promise.all का उपयोग करें। कई प्रतीक्षा के साथ ट्रिगर किए गए async समानांतर कार्यों की त्रुटियों को सही ढंग से संभालना संभव नहीं है। नकारात्मक परिदृश्य में आप हमेशा साथ रहेंगे UnhandledPromiseRejectionWarning
औरPromiseRejectionHandledWarning
यद्यपि आप कहीं भी कोशिश / पकड़ का उपयोग करेंगे। यही कारण है कि Promise.all डिजाइन किया गया था। बेशक किसी को कह सकते हैं कि हम प्रयोग कर कि त्रुटियों को दबाने कर सकते हैं process.on('unhandledRejection', err => {})
और process.on('rejectionHandled', err => {})
लेकिन यह अच्छा अभ्यास नहीं है। मुझे इंटरनेट पर ऐसे कई उदाहरण मिले जो दो या दो से अधिक स्वतंत्र async समानांतर कार्यों के लिए त्रुटि से निपटने पर विचार नहीं करते हैं या इसे गलत तरीके से देखते हैं - बस कोशिश / पकड़ का उपयोग करना और उम्मीद करना कि यह त्रुटियों को पकड़ लेगा। अच्छा अभ्यास पाना लगभग असंभव है। इसलिए मैं यह उत्तर लिख रहा हूं।
सारांश
कभी भी दो या दो से अधिक स्वतंत्र async समानांतर कार्यों के लिए एकाधिक प्रतीक्षा का उपयोग न करें क्योंकि आप त्रुटियों को गंभीरता से संभाल नहीं पाएंगे। हमेशा इस उपयोग के मामले के लिए Promise.all () का उपयोग करें।
Async / प्रतीक्षा प्रॉमिस के लिए प्रतिस्थापन नहीं है। यह सिर्फ सुंदर तरीका है कि वादों का उपयोग कैसे किया जाता है ... एसिंक्स कोड सिंक शैली में लिखा गया है और हम then
वादों में कई से बच सकते हैं ।
कुछ लोग कहते हैं कि Promise.all () का उपयोग करके हम अलग-अलग कार्य त्रुटियों को संभाल नहीं सकते हैं, लेकिन केवल पहले खारिज किए गए वादे से त्रुटि (हाँ, कुछ उपयोग मामलों को लॉगिंग के लिए अलग से हैंडलिंग की आवश्यकता हो सकती है)। यह समस्या नहीं है - नीचे "जोड़" देखें।
उदाहरण
इस async कार्य पर विचार करें ...
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)
});
};
जब आप सकारात्मक परिदृश्य में कार्य चलाते हैं तो Promise.all और एकाधिक प्रतीक्षा के बीच कोई अंतर नहीं होता है। Task 1 succeed! Task 2 succeed!
5 सेकंड के बाद दोनों उदाहरण समाप्त होते हैं ।
// 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!
जब पहला कार्य सकारात्मक परिदृश्य में 10 सेकंड लेता है और सेकंड कार्य नकारात्मक परिदृश्य में 5 सेकंड लेता है तो जारी की गई त्रुटियों में अंतर होता है।
// 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!
हमें पहले से ही यहां ध्यान देना चाहिए कि समानांतर में कई प्रतीक्षा का उपयोग करते समय हम कुछ गलत कर रहे हैं। बेशक त्रुटियों से बचने के लिए हमें इसे संभालना चाहिए! कोशिश करते हैं...
// 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!
जैसा कि आप सफलतापूर्वक त्रुटि को देखने के लिए देख सकते हैं कि हमें run
फ़ंक्शन में केवल एक कैच जोड़ने की आवश्यकता है और कैच लॉजिक के साथ कोड कॉलबैक ( async शैली ) में है। हमें run
फ़ंक्शन के अंदर हैंडल त्रुटियों की आवश्यकता नहीं है क्योंकि async फ़ंक्शन यह स्वचालित रूप से करता है - task
फ़ंक्शन की अस्वीकृति का कारण फ़ंक्शन की अस्वीकृति का कारण बनता है run
। कॉलबैक से बचने के लिए हम सिंक स्टाइल (async / wait + try / catch) का उपयोग कर सकते हैं try { await run(); } catch(err) { }
लेकिन इस उदाहरण में यह संभव नहीं है क्योंकि हम await
मुख्य धागे में उपयोग नहीं कर सकते हैं - इसका उपयोग केवल async फ़ंक्शन में किया जा सकता है (यह तर्कसंगत है क्योंकि कोई भी नहीं चाहता है ब्लॉक मुख्य धागा)। यह देखने के लिए कि किसी अन्य async फ़ंक्शन से फ़ंक्शन में काम करना या IIFE का उपयोग करना (तुरंत इनवॉइस फ़ंक्शन अभिव्यक्ति) :। सिंक शैली हम कॉल कर सकते हैंrun
(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
यह केवल एक सही तरीका है कि कैसे दो या दो से अधिक async समानांतर कार्यों को चलाएं और त्रुटियों को संभालें। आपको नीचे दिए गए उदाहरणों से बचना चाहिए।
// 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);
};
हम कई तरीकों से कोड को संभालने की कोशिश कर सकते हैं ...
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
... कुछ भी नहीं पकड़ा गया क्योंकि यह सिंक कोड को संभालता है लेकिन run
यह async है
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)
... Wtf? हम पहले देखते हैं कि कार्य 2 के लिए त्रुटि को संभाला नहीं गया था और बाद में वह पकड़ा गया था। भ्रामक और अभी भी कंसोल में त्रुटियों से भरा है। इस तरह अनुपयोगी।
(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)
... उपरोक्त के समान। उपयोगकर्ता @Qwerty ने अपने हटाए गए उत्तर में इस अजीब व्यवहार के बारे में पूछा, जो कि पकड़ा जा रहा है, लेकिन इसमें कोई त्रुटि भी है। हम त्रुटि पकड़ते हैं क्योंकि रन () प्रतीक्षित कीवर्ड के साथ लाइन पर अस्वीकार कर दिया जाता है और रन () कॉल करते समय कोशिश / पकड़ का उपयोग करके पकड़ा जा सकता है। हमें अनचाही त्रुटि भी मिलती है क्योंकि हम async कार्य फ़ंक्शन को सिंक्रोनाइज़ (बिना प्रतीक्षा किए कीवर्ड) कह रहे हैं और यह कार्य रन आउट () फ़ंक्शन से चलता है और बाहर भी विफल होता है। यह इसी तरह की है जब हम ट्राई / कैच करके त्रुटि को संभालने में सक्षम है जब कुछ सिंक समारोह जो setTimeout में कोड रन का हिस्सा बुला नहीं रहे हैं ... 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)
... "केवल" दो त्रुटियां (3 एक लापता है) लेकिन कुछ भी पकड़ा नहीं गया।
जोड़ (अलग-अलग कार्य त्रुटियों को और पहले-असफल त्रुटि)
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!
... ध्यान दें कि इस उदाहरण में मैंने नकारात्मक प्रदर्शन का इस्तेमाल किया = बेहतर प्रदर्शन के लिए दोनों कार्यों के लिए सच है कि क्या होता है ( throw err
अंतिम त्रुटि को आग लगाने के लिए उपयोग किया जाता है)