我有一个提供标准扩展点的JavaScript小部件。其中之一是beforereate函数。它应该返回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,而不是在回调中返回false。是否有一种方法可以使用jQuery或任何其他浏览器内API执行同步AJAX请求?


当前回答

因为最初的问题是关于jQuery的。在这里值得一提的是(正如这里提到的),可以在$.get()中使用async: false,但理想情况下要避免它,因为异步XMLHTTPRequest已弃用(浏览器可能会给出警告):

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

其他回答

因为xmlhttpreonse同步操作已弃用,所以我提出了以下包装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;
};

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

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

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

ajax—调用您自己的域—并在服务器端执行跨域调用 将代码更改为异步工作 使用像Frame.js这样的“函数排序器”库(这个答案) 阻塞UI而不是阻塞执行(这个答案)(我最喜欢的方式)

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

ES7

在ES7中,你可以使用async 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
    })(); 
}

优秀的解决方案!我注意到,当我试图实现它时,如果我在success子句中返回一个值,它返回为未定义。我必须把它存储在一个变量中,然后返回那个变量。这是我想出的方法:

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

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