यदि आपने कभी फ़ॉर्म सबमिशन बढ़ाने के लिए 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: उपयोगकर्ता ने एक अनुरोध रद्द कर दिया।" और यदि आप नेटवर्क टैब एक्सप्लोर करते हैं, तो आपको स्टेटस टेक्स्ट "(रद्द)" के साथ एक असफल अनुरोध देखना चाहिए।
इसे ध्यान में रखते हुए, हम अपने फॉर्म के सबमिट हैंडलर में 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
वे स्क्रिप्टेड अनुरोधों के माध्यम से दुरुपयोग को संबोधित नहीं करते हैं।
अपने सर्वर पर बहुत अधिक अनुरोधों से होने वाले दुरुपयोग को संबोधित करने के लिए, आप शायद कुछ सेट अप करना चाहेंगे
यह भी उल्लेखनीय है कि दर सीमित करने से डुप्लिकेट अनुरोधों, दौड़ की स्थितियों और असंगत यूआई अपडेट की मूल समस्या का समाधान नहीं होता है। आदर्श रूप से, हमें दोनों सिरों को ढकने के लिए दोनों का उपयोग करना चाहिए।
वैसे भी, मेरे पास आज के लिए बस इतना ही है। यदि आप इसी विषय पर एक वीडियो देखना चाहते हैं, तो इसे देखें।
पढ़ने के लिए आपको बहुत बहुत शुक्रिया। अगर आपको यह लेख पसंद आया हो तो प्लीज
मूल रूप से पर प्रकाशित किया गया