Kodokon kodokon.com

गहराई से एसिंक्रोनस: प्रॉमिस, async/await, एरर हैंडलिंग

इवेंट लूप का लाभ उठाएँ, अपने प्रॉमिस को सही ढंग से समानांतर चलाएँ, और एसिंक्रोनस एरर हैंडलिंग को भरोसेमंद बनाएँ।

11 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

एग्ज़ीक्यूशन मॉडल एक मैक्रोटास्क क्यू (setTimeout, I/O) और एक माइक्रोटास्क क्यू (.then, await, queueMicrotask) पर आधारित है। हर टास्क के बाद, इंजन आगे बढ़ने से पहले माइक्रोटास्क क्यू को पूरी तरह खाली करता है। सीधा नतीजा: एक हल हुआ प्रॉमिस हमेशा setTimeout(fn, 0) से पहले चलता है, और माइक्रोटास्क की जरूरत से ज़्यादा लंबी चेन रेंडरिंग को उतनी ही निश्चितता से जमा सकती है जितनी कोई सिंक्रोनस लूप।

JAVASCRIPT
console.log("start");
setTimeout(() => console.log("macrotask"), 0);
Promise.resolve().then(() => console.log("microtask"));
console.log("end");
// start, end, microtask, macrotask
माइक्रोटास्क किसी भी लंबित मैक्रोटास्क से पहले चलते हैं।

async/await प्रॉमिस के ऊपर की शक्कर है, पर यह एक परफ़ॉर्मेंस जाल छिपाता है: किसी लूप के भीतर हर await ऑपरेशन को क्रमिक बना देता है। अगर रिक्वेस्ट स्वतंत्र हैं, तो पहले उन सबको शुरू करें, फिर Promise.all के साथ पूरे बैच का इंतज़ार करें। 100 ms वाली तीन रिक्वेस्ट के लिए, क्रमिक संस्करण की लागत 300 ms है, समानांतर संस्करण की 100 ms।

JAVASCRIPT
const fetchUser = (id) => new Promise((res) => {
  setTimeout(() => res({ id }), 100);
});
async function loadSequential(ids) {
  const users = [];
  for (const id of ids) {
    users.push(await fetchUser(id)); // ~100 ms each
  }
  return users;
}
async function loadParallel(ids) {
  return Promise.all(ids.map(fetchUser)); // ~100 ms total
}
वही नतीजा, लेटेंसी कॉल की संख्या से भाग दी हुई।

अपनी ज़रूरत के फ़ेल्योर कॉन्ट्रैक्ट के लिए सही कॉम्बिनेटर चुनें: Promise.all पहली विफलता पर रिजेक्ट होता है (सब कुछ या कुछ नहीं), allSettled सबका इंतज़ार करता है और हर नतीजे का वर्णन करता है, race पहले निपटान (सफलता या विफलता, किसी टाइमआउट के लिए उपयोगी) को लेता है, any पहली सफलता लेता है और केवल तभी रिजेक्ट होता है जब सब कुछ विफल हो जाए। किसी ऐसे डैशबोर्ड के लिए जिसे आंशिक नतीजे दिखाने ही हैं, allSettled लगभग हमेशा सही चुनाव है।

JAVASCRIPT
const tasks = [
  Promise.resolve("profile loaded"),
  Promise.reject(new Error("network unavailable")),
];
const results = await Promise.allSettled(tasks);
for (const r of results) {
  if (r.status === "fulfilled") {
    console.log("success:", r.value);
  } else {
    console.error("failure:", r.reason.message);
  }
}
हर नतीजे का वर्णन होता है; कोई विफलता दूसरों को नहीं छिपाती।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. एक सिंक्रोनस console.log, एक Promise.resolve().then कॉलबैक, और एक setTimeout(fn, 0) कॉलबैक किस क्रम में चलते हैं?
    • सिंक्रोनस, setTimeout, then
    • सिंक्रोनस, then, setTimeout
    • then, सिंक्रोनस, setTimeout
  2. आप दस स्वतंत्र रिक्वेस्ट भेजते हैं। कौन-सा तरीका कुल लेटेंसी को न्यूनतम करता है?
    • for...of लूप के भीतर प्रति रिक्वेस्ट एक await
    • सभी प्रॉमिस बनाएँ, फिर Promise.all का await करें
    • .then कॉल को एक के बाद एक चेन करें
    • कॉल के बीच अंतराल देने के लिए setTimeout का उपयोग करें
  3. बिना await के try { doAsync(); } catch (e) {} doAsync के रिजेक्शन को पकड़ने में क्यों नाकाम रहता है?
    • क्योंकि catch async फंक्शन्स के भीतर काम नहीं करता
    • क्योंकि रिजेक्शन try ब्लॉक के बाहर निकल जाने के बाद होता है, चूँकि प्रॉमिस का कभी await नहीं होता
    • क्योंकि प्रॉमिस केवल .catch से ही पकड़े जा सकते हैं