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

How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?

提问人:Artem Tikhomirov 提问时间:9/25/2008 最后编辑:Artem Tikhomirov 更新时间:11/16/2020 访问量:825904

问:

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

我使用 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);
  });
}

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

JavaScript jQuery Ajax 异步

评论

36赞 Kos 12/22/2013
解决这个问题的正确方法是重写小部件的扩展点使用承诺。这种方式可以很容易地将异步操作(如ajax请求)设置为.beforecreate
3赞 Cody O'Dell 4/14/2016
我提出了 Sajak 函数。我们有字母 T 吗?是的,vanna,给我 3 个 T。
2赞 Michael Westcott 10/9/2016
@Kos是正确的。此处不需要同步 XHR
4赞 Emmanuel Delay 12/14/2017
如果人们在启动 javascript 时问我 1 条建议,我会说:拥抱 javascript 的异步特性,不要试图与之抗争。
6赞 Vectorjohn 10/30/2018
这个问题就像问“如何以纯文本形式存储我的用户密码?当然有答案,但不要这样做。

答:

1250赞 Adam Bellaire 9/25/2008 #1

jQuery 文档中:将异选项指定为 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
    });
}

评论

106赞 SLA80 5/16/2010
确切地说,不可能使用 get()、post()、load() 进行同步调用。只有 ajax() 有 “async” 参数,可以设置为 “false”。
42赞 StuperUser 8/2/2011
@SLA80 没有。从 jQuery 1.1 开始:stackoverflow.com/questions/6849686/...
4赞 Vladimir Fokow 12/21/2021
请注意,不推荐使用 。如果您使用它,您将在控制台中看到以下消息:“主线程上的同步 XMLHttpRequest 已被弃用,因为它对最终用户的体验产生了不利影响。如需更多帮助,xhr.spec.whatwg.orgasync: false"
0赞 AbhiAbzs 5/27/2022
参考这个:也可以使用返回的变量,或者你可以在调用点创建一个 promise 并在其中调用函数。https://www.taniarascia.com/how-to-promisify-an-ajax-call/jqxhr
149赞 James in Indy 4/7/2010 #2

优秀的解决方案!当我尝试实现它时,我注意到,如果我在 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;
}

评论

66赞 James in Indy 3/6/2012
这是一个同步调用 (async:false)。
276赞 Sydwell 4/13/2011 #3

您可以通过调用

jQuery.ajaxSetup({async:false});

然后使用jQuery.get( ... );

然后再次打开一次

jQuery.ajaxSetup({async:true});

我想它的工作原理与@Adam建议的相同,但对于确实想要重新配置或更复杂的语法的人来说,它可能会有所帮助。jQuery.get()jQuery.post()jQuery.ajax()

55赞 Carcione 4/25/2012 #4
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);
94赞 BishopZ 4/29/2012 #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();
}
10赞 searching9x 8/16/2014 #6

示例如下:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
18赞 paulo62 11/15/2014 #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”的数据类型,并将.responseText更改为responseJSON。

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

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

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

我也用 POST 尝试过,它奏效了。

我将 GET 更改为 POST 并添加了数据: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 调用返回时无效,只有在异步调用完成后才有效,但这太晚了,无法设置响应变量。

31赞 Serge Shultz 5/10/2015 #8

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

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

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

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

有了你,你就会得到一个被阻止的浏览器。 对于非阻塞同步解决方案,可以使用以下方法:async: false

ES6/ECMAScript2015 格式

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

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

DSV公司

在 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
    })(); 
}
31赞 Endless 12/13/2016 #10

注意:由于出现以下警告消息,您不应使用:async: false

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

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

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

如果您正在做这样的事情,这可能会破坏您的页面,因为它随时可能停止工作。

If you want to do it a way that still feels like if it's synchronous but still don't block then you should use async/await and probably also some ajax that is based on promises like the new Fetch API

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

Edit chrome is working on Disallowing sync XHR in page dismissal when the page is being navigated away or closed by the user. This involves beforeunload, unload, pagehide and visibilitychange.

if this is your use case then you might want to have a look at navigator.sendBeacon instead

It is also possible for the page to disable sync req with either http headers or iframe's allow attribute

7赞 Sheo Dayal Singh 10/3/2017 #11

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

When we require low level control over the ajax request such as request header settings, caching settings, synchronous settings etc.then we should go for $.ajax.

$.get/$.post: When we do not require low level control over the ajax request.Only simple get/post the data to the server.It is shorthand of

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

and hence we can not use other features(sync,cache etc.) with $.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
    });
6赞 Felipe Marques 2/16/2018 #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();
5赞 Geoffrey 4/29/2018 #13

由于不推荐使用同步操作,因此我想出了以下解决方案,该解决方案将 .这允许有序的 AJAX 查询,同时本质上仍然是无菌的,这对于一次性 CSRF 令牌非常有用。XMLHttpReponseXMLHttpRequest

它也是透明的,因此像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;
};
1赞 Anupam 1/12/2020 #14

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

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