首页 文章

如何让jQuery执行同步而非异步的Ajax请求?

提问于
浏览
1092

我有一个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请求?

13 回答

  • 20
    function getURL(url){
        return $.ajax({
            type: "GET",
            url: url,
            cache: false,
            async: false
        }).responseText;
    }
    
    
    //example use
    var msg=getURL("message.php");
    alert(msg);
    
  • 50

    所有这些答案都错过了使用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

    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
        });
    }
    
  • 9

    优秀解决方案我注意到当我尝试实现它时,如果我在success子句中返回了一个值,那么它将返回undefined . 我必须将它存储在变量中并返回该变量 . 这是我提出的方法:

    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;
    }
    
  • 23

    我使用了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''JSON' 并将 .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 变量为时已晚 .

  • 2

    这是一个例子:

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

    Firstly we should understand when we use $.ajax and when we use $.get/$.post

    当我们需要对ajax请求进行低级别控制时,例如请求标头设置,缓存设置,同步设置等,然后我们应该去$ .ajax .

    $ .get / $ .post:当我们不需要对ajax请求进行低级别控制时 . 只需将数据获取/发布到服务器 . It is shorthand of

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

    因此我们不能使用$ .get / $ .post的其他功能(同步,缓存等) .

    Hence for low level control(sync,cache,etc.) over ajax request,we should go for $.ajax

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

    因为不推荐使用 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;
    };
    
  • 10

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

    jQuery.ajaxSetup({async:false});
    

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

    然后再打开一次

    jQuery.ajaxSetup({async:true});
    

    我想这与@Adam所建议的相同,但对于那些想要将 jQuery.get()jQuery.post() 重新配置为更复杂的 jQuery.ajax() 语法的人来说,它可能会有所帮助 .

  • 1067

    请记住,如果您正在进行跨域Ajax调用(通过使用JSONP) - 您 can't 同步执行,jQuery将忽略 async 标志 .

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

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

    • Ajax-调用您自己的域 - 并进行跨域调用服务器端

    • 将代码更改为异步工作

    • 使用像Frame.js这样的"function sequencer"库(这个answer

    • 阻止UI而不是阻止执行(这是answer)(我最喜欢的方式)

  • 83

    这是我使用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();
    
  • 0

    使用 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等待:

    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
        })(); 
    }
    
  • 0

    注意:由于这个原因,您不应该使用async:

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

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

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

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

    如果你想这样做仍然感觉像是阻塞那么你应该使用async / await,也许还有一些基于promises的ajax,比如新的Fetch API

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

相关问题