पहले await से पहले event.preventDefault() कॉल करें, अपना async सत्यापन चलाएँ, फिर सबमिशन का नया प्रयास शुरू करने के लिए form.requestSubmit() का उपयोग करें। एक बार उपयोग होने वाला गार्ड उस दूसरे प्रयास को async जाँच दोहराए बिना आगे बढ़ने देता है।

यह ट्यूटोरियल Mac पर एक छोटा ब्राउज़र इंटरफ़ेस संपादित करने वाले व्यक्ति के लिए है। आप एक स्थानीय HTML उदाहरण बनाएँगे, जिसमें सिंथेटिक लेबल जाँच सफल होने के बाद ही फ़ॉर्म डायलॉग बंद करता है। यही इवेंट-हैंडलिंग पैटर्न उन सामान्य फ़ॉर्म पर भी लागू होता है जो नेविगेट करते हैं या अनुरोध भेजते हैं।

Async submit हैंडलर सबमिशन को विलंबित क्यों नहीं करता?

ब्राउज़र किसी इवेंट लिसनर से मिले प्रॉमिस का इंतज़ार नहीं करता। DOM इवेंट-डिस्पैच एल्गोरिदम लिसनर को चलाता है और आगे बढ़ता रहता है; HTML सबमिशन एल्गोरिदम आगे बढ़ने से पहले जाँचता है कि submit इवेंट रद्द हुआ था या नहीं। लिसनर में async जोड़ने से इस जीवनचक्र में प्रतीक्षा का चरण नहीं जुड़ता। DOM Standard: event dispatch, HTML Standard: form submission algorithm

Async फ़ंक्शन अपने पहले await तक सिंक्रोनस रूप से चलता है, फिर async रूप से आगे बढ़ता है। इसलिए रद्द करना पहले await से पहले होना चाहिए, भले ही जिसका इंतज़ार किया जा रहा हो वह प्रॉमिस पहले से पूरा हो चुका हो। उसके बाद preventDefault() कॉल करने से ब्राउज़र के सबमिशन का निर्णय पहले ही लेने के बाद उसे पलटा नहीं जा सकता। MDN: async function

रद्द करने से मूल प्रयास समाप्त हो जाता है; वह बाद में फिर शुरू करने के लिए रुका नहीं रहता। सत्यापन सफल होने के बाद, आपके कोड को स्पष्ट रूप से एक और सबमिशन का अनुरोध करना होगा। साथ ही, preventDefault() इवेंट का प्रसार जारी रखता है, इसलिए दूसरे लिसनर भी इवेंट को देख सकते हैं। MDN: Event.preventDefault()

फ़ॉर्म के submit इवेंट को सुनें। इससे बटन के ज़रिए सबमिशन, Enter दबाने जैसे समर्थित इंप्लिसिट सबमिशन, और requestSubmit() कॉल शामिल होते हैं। केवल बटन का क्लिक हैंडलर हर तरीके को शामिल नहीं करता। नेटिव कंस्ट्रेंट सत्यापन सामान्यतः पहले चलता है: आवश्यक फ़ील्ड अमान्य होने पर सबमिशन रुक जाता है और submit फ़ायर नहीं होता। MDN: HTMLFormElement submit event

कौन-सा तरीका फ़ॉर्म के सबमिशन व्यवहार को बनाए रखता है?

जब सफल जाँच के बाद सामान्य फ़ॉर्म जीवनचक्र फिर से शुरू होना चाहिए, तो requestSubmit() का उपयोग करें। सीधे submit() कॉल करने से कंस्ट्रेंट सत्यापन और submit इवेंट, दोनों छूट जाते हैं। यह शॉर्टकट उस व्यवहार को बदल देता है जिसे आपके async हैंडलर को बनाए रखना चाहिए। MDN: HTMLFormElement.submit()

तरीका क्या होता है यहाँ उपयुक्तता
लिसनर से प्रॉमिस लौटाएँ इवेंट डिस्पैच उसका इंतज़ार किए बिना आगे बढ़ता है डिफ़ॉल्ट कार्रवाई को विलंबित नहीं करता
रद्द करें, फिर submit() कॉल करें सबमिशन नेटिव सत्यापन और submit लिसनर को छोड़ देता है जीवनचक्र बदल देता है
रद्द करें, फिर requestSubmit(submitter) कॉल करें नेटिव सत्यापन और submit इवेंट फिर से चलते हैं इच्छित सबमिशन पथ बनाए रखता है

