AD

JavaScript में Async/Await की 4 सबसे आम गलतियां और उन्हें ठीक करने के प्रैक्टिकल तरीके

JavaScript में Async/Await की 4 सबसे आम गलतियां और उन्हें ठीक करने के प्रैक्टिकल तरीके

JavaScript वेब डेवलपमेंट की रीढ़ है, लेकिन इसकी असिंक्रोनस (Asynchronous) प्रकृति कई डेवलपर्स के लिए सिरदर्द बन जाती है। जब हम API से डेटा फेच करते हैं, डेटाबेस क्वेरी चलाते हैं या फाइल रीड करते हैं, तो अक्सर कोड वैसा व्यवहार नहीं करता जैसा हम सोचते हैं। कंसोल में Promise { <pending> } दिखना, लूप का आधा-अधूरा चलना या सर्वर का अचानक क्रैश हो जाना बेहद आम समस्याएं हैं।

अगर आपके कोड में भी डेटा गायब हो रहा है या API कॉल्स अपेक्षा से बहुत धीमी चल रही हैं, तो यह गाइड आपके लिए है। इस आर्टिकल में हम JavaScript में Async/Await और Promises से जुड़ी 4 बड़ी समस्याओं, उनके कारणों और हैंड्स-ऑन कोड उदाहरणों के साथ फिक्स करने के स्टेप्स समझेंगे।

समस्या 1: डेटा की जगह Promise { <pending> } या undefined मिलना

यह सबसे आम समस्या है। आप किसी असिंक्रोनस फंक्शन से यूजर डेटा मंगाते हैं, लेकिन जब उसे प्रिंट करते हैं, तो डेटा के बजाय Promise { <pending> } दिखाई देता है या वेरिएबल undefined रह जाता है।

संभावित कारण

JavaScript का इंजन जब किसी async फंक्शन को देखता है, तो वह तुरंत एक 'प्रॉमिस' लौटा देता है। अगर कॉलिंग फंक्शन के आगे आपने await नहीं लगाया, तो कोड डेटा आने का इंतज़ार किए बिना अगली लाइन पर बढ़ जाता है।

गलत कोड उदाहरण:

async function getUserData() {
    return { id: 101, name: "राहुल शर्मा" };
}

function displayUser() {
    const user = getUserData(); // गलती: यहां await गायब है
    console.log(user); // आउटपुट: Promise { <pending> }
}
displayUser();

समाधान और स्टेप-बाय-स्टेप फिक्स

  1. कॉलर फंक्शन को भी async बनाएं।
  2. असिंक्रोनस कॉल के आगे await कीवर्ड जोड़ें ताकि JavaScript प्रॉमिस के रिजॉल्व होने का इंतजार करे।

सही कोड उदाहरण:

async function getUserData() {
    return { id: 101, name: "राहुल शर्मा" };
}

async function displayUser() {
    const user = await getUserData(); // सही तरीका
    console.log(user.name); // आउटपुट: राहुल शर्मा
}
displayUser();

समस्या 2: forEach लूप में Async/Await का काम न करना

डेवलपर्स अक्सर एरे (Array) के हर एलिमेंट पर असिंक्रोनस ऑपरेशन करने के लिए .forEach() का इस्तेमाल करते हैं। लेकिन नतीजा यह होता है कि लूप के अंदर का काम खत्म होने से पहले ही बाकी कोड एग्जीक्यूट हो जाता है।

संभावित कारण

JavaScript का इन-बिल्ट forEach मेथड कॉलबैक फंक्शन के प्रॉमिस को रिटर्न नहीं करता और न ही उसके पूरा होने का इंतज़ार करता है। यह सिंक्रोनस तरीके से लूप चला देता है, जिससे रेस कंडीशन (Race Condition) बन जाती है।

गलत कोड उदाहरण:

const userIds = [1, 2, 3];

async function fetchAllUsers() {
    userIds.forEach(async (id) => {
        const data = await fakeApiCall(id);
        console.log(`Fetched: ${id}`);
    });
    console.log("सभी यूजर्स प्रोसेस हो गए!"); // यह लाइन पहले प्रिंट हो जाएगी!
}

समाधान और स्टेप-बाय-स्टेप फिक्स

अगर आपको आइटम्स को एक-एक करके (सीक्वेंशियल) प्रोसेस करना है, तो पारंपरिक for...of लूप का इस्तेमाल करें। अगर समानांतर (पैरेलल) चलाना है, तो Promise.all() के साथ map() का उपयोग करें।

सही कोड (for...of लूप):

async function fetchAllUsersSequential() {
    for (const id of userIds) {
        const data = await fakeApiCall(id);
        console.log(`Fetched: ${id}`);
    }
    console.log("सभी यूजर्स क्रम से प्रोसेस हो गए!");
}

सही कोड (Promise.all - तेज तरीका):

async function fetchAllUsersParallel() {
    const promises = userIds.map(id => fakeApiCall(id));
    await Promise.all(promises);
    console.log("सभी यूजर्स एक साथ प्रोसेस हो गए!");
}

समस्या 3: UnhandledPromiseRejection से एप्लिकेशन क्रैश होना

जब नेटवर्क एरर आता है या API डाउन होती है, तो Node.js बैकएंड में अक्सर UnhandledPromiseRejectionWarning एरर आता है और पूरा सर्वर बंद पड़ जाता है। फ्रंटएंड में यूजर इंटरफेस फ्रीज हो जाता है।

