How can I get jQuery to perform a synchronous, rat

2018-12-31 00:33发布

I have a JavaScript widget which provides standard extension points. One of them is the beforecreate function. It should return false to prevent an item from being created.

I've added an Ajax call into this function using jQuery:

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

But I want to prevent my widget from creating the item, so I should return false in the mother-function, not in the callback. Is there a way to perform a synchronous AJAX request using jQuery or any other in-browser API?

13条回答
无与为乐者.
2楼-- · 2018-12-31 01:07

Keep in mind that if you're doing a cross-domain Ajax call (by using JSONP) - you can't do it synchronously, the async flag will be ignored by jQuery.

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

For JSONP-calls you could use:

  1. Ajax-call to your own domain - and do the cross-domain call server-side
  2. Change your code to work asynchronously
  3. Use a "function sequencer" library like Frame.js (this answer)
  4. Block the UI instead of blocking the execution (this answer) (my favourite way)
查看更多
浪荡孟婆
3楼-- · 2018-12-31 01:09

Because XMLHttpReponse synchronous operation is deprecated I came up with the following solution that wraps XMLHttpRequest. This allows ordered AJAX queries while still being asycnronous in nature, which is very useful for single use CSRF tokens.

It is also transparent so libraries such as jQuery will operate seamlessly.

/* 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;
};
查看更多
梦寄多情
4楼-- · 2018-12-31 01:10

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
    });
查看更多
浅入江南
5楼-- · 2018-12-31 01:11

Note: You shouldn't use async due to this:

Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), synchronous requests on the main thread have been deprecated due to the negative effects to the user experience.

Chrome even warns about this in the console:

Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.

This could break your page if you are doing something like this since it could stop working any day.

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)
}
查看更多
只若初见
6楼-- · 2018-12-31 01:13

this is my simple implementation for ASYNC requests with jQuery. I hope this help anyone.

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();
查看更多
姐姐魅力值爆表
7楼-- · 2018-12-31 01:15

With async: false you get yourself a blocked browser. For a non blocking synchronous solution you can use the following:

ES6/ECMAScript2015

With ES6 you can use a generator & the 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

With ES7 you can just use 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
    })(); 
}
查看更多
登录 后发表回答