【问题标题】:How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?如何让 jQuery 执行同步而不是异步的 Ajax 请求?
【发布时间】:2016-02-13 03:44:19
【问题描述】:

我有一个提供标准扩展点的 JavaScript 小部件。其中之一是beforecreate 函数。它应该返回false 以防止创建项目。

我已经使用 jQuery 在这个函数中添加了一个 Ajax 调用:

beforecreate: function (node, targetNode, type, to) {
  jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),

  function (result) {
    if (result.isOk == false) 
        alert(result.message);
  });
}

但我想阻止我的小部件创建项目,所以我应该在母函数中返回false,而不是在回调中。有没有办法使用 jQuery 或任何其他浏览器内 API 执行同步 AJAX 请求?

【问题讨论】:

  • 解决这个问题的正确方法是重写小部件的扩展点使用承诺。这种方式很容易让您将异步操作(如 ajax 请求)设置为beforecreate
  • 我提出了 Sajak 函数。我们有字母 T 吗?是的,vanna,给我 3 个 T。
  • @Kos 很到位。此处不需要同步 XHR
  • 如果人们在启动 javascript 时问我 1 条建议,我会说:拥抱 javascript 的异步特性,不要试图与之抗争。
  • 这个问题就像在问“我如何以纯文本形式存储我的用户密码?”当然有答案,但不要这样做。

标签: javascript jquery ajax asynchronous


【解决方案1】:

jQuery documentation:您将 asynchronous 选项指定为 false 以获取同步 Ajax 请求。然后你的回调可以在你的母亲函数继续之前设置一些数据。

如果按照建议进行更改,您的代码将如下所示:

beforecreate: function (node, targetNode, type, to) {
    jQuery.ajax({
        url: 'http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),
        success: function (result) {
            if (result.isOk == false) alert(result.message);
        },
        async: false
    });
}

【讨论】:

  • 没错,同步调用不可能使用get()、post()、load()。只有ajax()有“async”参数,可以设置为“false”。
  • @SLA80 不。从 jQuery 1.1 开始:stackoverflow.com/questions/6849686/…
  • 注意async: false的使用是deprecated。如果您使用它,您将在控制台中看到以下消息:“主线程上的同步 XMLHttpRequest 已被弃用,因为它对最终用户的体验产生不利影响。更多帮助xhr.spec.whatwg.org
【解决方案2】:

优秀的解决方案!我注意到当我尝试实现它时,如果我在成功子句中返回一个值,它会以未定义的形式返回。我必须将它存储在一个变量中并返回该变量。这是我想出的方法:

function getWhatever() {
  // strUrl is whatever URL you need to call
  var strUrl = "", strReturn = "";

  jQuery.ajax({
    url: strUrl,
    success: function(html) {
      strReturn = html;
    },
    async:false
  });

  return strReturn;
}

【讨论】:

  • 这是一个同步调用 (async:false)。
【解决方案3】:

您可以通过调用将 jQuery 的 Ajax 设置为同步模式

jQuery.ajaxSetup({async:false});

然后使用 jQuery.get( ... ); 执行您的 Ajax 调用

然后再打开一次

jQuery.ajaxSetup({async:true});

我猜它的工作原理与 @Adam 建议的相同,但它可能对想要将其 jQuery.get()jQuery.post() 重新配置为更复杂的 jQuery.ajax() 语法的人有所帮助。

