paint-brush
JavaScript वर्धित प्रपत्रों में डुप्लिकेट फ़ेच अनुरोधों को रद्द करने के तरीके पर एक मार्गदर्शिकाद्वारा@austingil
2,570 रीडिंग
2,570 रीडिंग

JavaScript वर्धित प्रपत्रों में डुप्लिकेट फ़ेच अनुरोधों को रद्द करने के तरीके पर एक मार्गदर्शिका

द्वारा Austin Gil8m2023/02/10
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

एक अच्छा मौका है कि आपने गलती से डुप्लिकेट-अनुरोध / दौड़-स्थिति बग पेश कर दी है। आज, मैं आपको इस समस्या और इससे बचने के अपने सुझावों के बारे में बताऊँगा। यहाँ मुख्य मुद्दा `event.preventDefault()` है। यह विधि ब्राउज़र को नए पेज को लोड करने और फॉर्म सबमिट करने के डिफ़ॉल्ट व्यवहार को करने से रोकती है।
featured image - JavaScript वर्धित प्रपत्रों में डुप्लिकेट फ़ेच अनुरोधों को रद्द करने के तरीके पर एक मार्गदर्शिका
Austin Gil HackerNoon profile picture

यदि आपने कभी फ़ॉर्म सबमिशन बढ़ाने के लिए JavaScript fetch API का उपयोग किया है, तो इस बात की अच्छी संभावना है कि आपने गलती से एक डुप्लिकेट-अनुरोध/रेस-स्थिति बग पेश कर दिया है। आज, मैं आपको समस्या और इससे बचने के अपने सुझावों के बारे में बताऊँगा।


(यदि आप चाहें तो वीडियो अंत में)


आइए एक बहुत ही बुनियादी पर विचार करें एचटीएमएल फॉर्म एक इनपुट और सबमिट बटन के साथ।


 <form method="post"> <label for="name">Name</label> <input id="name" name="name" /> <button>Submit</button> </form> 


जब हम सबमिट बटन दबाते हैं, तो ब्राउजर पूरे पेज को रिफ्रेश करेगा।


ध्यान दें कि सबमिट बटन क्लिक करने के बाद ब्राउज़र कैसे पुनः लोड होता है।


पृष्ठ ताज़ा करना हमेशा वह अनुभव नहीं होता है जो हम अपने उपयोगकर्ताओं को प्रदान करना चाहते हैं, इसलिए एक सामान्य विकल्प का उपयोग करना है जावास्क्रिप्ट प्रपत्र के "सबमिट" ईवेंट में ईवेंट श्रोता जोड़ने के लिए, डिफ़ॉल्ट व्यवहार को रोकें, और fetch API का उपयोग करके फ़ॉर्म डेटा सबमिट करें।


एक सरल दृष्टिकोण नीचे दिए गए उदाहरण की तरह लग सकता है।


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


 const form = document.querySelector('form'); form.addEventListener('submit', handleSubmit); function handleSubmit(event) { const form = event.currentTarget; fetch(form.action, { method: form.method, body: new FormData(form) }); event.preventDefault(); }


अब, इससे पहले कि कोई जावास्क्रिप्ट हॉटशॉट GET बनाम POST के बारे में मुझ पर ट्वीट करना शुरू करे और शरीर और अनुरोध करें सामग्री प्रकार और जो कुछ भी हो, मुझे बस इतना कहने दो, मुझे पता है। मैं fetch अनुरोध को जानबूझकर सरल रख रहा हूं क्योंकि यह मुख्य फोकस नहीं है।


यहाँ प्रमुख मुद्दा है event.preventDefault() । यह विधि ब्राउज़र को नया पृष्ठ लोड करने और फ़ॉर्म सबमिट करने के डिफ़ॉल्ट व्यवहार को करने से रोकती है।


अब, यदि हम स्क्रीन को देखते हैं और सबमिट दबाते हैं, तो हम देख सकते हैं कि पृष्ठ पुनः लोड नहीं होता है, लेकिन हम अपने नेटवर्क टैब में HTTP अनुरोध देखते हैं।


ध्यान दें कि ब्राउज़र पूर्ण पृष्ठ पुनः लोड नहीं करता है।


दुर्भाग्य से, डिफ़ॉल्ट व्यवहार को रोकने के लिए जावास्क्रिप्ट का उपयोग करके, हमने वास्तव में एक बग पेश किया है जो कि डिफ़ॉल्ट ब्राउज़र व्यवहार में नहीं है।