इंतज़ार करने से पहले event.submitter सहेज लें। उस बटन को requestSubmit() में पास करने से उसकी भूमिका बनी रहती है, जिसमें सबमिशन ओवरराइड और लागू होने पर उसका name/value योगदान भी शामिल है। अगर कोई submitter नहीं था, तो requestSubmit() को बिना किसी आर्ग्युमेंट के कॉल करें। दिया गया बटन उसी फ़ॉर्म का होना चाहिए; अमान्य बटन या किसी दूसरे फ़ॉर्म का बटन अपवाद उत्पन्न करता है। MDN: HTMLFormElement.requestSubmit()

नया सबमिशन आपके लिसनर को फिर से ट्रिगर करता है। इसलिए नीचे दिया गया उदाहरण स्थिति के दो अलग हिस्से इस्तेमाल करता है: pending एक साथ चलने वाली async जाँचों को रोकता है, जबकि allowNextSubmit हैंडलर से ठीक एक सिंक्रोनस बार गुज़रने की अनुमति देता है। स्थायी “सत्यापित” फ़्लैग बहुत व्यापक होगा, क्योंकि बाद में किए गए बदलाव पहले के परिणाम का फिर से उपयोग कर सकते हैं।

अपने Mac पर एक पूरा स्थानीय उदाहरण बनाएँ

नीचे दिए गए पूरे दस्तावेज़ को किसी साधारण टेक्स्ट कोड एडिटर में async-form.html नाम से सहेजें, फिर इसे अपने Mac पर किसी वर्तमान ब्राउज़र में खोलें। किसी लाइब्रेरी, इंपोर्ट, सर्वर या इंस्टॉलेशन की ज़रूरत नहीं है। मानक में requestSubmit() के लिए Safari 16 और उसके बाद के संस्करणों का समर्थन सूचीबद्ध है। HTML Standard: form APIs

उदाहरण में method="dialog" का उपयोग किया गया है ताकि डिफ़ॉल्ट कार्रवाई स्थानीय रूप से आसानी से दिखाई दे। सफल सबमिशन से बाहरी डायलॉग बंद होता है और submit बटन से उसका returnValue सेट होता है; फ़ॉर्म कंट्रोल कहीं भेजे नहीं जाते। खोलने वाला बटन उसे दिखाने के लिए showModal() कॉल करता है। MDN: dialog element, MDN: HTMLDialogElement.showModal()

