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

我有一个提供标准扩展点的 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 请求?

答案

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

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

jQuery.ajaxSetup({async:false});

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

然后再打开一次

jQuery.ajaxSetup({async:true});

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

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

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 进行 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();
}
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);

请记住,如果您正在执行跨域 Ajax 调用(通过使用JSONP )- 您无法同步进行,那么 jQuery 将忽略async标志。

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

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

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

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

从 Gecko 30.0(Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27)开始,由于对用户体验的负面影响,不赞成在主线程上执行同步请求。

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

不赞成在主线程上使用同步 XMLHttpRequest,因为它会对最终用户的体验产生不利影响。如需更多帮助,请访问https://xhr.spec.whatwg.org/

如果您正在执行此类操作,则可能会中断您的页面,因为它可能会在一天中停止工作。

如果您想要以一种仍然是同步的方式但仍然不会阻塞的方式来执行此操作,则应使用 async / await,还可能使用一些基于 Promise 的 ajax,例如新的Fetch API

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

当用户导航离开或关闭页面时, Edit chrome 正在处理 “ 禁止在页面关闭中同步 XHR” 。这涉及到卸载,卸载,页面隐藏和可见性更改。

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

页面也可能会禁用带有 HTTP 标头或 iframe 的 allow 属性的同步请求

我使用了 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 调用返回时将是无效的,只有在异步调用完成后才有效,但这太晚了,无法设置响应变量。

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

ES6 / ECMAScript2015

使用 ES6,您可以使用 generator 和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
    });
}

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

这是示例:

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