【讨论】:

    【解决方案4】:
    function getURL(url){
        return $.ajax({
            type: "GET",
            url: url,
            cache: false,
            async: false
        }).responseText;
    }
    
    
    //example use
    var msg=getURL("message.php");
    alert(msg);
    

    【讨论】:

      【解决方案5】:

      所有这些答案都忽略了使用 async:false 进行 Ajax 调用将导致浏览器挂起直到 Ajax 请求完成这一点。使用流控制库将解决这个问题,而不会挂起浏览器。这是Frame.js 的示例:

      beforecreate: function(node,targetNode,type,to) {
      
          Frame(function(next)){
      
              jQuery.get('http://example.com/catalog/create/', next);
          });
      
          Frame(function(next, response)){
      
              alert(response);
              next();
          });
      
          Frame.init();
      }
      

      【讨论】:

        【解决方案6】:

        这是一个例子:

        $.ajax({
          url: "test.html",
          async: false
        }).done(function(data) {
           // Todo something..
        }).fail(function(xhr)  {
           // Todo something..
        });
        

        【讨论】:

          【解决方案7】:

          我使用了 Carcione 给出的答案并将其修改为使用 JSON。

           function getUrlJsonSync(url){
          
              var jqxhr = $.ajax({
                  type: "GET",
                  url: url,
                  dataType: 'json',
                  cache: false,
                  async: false
              });
          
              // 'async' has to be 'false' for this to work
              var response = {valid: jqxhr.statusText,  data: jqxhr.responseJSON};
          
              return response;
          }    
          
          function testGetUrlJsonSync()
          {
              var reply = getUrlJsonSync("myurl");
          
              if (reply.valid == 'OK')
              {
                  console.dir(reply.data);
              }
              else
              {
                  alert('not valid');
              }    
          }
          

          我添加了 'JSON'dataType 并将 .responseText 更改为 responseJSON

          我还使用返回对象的 statusText 属性检索了状态。请注意,这是 Ajax 响应的状态,而不是 JSON 是否有效。

          后端必须以正确(格式良好)的 JSON 返回响应,否则返回的对象将是未定义的。

          回答原始问题时需要考虑两个方面。一种是告诉 Ajax 同步执行(通过设置 async: false),另一种是通过调用函数的 return 语句而不是回调函数返回响应。

          我也尝试过使用 POST 并成功了。

          我将 GET 更改为 POST 并添加了 data: postdata

          function postUrlJsonSync(url, postdata){
          
              var jqxhr = $.ajax({
                  type: "POST",
                  url: url,
                  data: postdata,
                  dataType: 'json',
                  cache: false,
                  async: false
              });
          
              // 'async' has to be 'false' for this to work
              var response = {valid: jqxhr.statusText,  data: jqxhr.responseJSON};
          
              return response;
          }
          

          请注意,上述代码仅适用于 asyncfalse 的情况。如果您要设置 async: true,则返回的对象 jqxhr 在 AJAX 调用返回时无效,只有在异步调用完成后才有效,但那是设置 response 变量为时已晚。

          【讨论】:

            【解决方案8】:

            请记住,如果您正在执行跨域 Ajax 调用(通过使用 JSONP) - 您不能同步执行此操作,async 标志将被忽略jQuery.

            $.ajax({
                url: "testserver.php",
                dataType: 'jsonp', // jsonp
                async: false //IGNORED!!
            });
            

            对于 JSONP 调用,您可以使用:

            1. Ajax 调用到您自己的域 - 并在服务器端进行跨域调用
            2. 更改您的代码以异步工作
            3. 使用像 Frame.js 这样的“函数序列器”库(这个answer
            4. 阻止 UI 而不是阻止执行(这个answer)(我最喜欢的方式)

            【讨论】:

              【解决方案9】:

              使用async: false,您将获得一个被阻止的浏览器。 对于非阻塞同步解决方案,您可以使用以下内容:

              ES6/ECMAScript2015

              在 ES6 中,您可以使用生成器和 co library

              beforecreate: function (node, targetNode, type, to) {
                  co(function*(){  
                      let result = yield jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
                      //Just use the result here
                  });
              }
              

              ES7

              在 ES7 中,你可以只使用 asyc await:

              beforecreate: function (node, targetNode, type, to) {
                  (async function(){
                      let result = await jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
                      //Just use the result here
                  })(); 
              }
              

              【讨论】:

                【解决方案10】:

                注意:由于此警告消息,您不应使用 async: false

                从 Gecko 30.0(Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27)开始,由于对用户体验的负面影响,主线程上的同步请求已被弃用。

                Chrome 甚至会在控制台中对此发出警告:

                主线程上的同步 XMLHttpRequest 已被弃用,因为它会对最终用户的体验产生不利影响。如需更多帮助,请查看https://xhr.spec.whatwg.org/

                如果你正在做这样的事情,这可能会破坏你的页面,因为它可能会在任何一天停止工作。

                如果您想以一种仍然感觉像是同步但仍不阻塞的方式来执行此操作,那么您应该使用 async/await 并且可能还使用一些基于新的Fetch API 等承诺的 ajax

                async function foo() {
                  var res = await fetch(url)
                  console.log(res.ok)
                  var json = await res.json()
                  console.log(json)
                }
                

                编辑 当页面被用户导航离开或关闭时,chrome 正在处理Disallowing sync XHR in page dismissal。这包括 beforeunload、unload、pagehide 和 visibilitychange。

                如果这是你的用例,那么你可能想看看navigator.sendBeacon

                页面也可以使用 http 标头或 iframe 的允许属性禁用同步请求

                【讨论】:

                  【解决方案11】:

                  首先我们应该明白什么时候使用$.ajax,什么时候使用$.get/$.post

                  当我们需要对 ajax 请求进行低级别控制时,例如请求标头设置、缓存设置、同步设置等。那么我们应该使用 $.ajax。

                  $.get/$.post:当我们不需要对ajax请求进行低级控制时。只需简单的get/post数据到服务器。简写

                  $.ajax({
                    url: url,
                    data: data,
                    success: success,
                    dataType: dataType
                  });
                  

                  因此我们不能在 $.get/$.post 中使用其他功能(同步、缓存等)。

                  因此对于 ajax 请求的低级控制(同步、缓存等),我们应该使用 $.ajax

                   $.ajax({
                       type: 'GET',
                        url: url,
                        data: data,
                        success: success,
                        dataType: dataType,
                        async:false
                      });
                  

                  【讨论】:

                    【解决方案12】:

                    这是我使用 jQuery 对 ASYNC 请求的简单实现。我希望这对任何人都有帮助。

                    var queueUrlsForRemove = [
                        'http://dev-myurl.com/image/1', 
                        'http://dev-myurl.com/image/2',
                        'http://dev-myurl.com/image/3',
                    ];
                    
                    var queueImagesDelete = function(){
                    
                        deleteImage( queueUrlsForRemove.splice(0,1), function(){
                            if (queueUrlsForRemove.length > 0) {
                                queueImagesDelete();
                            }
                        });
                    
                    }
                    
                    var deleteImage = function(url, callback) {
                        $.ajax({
                            url: url,
                            method: 'DELETE'
                        }).done(function(response){
                            typeof(callback) == 'function' ? callback(response) : null;
                        });
                    }
                    
                    queueImagesDelete();
                    

                    【讨论】:

                      【解决方案13】:

                      因为不推荐使用XMLHttpReponse 同步操作,所以我想出了以下包装XMLHttpRequest 的解决方案。这允许有序的 AJAX 查询,同时本质上仍然是异步的,这对于单次使用的 CSRF 令牌非常有用。

                      它也是透明的,因此 jQuery 等库可以无缝运行。

                      /* wrap XMLHttpRequest for synchronous operation */
                      var XHRQueue = [];
                      var _XMLHttpRequest = XMLHttpRequest;
                      XMLHttpRequest = function()
                      {
                        var xhr   = new _XMLHttpRequest();
                        var _send = xhr.send;
                      
                        xhr.send = function()
                        {
                          /* queue the request, and if it's the first, process it */
                          XHRQueue.push([this, arguments]);
                          if (XHRQueue.length == 1)
                            this.processQueue();
                        };
                      
                        xhr.processQueue = function()
                        {
                          var call = XHRQueue[0];
                          var xhr  = call[0];
                          var args = call[1];
                      
                          /* you could also set a CSRF token header here */
                      
                          /* send the request */
                          _send.apply(xhr, args);
                        };
                      
                        xhr.addEventListener('load', function(e)
                        {
                          /* you could also retrieve a CSRF token header here */
                      
                          /* remove the completed request and if there is more, trigger the next */
                          XHRQueue.shift();
                          if (XHRQueue.length)
                            this.processQueue();
                        });
                      
                        return xhr;
                      };
                      

                      【讨论】:

                        【解决方案14】:

                        由于最初的问题是关于jQuery.get,因此这里值得一提的是(如here 所述)一个可以$.get() 中使用async: false,但最好避免 因为异步XMLHTTPRequest 已被弃用(并且浏览器可能会给出警告):

                        $.get({
                          url: url,// mandatory
                          data: data,
                          success: success,
                          dataType: dataType,
                          async:false // to make it synchronous
                        });
                        

                        【讨论】:

                          猜你喜欢
                          • 2011-10-04
                          • 1970-01-01
                          • 2015-02-10
                          • 1970-01-01
                          • 2023-04-01
                          • 1970-01-01
                          • 2016-10-22
                          相关资源
                          最近更新 更多