वैलिडेटर जानबूझकर सिंथेटिक रखा गया है: थोड़े समय के बाद, यह ट्रिम करने पर कम से कम तीन अक्षर वाले लेबल स्वीकार करता है, ठीक lowercase लेबल taken को अस्वीकार करता है, और ठीक lowercase लेबल error के लिए त्रुटि देता है। यह विलंब async कार्य पूरा होने को दर्शाता है; यह प्रदर्शन का माप नहीं है।

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Asynchronous form validation</title>
</head>
<body>
  <h1>Label preview</h1>
  <button id="open" type="button">Open form</button>
  <p id="outcome" role="status"></p>

  <dialog id="editor" aria-labelledby="dialog-title">
    <form id="label-form" method="dialog">
      <h2 id="dialog-title">Choose a label</h2>
      <label for="label">Label</label>
      <input id="label" name="label" required autofocus>
      <button type="submit" value="accepted">Continue</button>
      <button id="cancel" type="button">Cancel</button>
      <p id="status" role="status"></p>
    </form>
  </dialog>

  <script>
    const dialog = document.getElementById("editor");
    const form = document.getElementById("label-form");
    const input = document.getElementById("label");
    const status = document.getElementById("status");
    const outcome = document.getElementById("outcome");
    const openButton = document.getElementById("open");
    const cancelButton = document.getElementById("cancel");

    let pending = false;
    let allowNextSubmit = false;
    let revision = 0;

    async function validateLabel(value) {
      await new Promise(resolve => setTimeout(resolve, 700));
      const label = value.trim();
      if (label === "error") {
        throw new Error("Synthetic validation failure");
      }
      return label.length >= 3 && label !== "taken";
    }

    openButton.addEventListener("click", () => {
      revision += 1;
      dialog.returnValue = "";
      status.textContent = "";
      outcome.textContent = "";
      dialog.showModal();
    });

    input.addEventListener("input", () => {
      revision += 1;
    });

    cancelButton.addEventListener("click", () => dialog.close());

    dialog.addEventListener("close", () => {
      revision += 1;
      outcome.textContent = "Dialog closed. Return value: " +
        (dialog.returnValue || "(empty)");
    });

    form.addEventListener("submit", async event => {
      if (allowNextSubmit) {
        allowNextSubmit = false;
        return;
      }

      event.preventDefault();
      if (pending) return;

      pending = true;
      const submitter = event.submitter;
      const value = input.value;
      const checkedRevision = revision;
      status.textContent = "Checking label...";

      try {
        const valid = await validateLabel(value);
        if (!dialog.open) return;

        if (revision !== checkedRevision || input.value !== value) {
          status.textContent = "The label changed. Submit again.";
          return;
        }

        if (!valid) {
          status.textContent =
            "Use at least three characters and avoid taken.";
          return;
        }

        status.textContent = "Check passed. Submission requested.";
        allowNextSubmit = true;
        try {
          if (submitter) form.requestSubmit(submitter);
          else form.requestSubmit();
        } finally {
          allowNextSubmit = false;
        }
      } catch (error) {
        if (dialog.open && revision === checkedRevision) {
          status.textContent = "The check failed. Try again.";
        }
        console.error(error);
      } finally {
        pending = false;
      }
    });
  </script>
</body>
</html>

उदाहरण का ब्राउज़र व्यवहार दस्तावेज़ित जीवनचक्र पर आधारित है। Node.js EventTarget की एक छोटी जाँच ने समय के इस अंतर की पुष्टि की: सिंक्रोनस रद्द करने से डिस्पैच का परिणाम बदला, जबकि await के बाद रद्द करने से वह परिणाम छूट गया। यहाँ पूरा डायलॉग उदाहरण Mac ब्राउज़र में चलाया नहीं गया है।

गार्ड क्रम बनाए रखते हुए बदलावों को कैसे संभालता है?

पहले सबमिशन पर, हैंडलर तुरंत रद्द करता है और लेबल तथा सबमिशन शुरू करने वाले बटन—दोनों को सहेजता है। जाँच लंबित रहने के दौरान कोई दूसरा submit इवेंट भी रद्द कर दिया जाता है, लेकिन उससे दूसरी सत्यापन प्रक्रिया शुरू नहीं होती। इससे तेज़ी से किए गए कई सबमिशन एक साथ चलने वाली जाँचों को शुरू नहीं करते।

जाँच पूरी होने पर, हैंडलर पुष्टि करता है कि डायलॉग अभी भी खुला है और लेबल बदला नहीं है। रिविज़न काउंटर तब भी उपयोगकर्ता के संपादन का पता लगाता है जब उपयोगकर्ता टेक्स्ट बदलकर फिर पहले जैसा कर दे। वैल्यू की तुलना उस सीधे वैल्यू बदलाव को भी पकड़ती है जिससे फ़ील्ड में अलग टेक्स्ट रह जाता है।

पास होने वाला परिणाम allowNextSubmit को requestSubmit() कॉल करने से ठीक पहले सेट करता है। वह मेथड नेटिव सत्यापन और submit इवेंट का पथ फिर चलाता है। लिसनर दोबारा बुलाए जाने पर अनुमति का उपयोग करके बिना रद्द किए लौट जाता है। फिर ब्राउज़र डायलॉग बंद करने वाली डिफ़ॉल्ट कार्रवाई कर सकता है। यह क्रम मानक के सत्यापन-के-बाद-submit-इवेंट वाले क्रम का पालन करता है। HTML Standard: form submission algorithm