जब हम सादा उपयोग करते हैं एचटीएमएल और आप सबमिट बटन को वास्तव में जल्दी से कई बार तोड़ देते हैं, आप देखेंगे कि सबसे हाल के अनुरोध को छोड़कर सभी नेटवर्क अनुरोध लाल हो जाते हैं। यह इंगित करता है कि उन्हें रद्द कर दिया गया था, और केवल सबसे हाल के अनुरोध को सम्मानित किया गया है।


यदि हम इसकी तुलना जावास्क्रिप्ट उदाहरण से करते हैं, तो हम देखेंगे कि सभी अनुरोध भेज दिए गए हैं, और वे सभी रद्द किए बिना पूर्ण हैं।


यह एक समस्या हो सकती है क्योंकि हालांकि प्रत्येक अनुरोध में अलग-अलग समय लग सकता है, वे शुरू किए गए समय की तुलना में एक अलग क्रम में हल हो सकते हैं। इसका अर्थ है कि यदि हम उन अनुरोधों के समाधान में कार्यात्मकता जोड़ते हैं, तो हमारे पास कुछ अनपेक्षित व्यवहार हो सकते हैं।


एक उदाहरण के रूप में, हम प्रत्येक अनुरोध (" totalRequestCount ") के लिए वृद्धि करने के लिए एक चर बना सकते हैं। हर बार जब हम handleSubmit फ़ंक्शन चलाते हैं, तो हम कुल संख्या बढ़ा सकते हैं और वर्तमान अनुरोध (" thisRequestNumber ") को ट्रैक करने के लिए वर्तमान संख्या को कैप्चर कर सकते हैं।


जब एक fetch अनुरोध हल हो जाता है, तो हम कंसोल में इसकी संबंधित संख्या लॉग कर सकते हैं।


 const form = document.querySelector('form'); form.addEventListener('submit', handleSubmit); let totalRequestCount = 0 function handleSubmit(event) { totalRequestCount += 1 const thisRequestNumber = totalRequestCount const form = event.currentTarget; fetch(form.action, { method: form.method, body: new FormData(form) }).then(() => { console.log(thisRequestNumber) }) event.preventDefault(); }


अब, यदि हम उस सबमिट बटन को बार-बार तोड़ते हैं, तो हम कंसोल पर मुद्रित विभिन्न संख्याओं को क्रम से बाहर देख सकते हैं: 2, 3, 1, 4, 5। यह नेटवर्क की गति पर निर्भर करता है, लेकिन मुझे लगता है कि हम सभी सहमत हो सकते हैं कि यह आदर्श नहीं है।


एक ऐसे परिदृश्य पर विचार करें जहां एक उपयोगकर्ता निकट उत्तराधिकार में कई fetch को ट्रिगर करता है, और पूरा होने पर, आपका एप्लिकेशन उनके परिवर्तनों के साथ पृष्ठ को अपडेट करता है। ऑर्डर के बाहर समाधान करने के अनुरोध के कारण उपयोगकर्ता अंततः गलत जानकारी देख सकता है।


यह गैर-जावास्क्रिप्ट दुनिया में एक गैर-मुद्दा है क्योंकि ब्राउज़र किसी भी पिछले अनुरोध को रद्द कर देता है और सबसे हालिया अनुरोध पूरा होने के बाद पृष्ठ को लोड करता है, सबसे अद्यतित संस्करण को लोड करता है। लेकिन पेज रिफ्रेश उतना सेक्सी नहीं है।


जावास्क्रिप्ट प्रेमियों के लिए अच्छी खबर यह है कि हमारे पास दोनों हो सकते हैं सेक्सी उपयोगकर्ता अनुभव और एक सतत यूआई!


हमें बस थोड़ा और लेगवर्क करने की जरूरत है।


यदि आप fetch एपीआई दस्तावेज़ को देखते हैं, तो आप देखेंगे कि AbortController और fetch विकल्पों की signal प्रॉपर्टी का उपयोग करके फ़ेच को रोकना संभव है। ऐसा कुछ दिखता है:


 const controller = new AbortController(); fetch(url, { signal: controller.signal });


