आपका async/await forEach में काम क्यों नहीं करता? पहली नज़र में समझें
\n\nक्या आपने कभी ऐसा कोड लिखा है, उम्मीद करते हुए कि वह एसिंक्रोनस ऑपरेशन को क्रम से चलाएगा?
\n\nconst users = ['alice', 'bob', 'charlie'];\n\nusers.forEach(async (user) => {\n const data = await fetch(`https://api.example.com/users/${user}`);\n console.log(await data.json());\n});\n\nconsole.log('हो गया!');\n\nऔर आपको "हो गया!" डेटा लोड होने से पहले ही मिल गया? परिचित लग रहा है? आप अकेले नहीं हैं। Stack Overflow पर यह सबसे लोकप्रिय सवालों में से एक है, जिसे हज़ारों बार देखा गया है। समस्या यह है कि forEach एक सिंक्रोनस मेथड है। यह सिर्फ कॉलबैक को चलाता है, लेकिन उनके पूरा होने का इंतज़ार नहीं करता। आपके async फंक्शन शुरू होते हैं, लेकिन forEach आगे बढ़ जाता है, जैसे कोई धावक जिसे दूसरों की परवाह नहीं।
इस लेख में हम सिर्फ यह नहीं दिखाएंगे कि इसे कैसे ठीक करें। हम समझाएंगे क्यों ऐसा होता है, और आपको तीन काम के समाधान उदाहरण सहित देंगे। आप जानेंगे कि कब Promise.all इस्तेमाल करें और कब पुराना अच्छा for...of। चलिए शुरू करते हैं!
समस्या की जड़: forEach और Promises
\n\nआइए हुड के नीचे झांकते हैं। JavaScript में forEach मेथड इस तरह काम करता है:
Array.prototype.forEach = function(callback) {\n for (let i = 0; i < this.length; i++) {\n callback(this[i], i, this);\n }\n};\n\nदेखा? यह हर एलिमेंट के लिए callback को कॉल करता है, लेकिन कुछ भी रिटर्न नहीं करता। भले ही कॉलबैक Promise रिटर्न करे, forEach उसे इग्नोर कर देता है। यह इंतज़ार नहीं करता, रिजल्ट इकट्ठा नहीं करता — बस चलाता है और भूल जाता है।
जब आप async (user) => { ... } लिखते हैं, तो आपका फंक्शन Promise रिटर्न करता है। लेकिन forEach को पता नहीं कि उसके साथ क्या करना है। नतीजतन, सभी रिक्वेस्ट समानांतर में चलती हैं (अगर fetch है), लेकिन आप उनके पूरा होने का इंतज़ार नहीं कर सकते। forEach के बाद का कोड तुरंत चलता है।
वास्तविक उदाहरण: प्रैक्टिस में यह कैसा दिखता है
\n\nमान लीजिए आप प्रोडक्ट ID की एक array प्रोसेस कर रहे हैं और हर एक के लिए डेटाबेस से कीमत लानी है:
\n\nconst productIds = [101, 102, 103];\n\nasync function getPrice(id) {\n // DB क्वेरी का अनुकरण\n return new Promise(resolve => {\n setTimeout(() => {\n resolve(`${id} की कीमत: $${Math.floor(Math.random() * 100)}`);\n }, 1000);\n });\n}\n\nproductIds.forEach(async (id) => {\n const price = await getPrice(id);\n console.log(price);\n});\n\nconsole.log('सभी कीमतें लोड हो गईं!'); // 3 सेकंड के बजाय तुरंत चलेगा\n\nकंसोल में परिणाम:
\nसभी कीमतें लोड हो गईं!\n101 की कीमत: $42\n102 की कीमत: $78\n103 की कीमत: $15\n\nमानिए, यह वैसा नहीं है जैसा हमने उम्मीद की थी। अच्छी खबर: इसके सरल और सुंदर समाधान हैं।
\n\nसमाधान #1: for...of — क्रमिक निष्पादन
\n\nलूप में async/await को काम कराने का सबसे आसान तरीका है for...of का उपयोग करना। forEach के विपरीत, for...of async/await के साथ नेटिव रूप से काम करता है, क्योंकि आप ब्लॉक के अंदर एक्ज़ीक्यूशन फ्लो को कंट्रोल करते हैं।
const productIds = [101, 102, 103];\n\nasync function getPrice(id) {\n return new Promise(resolve => {\n setTimeout(() => {\n resolve(`${id} की कीमत: $${Math.floor(Math.random() * 100)}`);\n }, 1000);\n });\n}\n\nasync function loadAllPrices() {\n for (const id of productIds) {\n const price = await getPrice(id);\n console.log(price);\n }\n console.log('सभी कीमतें लोड हो गईं!');\n}\n\nloadAllPrices();\n\nअब परिणाम इस प्रकार होगा:
\n101 की कीमत: $42\n(1 सेकंड बाद)\n102 की कीमत: $78\n(1 सेकंड बाद)\n103 की कीमत: $15\n(1 सेकंड बाद)\nसभी कीमतें लोड हो गईं!\n\nफायदे: सरलता, पठनीयता, पूर्वानुमानित क्रम।
\nनुकसान: धीमा — हर रिक्वेस्ट पिछले के पूरा होने का इंतज़ार करती है।