【问题标题】:How do I do a jQuery blocking AJAX call without async = false?如何在没有 async = false 的情况下执行 jQuery 阻止 AJAX 调用?
【发布时间】:2012-06-16 10:57:40
【问题描述】:

我有一个页面使用 HTML5 setCustomValidity() 方法 [使用旧浏览器的 webshims 库] 对电子邮件进行 AJAX 验证,然后继续到下一页。

为此,我在 $.ajax() 调用中将 async 选项设置为 false 以使其同步,阻止页面等待 AJAX 响应,否则表单在 ajax 调用返回之前提交,呈现验证无效。

<script>
  function validateEmailRegistered(input) {
    if (input.setCustomValidity === undefined) return;
    var err = 'Email address not found';
    $.ajax({
      url: 'email-is-registered.php', 
      data: { email: input.value }, 
      async: false, 
      success: function(data) {
        var ok = data=='true';
        input.setCustomValidity(ok ? '' : err)
      }
    });
  }
</script>

<form method="get" action="nextpage.php">
  <input name="email" id="email" type="email" required
    onChange="validateEmailRegistered(this)">
  <button type="submit">go</button>
<form>

我看到从 jQuery 1.8 开始不推荐使用 async 选项。

如何在没有异步选项的情况下实现此阻塞操作?

【问题讨论】:

  • 你最终使用了什么解决方案?我也面临同样的问题。

标签: jquery ajax html validation asynchronous


【解决方案1】:

http://bugs.jquery.com/ticket/11013#comment:40

在同步 ajax 请求中使用 Deferred/Promise 功能已在 1.8 中弃用。支持 async: false 的 $.ajax 方法,但您必须使用回调参数而不是 Promise 方法,例如 .then.done

因此,如果您使用的是成功/完成/错误处理程序,您仍然可以使用async:false。更多信息请参阅上面的 jquery 票。