fetch अनुरोध के लिए AbortContoller का संकेत प्रदान करके, हम किसी भी समय AbortContoller की abort विधि ट्रिगर होने पर अनुरोध को रद्द कर सकते हैं।


आप जावास्क्रिप्ट कंसोल में एक स्पष्ट उदाहरण देख सकते हैं। एक AbortController बनाने का प्रयास करें, fetch अनुरोध शुरू करें, फिर abort विधि को तुरंत निष्पादित करें।


 const controller = new AbortController(); fetch('', { signal: controller.signal }); controller.abort()


आपको तुरंत कंसोल पर मुद्रित अपवाद देखना चाहिए। क्रोमियम ब्राउज़र में, यह कहना चाहिए, "अनकॉट (वादे में) DOMException: उपयोगकर्ता ने एक अनुरोध रद्द कर दिया।" और यदि आप नेटवर्क टैब एक्सप्लोर करते हैं, तो आपको स्टेटस टेक्स्ट "(रद्द)" के साथ एक असफल अनुरोध देखना चाहिए।


जावास्क्रिप्ट कंसोल के खुले होने के साथ क्रोम देव उपकरण नेटवर्क पर खुल गए। कंसोल में कोड है "कॉन्स्ट कंट्रोलर = नया एबॉर्टकंट्रोलर (); लाने ('', {सिग्नल: कंट्रोलर.सिग्नल}); कंट्रोलर.बॉर्ट ()", इसके बाद अपवाद, "अनकॉट (वादे में) DOMException: द उपयोगकर्ता ने एक अनुरोध निरस्त कर दिया।" नेटवर्क में, स्थिति पाठ "(रद्द)" के साथ "लोकलहोस्ट" के लिए एक अनुरोध है

इसे ध्यान में रखते हुए, हम अपने फॉर्म के सबमिट हैंडलर में AbortController जोड़ सकते हैं। तर्क इस प्रकार होगा:


  • सबसे पहले, पिछले किसी भी अनुरोध के लिए AbortController की जांच करें। यदि कोई मौजूद है, तो उसे निरस्त करें।


  • अगला, वर्तमान अनुरोध के लिए एक AbortController बनाएं जिसे बाद के अनुरोधों पर निरस्त किया जा सकता है।


  • अंत में, जब कोई अनुरोध हल हो जाता है, तो उसके संबंधित AbortController हटा दें।


ऐसा करने के कई तरीके हैं, लेकिन मैं प्रत्येक सबमिट किए गए <form> DOM नोड और उसके संबंधित AbortController के बीच संबंधों को संग्रहीत करने के लिए WeakMap का उपयोग करूंगा। जब कोई फॉर्म सबमिट किया जाता है, तो हम उसके अनुसार WeakMap चेक और अपडेट कर सकते हैं।


 const pendingForms = new WeakMap(); function handleSubmit(event) { const form = event.currentTarget; const previousController = pendingForms.get(form); if (previousController) { previousController.abort(); } const controller = new AbortController(); pendingForms.set(form, controller); fetch(form.action, { method: form.method, body: new FormData(form), signal: controller.signal, }).then(() => { pendingForms.delete(form); }); event.preventDefault(); } const forms = document.querySelectorAll('form'); for (const form of forms) { form.addEventListener('submit', handleSubmit); }


मुख्य बात एक निरस्त नियंत्रक को इसके संबंधित रूप से संबद्ध करने में सक्षम हो रही है। WeakMap की कुंजी के रूप में फ़ॉर्म के DOM नोड का उपयोग करना ऐसा करने का एक सुविधाजनक तरीका है।


इसके साथ, हम fetch अनुरोध के लिए AbortController का संकेत जोड़ सकते हैं, किसी भी पिछले नियंत्रक को निरस्त कर सकते हैं, नए जोड़ सकते हैं, और पूरा होने पर उन्हें हटा सकते हैं।


उम्मीद है, यह सब समझ में आता है।


अब, यदि हम उस फॉर्म के सबमिट बटन को कई बार तोड़ते हैं, तो हम देख सकते हैं कि नवीनतम को छोड़कर सभी एपीआई अनुरोध रद्द हो जाते हैं।


इसका मतलब यह है कि HTTP प्रतिक्रिया का जवाब देने वाला कोई भी फ़ंक्शन आपकी अपेक्षा के अनुरूप अधिक व्यवहार करेगा।


