Async/await और Array.map: जब सबसे अच्छे दोस्त दुश्मन बन जाते हैं
\n\nक्या आपने कभी const results = await array.map(async (item) => {...}) जैसा कुछ लिखा है और अपेक्षित डेटा के बजाय Promise की एक सरणी प्राप्त की है? यदि हाँ — तो आप अकेले नहीं हैं। Stack Overflow पर इस प्रश्न को दसियों हज़ार बार देखा गया है, और इस गलती पर पैर रखने वाले डेवलपर्स की संख्या लाखों में है।
इस लेख में हम TypeScript में Array.map के साथ async/await का उपयोग करते समय 5 सबसे खतरनाक गलतियों का विश्लेषण करेंगे। और, इससे भी महत्वपूर्ण बात, मैं दिखाऊंगा कि उनसे कैसे बचा जाए। कमर कस लें — यह दर्दनाक होगा, लेकिन उपयोगी।
गलती #1: Promise.all को भूल जाना (सबसे आम)
\n\nसबसे आम बग यह सोचना है कि map के अंदर await जादुई रूप से सभी प्रॉमिसेज़ की प्रतीक्षा करेगा। स्पॉयलर: नहीं, यह इंतज़ार नहीं करेगा।
समस्या
\n// ❌ यह काम नहीं करता\nconst users = [1, 2, 3, 4, 5];\nconst userData = await users.map(async (id) => {\n const response = await fetch(`/api/users/${id}`);\n return response.json();\n});\n// userData — Promise[] है, UserData[] नहीं!\nconsole.log(userData); // [Promise, Promise, Promise, Promise, Promise]\n\nऐसा क्यों होता है?
\nArray.map सिंक्रोनस है। यह एसिंक्रोनस कॉलबैक के पूरा होने की प्रतीक्षा नहीं करता है। async फ़ंक्शन का प्रत्येक कॉल Promise लौटाता है, और map बस उन्हें एक सरणी में इकट्ठा करता है। map से पहले await केवल map के पूरा होने की प्रतीक्षा करता है, न कि इसके कॉलबैक की।
समाधान
\n// ✅ सही: Promise.all में लपेटें\nconst users = [1, 2, 3, 4, 5];\nconst userData = await Promise.all(\n users.map(async (id) => {\n const response = await fetch(`/api/users/${id}`);\n return response.json();\n })\n);\n// userData — UserData[]! 🎉\nconsole.log(userData);\n\nगलती #2: सभी प्रॉमिसेज़ समानांतर में निष्पादित होते हैं (जबकि अनुक्रमिक होना चाहिए)
\n\nPromise.all सभी प्रॉमिसेज़ को समानांतर में चलाता है। यह प्रदर्शन के लिए अच्छा है, लेकिन कभी-कभी हमें अनुरोधों को सख्ती से एक के बाद एक करने की आवश्यकता होती है। उदाहरण के लिए, ऐसे API के साथ काम करते समय जो अनुरोधों की संख्या को सीमित करता है, या जब प्रत्येक अगला अनुरोध पिछले पर निर्भर करता है।
समस्या
\n// ❌ सभी अनुरोध एक साथ जाते हैं\nconst ids = [1, 2, 3, 4, 5];\nconst results = await Promise.all(\n ids.map(async (id) => {\n // API को 5 समानांतर अनुरोध — rate limit मिल सकता है\n return await api.processItem(id);\n })\n);\n\nसमाधान: अनुक्रमिक निष्पादन
\n// ✅ await के साथ for...of का उपयोग करें\nconst ids = [1, 2, 3, 4, 5];\nconst results: ItemData[] = [];\n\nfor (const id of ids) {\n const result = await api.processItem(id);\n results.push(result);\n}\n// results — क्रम में संसाधित डेटा वाली सरणी\n\nवैकल्पिक तरीका — प्रॉमिस-एक्युमुलेटर के साथ Array.reduce:
// ✅ यह भी काम करता है, लेकिन कम पठनीय है\nconst ids = [1, 2, 3, 4, 5];\nconst results = await ids.reduce(async (accPromise, id) => {\n const acc = await accPromise;\n const result = await api.processItem(id);\n return [...acc, result];\n}, Promise.resolve([] as ItemData[]));\n\nगलती #3: TypeScript में गलत टाइपिंग
\n\nTypeScript को तुरंत समझ नहीं आ सकता है कि आप एसिंक्रोनस map के साथ क्या कर रहे हैं। खासकर यदि आप स्पष्ट प्रकार निर्दिष्ट किए बिना Promise.all का उपयोग कर रहे हैं।
समस्या
\n// ❌ TypeScript शिकायत करता है या गलत प्रकार निकालता है\nconst ids = [1, 2, 3];\nconst results = await Promise.all(\n ids.map(async (id) => {\n const data = await fetchData(id);\n return data.name; // TypeScript सोच सकता है कि यह Promise<string> है\n })\n);\n// results का प्रकार unknown या Promise<string>[] है\n\nसमाधान: स्पष्ट प्रकार एनोटेशन
\n// ✅ परिणाम का प्रकार स्पष्ट रूप से निर्दिष्ट करें