भीतरी finally अनुमति को साफ़ कर देता है, चाहे कोई submit इवेंट न हो या मेथड त्रुटि दे। बाहरी finally अस्वीकृति, अपवाद, पुराने परिणाम या सफल रीट्राई के बाद pending को साफ़ कर देता है। इसलिए असफल प्रयास के बाद फ़ॉर्म एक और जाँच के लिए उपलब्ध रहता है।

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

सफलता, अस्वीकृति और बीच में रुकने वाले पथों को जाँचें

इन जाँचों को स्थानीय ब्राउज़र उदाहरण में चलाएँ। अपेक्षित परिणाम दिए गए कोड के अनुसार हैं; इन्हें ब्राउज़र परीक्षण के परिणाम के रूप में प्रस्तुत नहीं किया जा रहा है।

क्रिया अपेक्षित परिणाम
लेबल खाली छोड़ें और Continue चुनें नेटिव आवश्यक-फ़ील्ड सत्यापन async हैंडलर चलने से पहले सबमिशन रोकता है
ab दर्ज करें और Continue चुनें जाँच के बाद डायलॉग खुला रहता है और अस्वीकृति संदेश दिखाता है
taken दर्ज करें सिंथेटिक नियम इसे अस्वीकार करता है
sample दर्ज करें जाँच के दौरान डायलॉग खुला रहता है, फिर accepted रिटर्न वैल्यू के साथ बंद हो जाता है
error दर्ज करें विफलता संदेश दिखाई देता है और एक और सबमिशन किया जा सकता है
लेबल सबमिट करें, फिर जाँच के दौरान उसे संपादित करें परिणाम छोड़ दिया जाता है और फ़ॉर्म एक और सबमिशन माँगता है
जाँच के दौरान बार-बार सबमिट करें केवल पहला प्रयास async जाँच शुरू करता है
जाँच के दौरान Cancel चुनें लंबित परिणाम का इंतज़ार किए बिना डायलॉग बंद हो जाता है

जब इनपुट पर फ़ोकस हो, तब Enter दबाकर सबमिट करने की भी कोशिश करें। इंप्लिसिट सबमिशन फ़ॉर्म की संरचना और प्लेटफ़ॉर्म की परंपराओं पर निर्भर करता है; submit को संभालने का कारण यह है कि इससे उस समर्थित इंटरैक्शन को कवर किया जा सके जो वास्तव में फ़ॉर्म सबमिशन ट्रिगर करता है। MDN: HTMLFormElement submit event

जब फ़ॉर्म सामान्य अनुरोध भेजता है, तो क्या बदलता है?

मौजूदा अनुरोध भेजने वाले फ़ॉर्म में उसका वास्तविक action, method, फ़ील्ड के नाम, बटन की वैल्यू और सबमिशन ओवरराइड बनाए रखें। सिंथेटिक वैलिडेटर को अपने वास्तविक async जाँच से बदलें। डायलॉग खोलने और बंद करने वाला कोड इस प्रदर्शन के लिए विशिष्ट है; रद्द–सत्यापित–अनुरोध क्रम फ़ॉर्म के सबमिशन हैंडलर में होना चाहिए।

अंतिम सबमिशन के लिए फ़ील्ड उपलब्ध रखें। प्रतीक्षा के दौरान कंट्रोल अक्षम करने में सावधानी चाहिए: ब्राउज़र सबमिशन एंट्री लिस्ट बनाते समय अक्षम फ़ील्ड शामिल नहीं करता। इसके बजाय, उदाहरण संपादन की अनुमति देता है और पुराने सत्यापन परिणामों को अस्वीकार करता है। HTML Standard: constructing the entry list

अगर अन्य submit लिसनर मौजूद हैं, तो वे रद्द किए गए पहले प्रयास और बाद वाले प्रयास—दोनों को देखेंगे। उनके साथ तालमेल रखें ताकि वे वही काम दो बार न करें या अधिकृत प्रयास के दौरान सत्यापित वैल्यू न बदलें। requestSubmit() पूरा होने की गारंटी भी नहीं देता: कोई अन्य लिसनर इसे रद्द कर सकता है और नेटिव कंस्ट्रेंट इसे रोक सकते हैं। इसका रिटर्न वैल्यू undefined है, पूरा होने का प्रॉमिस नहीं। MDN: HTMLFormElement.requestSubmit()

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

स्रोत