अब, यदि हम उसी गिनती और लॉगिंग लॉजिक का उपयोग करते हैं जो हमारे ऊपर है, तो हम सबमिट बटन को सात बार स्मैश कर सकते हैं और कंसोल में छह अपवाद ( AbortController के कारण) और "7" का एक लॉग देखेंगे।


यदि हम फिर से सबमिट करते हैं और अनुरोध को हल करने के लिए पर्याप्त समय देते हैं, तो हमें कंसोल में "8" दिखाई देगा। और अगर हम सबमिट बटन को कई बार तोड़ते हैं, तो हम अपवादों और अंतिम अनुरोध की गिनती को सही क्रम में देखना जारी रखेंगे।


यदि आप अनुरोध के निरस्त होने पर कंसोल में DOMExceptions को देखने से बचने के लिए कुछ और तर्क जोड़ना चाहते हैं, तो आप अपने fetch के बाद एक .catch() ब्लॉक जोड़ सकते हैं और जांच सकते हैं कि क्या त्रुटि का नाम " AbortError " से मेल खाता है:


 fetch(url, { signal: controller.signal, }).catch((error) => { // If the request was aborted, do nothing if (error.name === 'AbortError') return; // Otherwise, handle the error here or throw it back to the console throw error });

समापन

यह पूरी पोस्ट जावास्क्रिप्ट-वर्धित रूपों पर केंद्रित थी, लेकिन जब भी आप एक fetch अनुरोध करते हैं तो AbortController शामिल करना शायद एक अच्छा विचार है। यह वास्तव में बहुत बुरा है कि यह पहले से ही एपीआई में नहीं बनाया गया है। लेकिन उम्मीद है, यह आपको इसे शामिल करने का एक अच्छा तरीका दिखाता है।


यह भी उल्लेखनीय है कि यह दृष्टिकोण उपयोगकर्ता को सबमिट बटन को कई बार स्पैम करने से नहीं रोकता है। बटन अभी भी क्लिक करने योग्य है, और अनुरोध अभी भी बंद हो जाता है, यह प्रतिक्रियाओं से निपटने का एक अधिक सुसंगत तरीका प्रदान करता है।


दुर्भाग्य से, यदि कोई उपयोगकर्ता सबमिट बटन को स्पैम करता है , तो वे अनुरोध अभी भी आपके बैकएंड पर जाएंगे और अनावश्यक संसाधनों के एक समूह का उपयोग कर सकते हैं।


कुछ सहज समाधान सबमिट बटन को अक्षम कर सकते हैं, a debounce , या केवल पिछले अनुरोधों के समाधान के बाद नए अनुरोध बनाना। मुझे ये विकल्प पसंद नहीं हैं क्योंकि वे उपयोगकर्ता के अनुभव को धीमा करने पर भरोसा करते हैं और केवल ग्राहक पक्ष पर काम करते हैं।


वे स्क्रिप्टेड अनुरोधों के माध्यम से दुरुपयोग को संबोधित नहीं करते हैं।


अपने सर्वर पर बहुत अधिक अनुरोधों से होने वाले दुरुपयोग को संबोधित करने के लिए, आप शायद कुछ सेट अप करना चाहेंगे दर सीमित . यह इस पोस्ट के दायरे से बाहर है, लेकिन यह उल्लेखनीय था।


यह भी उल्लेखनीय है कि दर सीमित करने से डुप्लिकेट अनुरोधों, दौड़ की स्थितियों और असंगत यूआई अपडेट की मूल समस्या का समाधान नहीं होता है। आदर्श रूप से, हमें दोनों सिरों को ढकने के लिए दोनों का उपयोग करना चाहिए।


वैसे भी, मेरे पास आज के लिए बस इतना ही है। यदि आप इसी विषय पर एक वीडियो देखना चाहते हैं, तो इसे देखें।

पढ़ने के लिए आपको बहुत बहुत शुक्रिया। अगर आपको यह लेख पसंद आया हो तो प्लीज इसे शेयर करें . यह मेरा समर्थन करने के सर्वोत्तम तरीकों में से एक है। आप भी कर सकते हैं मेरे न्यूज़लैटर के लिए साइन अप करें या ट्विटर पर मुझे फॉलो करें यदि आप जानना चाहते हैं कि नए लेख कब प्रकाशित होते हैं।


मूल रूप से पर प्रकाशित किया गया austingil.com .