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

У меня есть виджет JavaScript, который обеспечивает стандартные точки расширения. Одним из них является beforecreate функция. Должно вернуться false чтобы предотвратить создание элемента.

Я добавил Ajax-вызов в эту функцию, используя 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);
  });
}

Но я хочу, чтобы мой виджет не создавал элемент, поэтому я должен вернуться false в функции-матери, а не в обратном вызове. Есть ли способ выполнить синхронный AJAX-запрос с помощью jQuery или любого другого API в браузере?

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() синтаксис.

Отличное решение! Когда я попытался реализовать это, я заметил, что если я возвращаю значение в предложении успеха, оно возвращается как неопределенное. Я должен был сохранить его в переменной и вернуть эту переменную. Это метод, который я придумал:

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. Использование библиотеки управления потоком данных решит эту проблему, не отключая браузер. Вот пример с 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);

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

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

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

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

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

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

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

Имейте в виду, что если вы делаете междоменный вызов Ajax (используя JSONP) - вы не можете сделать это синхронно, async флаг будет игнорироваться jQuery.

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

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

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

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

Я добавил dataType 'JSON' и изменил .responseText на responseJSON.

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

Серверная часть должна возвращать ответ в правильном (правильно сформированном) JSON, иначе возвращаемый объект будет неопределенным.

Есть два аспекта, которые следует учитывать при ответе на исходный вопрос. Один говорит Ajax выполнять синхронно (путем установки async: false), а другой возвращает ответ через оператор возврата вызывающей функции, а не в функцию обратного вызова.

Я также попробовал это с 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;
}

Обратите внимание, что приведенный выше код работает только в случае, когда async имеет значение 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 и когда мы используем $.get/$. Post

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

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

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

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

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

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

Это моя простая реализация запросов ASYNC с помощью 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, Это позволяет упорядоченным 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;
};

Поскольку исходный вопрос был о jQuery.get, здесь стоит упомянуть, что (как упоминалось здесь) можно использоватьasync: false в $.get()но в идеале избегайте этого, поскольку асинхронныйXMLHTTPRequest устарела (и браузер может выдать предупреждение):

$.get({
  url: url,// mandatory
  data: data,
  success: success,
  dataType: dataType,
  async:false // to make it synchronous
});
Другие вопросы по тегам