Как я могу заставить jQuery выполнить синхронный, а не асинхронный запрос Ajax?

у меня есть виджет JavaScript, который предоставляет стандартные точки расширения. Один из них -

13 ответов


С документация 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
    });
}

вы можете поместить настройку Ajax jQuery в синхронный режим, вызвав

jQuery.ajaxSetup({async:false});

а затем выполните вызовы Ajax с помощью jQuery.get( ... );

затем просто включите его еще раз

jQuery.ajaxSetup({async:true});

Я думаю, это работает так же, как предложил @Adam, но это может быть полезно для тех, кто хочет перенастроить их jQuery.get() или jQuery.post() для более сложных jQuery.ajax() синтаксис.


отличное решение! Я заметил, когда пытался реализовать его, что если я вернул значение в предложении 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;
}

все эти ответы пропускают момент, когда выполнение вызова Ajax с помощью async: false приведет к зависанию браузера до завершения запроса Ajax. Использование библиотеки управления потоком позволит решить эту проблему без зависания браузера. Вот пример с рамка.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) - вы не могу сделать это синхронно,async флаг будет проигнорирован jQuery.

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

для JSONP-вызовов вы можете использовать:

  1. Ajax-вызов на свой собственный домен-и выполните междоменный вызов на стороне сервера
  2. измените код для работы асинхронно
  3. используйте библиотеку "function sequencer", такую как Frame.js (это ответ)
  4. блокировать пользовательский интерфейс вместо блокировки выполнения (это ответ) (мой любимый способ)

Примечание: Вы не должны использовать async из-за этого:

начиная с Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), синхронные запросы в основном потоке устарели из-за негативных последствий для работы пользователя.

Chrome даже предупреждает об этом в консоли:

синхронный XMLHttpRequest в основном потоке устарел из-за его пагубного влияния на работу конечного пользователя. Для больше помощи, проверьте https://xhr.spec.whatwg.org/.

Это может сломать вашу страницу, если вы делаете что-то вроде этого, поскольку она может перестать работать в любой день.

Если вы хотите сделать это так, чтобы все еще было похоже, если это синхронно, но все еще не блокируется, вы должны использовать async / await и, вероятно, также некоторый ajax, основанный на обещаниях, таких как new Fetch API

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

я использовал ответ, данный 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');
    }    
}

добавил тип of 'JSON' и изменил .responseText to responseJSON.

Я также получил статус с помощью statustext также свойства возвращаемого объекта. Обратите внимание, что это состояние ответа Ajax, а не является ли JSON допустимым.

back-end должен вернуть ответ в правильном (хорошо сформированном) 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;
}

обратите внимание, что приведенный выше код работает только в том случае, когда асинхронные is false. Если бы вы установили async: true возвращенный объект jqxhr не будет действительным во время возврата вызова AJAX, только позже, когда асинхронный вызов завершен, но это слишком поздно, чтобы установить ответ переменной.


С 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 вы можете просто использовать 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..
});

во-первых, мы должны понять, когда мы используем $.AJAX и когда мы используем $.получить.$/пост

когда нам требуется контроль низкого уровня над запросом ajax, таким как настройки заголовка запроса, настройки кэширования, синхронные настройки и т. д.тогда мы должны пойти за $.Аякс.

$.получить.$/post: Когда нам не требуется контроль низкого уровня над запросом ajax.Только простой get / post данные на сервер.это сокращение от

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

и, следовательно, мы не можете использовать другие функции(синхронизация,кэш и т. д.) с.$ получить.$/должность.

следовательно, для управления низким уровнем (синхронизация, кэш и т. д.) по запросу ajax мы должны пойти на $.ajax

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

Это моя простая реализация для асинхронных запросов с помощью jQuery. Надеюсь, это поможет кому-нибудь.

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

, потому что XMLHttpReponse синхронная операция устарела я придумал следующее решение, которое обертывает XMLHttpRequest. Это позволяет заказать Аякс запросы, в то же время asycnronous в природе, что очень полезно для одноразового использования 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;
};