संभावित कारण

असिंक्रोनस कोड में जब प्रॉमिस रिजेक्ट होता है (जैसे नेटवर्क फेलियर), और उसे पकड़ने के लिए कोई एरर हैंडलर नहीं होता, तो रनटाइम अनहैंडल्ड एक्सेप्शन थ्रो कर देता है।

समाधान और स्टेप-बाय-स्टेप फिक्स

  1. हमेशा await वाले कोड ब्लॉक को try...catch ब्लॉक के अंदर लपेटें।
  2. एरर को केवल कंसोल में प्रिंट न करें, बल्कि यूजर के लिए सुरक्षित फॉलबैक (Fallback) डेटा या स्टेटस कोड लौटाएं।

सही कोड उदाहरण:

async function loadProductDetails(productId) {
    try {
        const response = await fetch(`https://api.example.com/products/${productId}`);
        
        if (!response.ok) {
            throw new Error(`HTTP एरर! स्टेटस: ${response.status}`);
        }
        
        const product = await response.json();
        return product;
    } catch (error) {
        console.error("डेटा लोड करने में विफलता:", error.message);
        // यूजर को क्रैश से बचाने के लिए सुरक्षित डिफॉल्ट ऑब्जेक्ट दें
        return { id: productId, title: "अनुपलब्ध", error: true };
    }
}

समस्या 4: वॉटरफॉल स्लोडाउन (Waterfall Effect) - गैर-जरूरी धीमापन

आपकी वेब ऐप का लोड टाइम अचानक 3 से 4 गुना बढ़ जाता है, जबकि डेटाबेस या सर्वर पर कोई भारी लोड नहीं होता। यह अक्सर खराब असिंक्रोनस आर्किटेक्चर के कारण होता है।

संभावित कारण

जब दो या तीन API कॉल्स पूरी तरह से स्वतंत्र (Independent) होती हैं, फिर भी डेवलपर उन्हें एक के बाद एक await लगाकर कॉल करते हैं। इसे "Async Waterfall" कहते हैं। पहली कॉल खत्म होने के बाद ही दूसरी शुरू होती है, जिससे कुल समय जुड़ता चला जाता है।

गलत कोड उदाहरण:

async function getDashboardData() {
    // अगर हर कॉल 1 सेकंड लेती है, तो कुल समय = 3 सेकंड
    const profile = await fetchProfile(); 
    const orders = await fetchOrders();   
    const notifications = await fetchNotifications(); 
    
    return { profile, orders, notifications };
}

समाधान और स्टेप-बाय-स्टेप फिक्स

जब कॉल्स एक-दूसरे पर निर्भर न हों, तो उन्हें समानांतर (concurrently) शुरू करें और Promise.all() का प्रयोग करें।

सही कोड उदाहरण:

async function getDashboardDataOptimized() {
    // तीनों कॉल्स एक ही समय पर शुरू होंगी (कुल समय = लगभग 1 सेकंड)
    const [profile, orders, notifications] = await Promise.all([
        fetchProfile(),
        fetchOrders(),
        fetchNotifications()
    ]);
    
    return { profile, orders, notifications };
}

डीबगिंग के लिए त्वरित चेकलिस्ट (Async Troubleshooting Checklist)

  • रिटर्न टाइप चेक करें: क्या आपका फंक्शन प्रॉमिस रिटर्न कर रहा है? यदि हां, तो कॉलर के पास await होना जरूरी है।
  • लूप्स में सावधानी: forEach और filter में कभी भी await पर भरोसा न करें; for...of या map + Promise.all चुनें।
  • नेटवर्क फॉलबैक: हर एक्सटर्नल API कॉल के चारों ओर try...catch मौजूद होना चाहिए।
  • पैरेलेलिज्म: जो डेटा एक-दूसरे पर निर्भर नहीं है, उसे सीक्वेंशियल await में बांधकर ऐप धीमी न बनाएं।

अक्सर पूछे जाने वाले सवाल (FAQs)

1. क्या हर फंक्शन के आगे 'async' लगाना सुरक्षित है?

नहीं। जिस फंक्शन में कोई असिंक्रोनस ऑपरेशन नहीं है, उसके आगे async लगाने से वह व्यर्थ में आउटपुट को प्रॉमिस में लपेट देता है, जिससे मेमोरी और कॉल स्टैक पर मामूली ओवरहेड बढ़ता है।

2. Promise.all() और Promise.allSettled() में क्या अंतर है?

Promise.all() में अगर एक भी प्रॉमिस फेल हो जाता है, तो पूरा ऑपरेशन रिजेक्ट हो जाता है। इसके विपरीत, Promise.allSettled() सभी प्रॉमिस के पूरे होने (चाहे वे सफल हों या फेल) का इंतज़ार करता है और हर एक का अलग स्टेटस देता है।

3. क्या हम टॉप-लेवल (बिना किसी फंक्शन के) await का उपयोग कर सकते हैं?

हाँ, आधुनिक JavaScript (ES2022 और बाद में) तथा आधुनिक Node.js वर्जन में ES मॉड्यूल्स (import/export) का उपयोग करते समय आप सीधे टॉप-लेवल await लिख सकते हैं।

Post a Comment

0 Comments