【讨论】:

    【解决方案2】:

    从 jQuery 1.8 开始,使用 async: false 和 jqXHR ($.Deferred) 是 已弃用;您必须使用完成/成功/错误回调。

    http://api.jquery.com/jQuery.ajax/(异步部分)

    我觉得这有点烦人...如果平台允许,开发人员应该选择使用async: false 进行阻塞调用 - 为什么要限制它?我只是设置了一个超时来尽量减少挂起的影响。

    尽管如此,我现在在 1.8 中使用队列,它是非阻塞的,并且工作得很好。 Sebastien Roch 创建了一个易于使用的实用程序,允许您对函数进行排队并运行/暂停它们。 https://github.com/mjward/Jquery-Async-queue

        queue = new $.AsyncQueue();
        queue.add(function (queue) { ajaxCall1(queue); });
        queue.add(function (queue) { ajaxCall2(queue); });
        queue.add(function() { ajaxCall3() });
        queue.run();
    

    在前 2 个函数中,我将 queue 对象传递给调用,这是调用的样子:

    function ajaxCall1(queue) {
        queue.pause();
        $.ajax({
           // your parameters ....
           complete: function() {
             // your completing steps
             queue.run();
           }
        });
    }
    
    // similar for ajaxCall2
    

    注意每个函数开头的queue.pause();,以及queue.run()complete 语句的末尾继续执行队列。

    【讨论】:

    • 这看起来不错,但链接已失效:(
    • 感谢@Gus 的提醒,我搜索了一下,发现该项目已转移到另一个 github 帐户。
    【解决方案3】:

    我假设您无论如何都会在提交时进行完整的表单验证,所以如果您键入电子邮件验证被中断,并且优先考虑表单提交,也许没问题。

    我想我会在用户提交表单时中止“validateEmailRegistered”AJAX 请求。然后,像往常一样执行完整的服务器端表单验证 - 包括电子邮件验证。

    我对 as-you-type 验证的理解是,它很好,不能替代在提交表单时验证表单。所以阻止表单提交对我来说没有意义。

    【讨论】:

    • 我也在做服务器端验证,但与通过常规 (setCustomValidity) 机制提供用户反馈的阻塞 AJAX 调用相比,这提供了更差的用户交互。客户端验证不能替代服务器端验证,但可以提供更好的用户体验。服务器端错误消息只是另一种阻塞方式,真的...
    【解决方案4】:

    如果您真的决定在您检查电子邮件有效性之前不让他们提交表单,请使用disabled 属性启动您的提交按钮,然后设置回调$('form button').removeAttr('disabled');

    话虽如此,我和其他人在一起 - 让他们提交表格!通常人们会做对,然后你就可以毫无错误地传递它们......

    【讨论】:

      【解决方案5】:

      我认为您需要稍微改变一下它的工作方式。不要试图实现阻塞,而要拥抱非阻塞。

      我会这样做: - 保留电子邮件验证;使其异步。当它有效时,在变量中的某处设置一个标志以知道它没问题。 - 在 form.submit() 上添加回调以检查电子邮件是否正常(使用变量),如果不正常则阻止提交。

      这样您可以保持异步调用而不会冻结 Web 浏览器 UI。

      -- [编辑]--

      这是我刚刚根据您已有的内容为示例编写的一些快速代码。

      供您参考,已经发明了一种称为“承诺”(期货和延期是它的其他术语)的编程概念,以准确解决您遇到的问题。

      这是一篇关于它是什么以及如何在 JavaScript 中使用它们(使用 dojo 或 jQuery)的文章:http://blogs.msdn.com/b/ie/archive/2011/09/11/asynchronous-programming-in-javascript-with-promises.aspx

      <script>
        function validateEmailRegistered(input) {
          if (input.setCustomValidity === undefined) return;
          var err = 'Email address not found';
          $.ajax({
            url: 'email-is-registered.php', 
            data: { email: input.value }, 
            success: function(data) {
              var ok = data=='true';
              input.setCustomValidity(ok ? '' : err);
              // If the form was already submited, re-launch the check
              if (form_submitted) {
                  input.form.submit();
              }
            }
          });
        }
      
        var form_submitted = false;
      
        function submitForm(form) {
          // Save the user clicked on "submit"
          form_submitted = true;
          return checkForm(form);
        }
      
        function checkForm(form, doSubmit) {
          if (doSubmit) {
              form_submitted = true;
          }
          // If the email is not valid (it can be delayed due to asynchronous call)
          if (!form.email.is_valid) {
              return false;
          }
          return true;
        }
      </script>
      
      <form method="get" action="nextpage.php" onsumbit="return submitForm(this);">
        <input name="email" id="email" type="email" required
          onChange="validateEmailRegistered(this)">
        <button type="submit">go</button>
      <form>
      

      【讨论】:

      • 在表单提交上使用回调来阻止提交正是我想要实现的,但是如果回调是异步的,我不知道该怎么做......
      【解决方案6】:

      您可以异步执行。

      创建一个全局变量,我称之为ajax_done_and_successful_flag, 您在开始时初始化为 false 程序。您将在各种情况下将其设置为 true 或 false 在您的 Ajax 功能中的位置,例如您的 Ajax 成功 函数或您的 Ajax 错误函数。

      然后你需要在你的底部添加一个提交处理程序 验证功能。

      submitHandler: function(form) {
         if (ajax_done_and_successful_flag === true) {
           form.submit()
         }
      }  
      

      问题是代码没有以线性方式执行。
      在您的代码中放置一堆 Firebug 的 console.log 语句。
      观察执行顺序。你会看到你的
      Ajax 响应将最后返回,或者在需要时返回。
      这就是您需要 submitHandler 和全局标志的原因
      强制验证函数等待正确的 Ajax
      表单提交前的结果。

      从 Ajax 响应到屏幕的任何输出, 需要在 Ajax 函数,例如成功函数和 误差函数。
      您需要写入相同的位置 作为验证函数的成功/错误函数。
      通过这种方式,Ajax 错误消息与 validate 函数的 误差函数。
      这个概念可能看起来有点棘手。
      坚持的想法 记住的是validate函数中的success和error函数 正在写入与成功和错误相同的位置 Ajax 调用中的函数,没关系,就是这样 应该是。
      我的错误消息的位置就在 用户键入输入。这创造了良好的用户体验 我想你要的。

      这是我的代码示例。我简化了它。

      我正在运行 jQuery-1.7.1
      和jQuery验证插件1.6
      我正在使用 Firefox 14.0.1,我也尝试过 它在 Chrome 21.0 上成功。

       var ajax_done_and_successful_flag = false;
      
       // Add methods
        ...
      
       $.validator.addMethod("USERNAME_NOT_DUPLICATE", function (value, element) {
         return this.optional(element) || validate_username_not_duplicate( );
        },
        "Duplicate user name.");
      
      // validate
      $(document).ready(function ( ) {
         $('#register_entry_form form').validate({
            rules: {
               username: {
               required: true,
               minlength: 2,
               maxlength: 20,
               USERNAME_PATTERN: true,
               USERNAME_NOT_DUPLICATE: true
              },    
          ...
          errorPlacement: function (error, element) { 
             element.closest("div").find(".reg_entry_error").html(error);
          },
          success: function(label) {
             label.text('Success!');
          } ,
      
          submitHandler: function(form) {   
             if (ajax_done_and_successful_flag === true ) {    
                form.submit();
             }
          }
        });
      });
      
      /* validation functions*/
      
      function validate_username_not_duplicate() {
      
         var username_value = $('#username').val(); // whatever is typed in
      
         $.ajax({
            url: "check_duplicate_username.php",
            type: "POST",
            data: { username: username_value },
            dataType: "text",
            cache: false,
            //async: false,
            timeout: 5000,
            error: function (jqXHR, errorType, errorMessage) {
               ajax_done_and_successful_flag = false;
      
              if ( errorType === "timeout" ) {
                 $('#username').closest("div").find(".reg_entry_error").html("Server timeout, try again later" );
              } else if ...
      
            },
      
            success:  function (retString, textStatus,jqXRH) {
      
              if ( retString === "yes") { // duplicate name 
                 // output message next to input field        
                 $('#username').closest("div").find(".reg_entry_error").html("Name already taken.");
                 ajax_done_and_successful_flag = false;
              } else if ( retString === "no") { // name is okay
                 // output message next to input field
                 $('#username').closest("div").find(".reg_entry_error").html("success!");
                 ajax_done_and_successful_flag = true;
      
              } else {
                 console.log("in validate_username_duplicate func, success function, string returned was not yes or no." );
                 $('#username').closest("div").find(".reg_entry_error").html("There are server problems. Try again later.");
                 ajax_done_and_successful_flag = false;
             }
           } // end success function
      
         }); // end ajax call
      
      
        return true; // automatically return true, the true/false is handled in
                    // the server side program and then the  submit handler
      
      }
      

      reg_entry_error 是文本输入旁边的位置。 这是表单的简化代码示例。

        <label class="reg_entry_label" for="username">Username</label>
        <input class="reg_entry_input" type="text" name="username" id="username" value="" />
        <span class="reg_entry_error" id="usernameError" ></span>  
      

      我希望这能回答你的问题。

      【讨论】:

        猜你喜欢
        • 2012-01-23
        • 1970-01-01
        • 1970-01-01
        • 2020-12-13
        • 1970-01-01
        • 1970-01-01
        • 2022-11-15
        • 1970-01-01
        相关资源
        最近更新 更多