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

1208

У меня есть виджет 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в функции mother, а не в функции обратного вызова. Есть ли способ выполнить синхронный AJAX-запрос с помощью jQuery или любого другого API в браузере?

Артем Тихомиров
источник
30
Правильный способ решить эту проблему - переписать обещания точки расширения вашего виджета. Этот способ позволит вам легко настроить асинхронное действие (например, ajax-запрос) как beforecreate.
Кос
3
Я предлагаю функцию Саджак. У нас есть буква Т? Да, Ванна, дай мне 3 Ти.
Коди О'Делл
2
@Kos на месте. Синхронизация XHR здесь не требуется
Майкл Уэсткотт
1
Если при запуске javascript меня попросят дать один совет, я скажу: примите асинхронный характер javascript, не пытайтесь бороться с этим.
Эммануэль Задержка
3
Этот вопрос похож на вопрос: «Как мне сохранить мои пароли пользователей в виде простого текста?» Конечно, есть ответ, но не делайте этого.
Vectorjohn

Ответы:

1165

Из документации 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
    });
}
Адам Беллер
источник
97
Точно, невозможно использовать get (), post (), load () для синхронных вызовов. Только ajax () имеет параметр «async», который может быть установлен в «false».
SLA80
39
@ SLA80 Нет. Начиная с jQuery 1.1: stackoverflow.com/questions/6849686/…
StuperUser
16
@qualidafial мой комментарий нацелен на неправильный комментарий SLA80; можно использовать get (), post (), load () для синхронных вызовов.
StuperUser
9
Значение async false больше не поддерживается для jQuery> = 1.8. Обратитесь к api.jquery.com/jQuery.ajax
кен
22
@ken async: falseвсе еще поддерживается в jQuery> = 1.8. Возможность использовать async: falseс jqXHR ( $.Deferred) - это то, что устарело. Другими словами, нужно использовать более новые параметры обратного вызова success / error / complete вместо устаревших методов, например jqXHR.done().
Бен Джонсон
255

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

jQuery.ajaxSetup({async:false});

А затем выполните ваши Ajax-вызовы, используя jQuery.get( ... );

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

jQuery.ajaxSetup({async:true});

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

Sydwell
источник
56
Это действительно плохая идея, зачем вы устанавливаете ее на глобальном уровне? А потом будет вынужден сбросить его после?
Хуан Мендес
11
По крайней мере, это лучшая плохая идея. вместо того, чтобы говорить: «НЕТ, ИСКЛЮЧЕНО $.ajax()». ;)
Rzassar
136

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

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;
}
Джеймс в Инди
источник
16
Это потому, что вы возвращали значение из обратного вызова, а не из getWhatever. Таким образом , вы возвратили ничего ИЭ undefinedот вашего getWhatever.
Гонки легкости на орбите
6
это абсолютно неправильно. Вы не можете быть уверены, что асинхронный вызов завершился при возврате 'strReturn'.
Томас Фриц
61
Это синхронный вызов (async: false).
Джеймс в Инди
85

Во всех этих ответах упущено, что выполнение вызова 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();
}
BishopZ
источник
4
Синхронные вызовы удобны, если вы хотите собрать быстрый тестовый комплект для REST-бэкенда и предпочли бы простоту аду обратного вызова.
Distortum
50
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);
Carcione
источник
10
Однако обратите внимание, что вы можете получить следующее: «Синхронный XMLHttpRequest в основном потоке устарел из-за его пагубных последствий для конечного пользователя».
Хари
5
@Hari Как выполнить синхронный вызов ajax с использованием jQuery без использования основного потока?
Хосе Нобиле
7
Я не вижу, чтобы этот ответ что-то предлагал, это просто принятый ответ, заключенный в функцию и отвеченный четыре года спустя. Плохо советовать людям C + P, потому что функция не указывает, что она делает. "Так getURLпротив get? Почему один браузер зависает?" и т.д.
moopet
27

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

Начиная с 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)
}

Правка chrome работает над запретом синхронизации XHR при закрытии страницы, когда страница перемещается или закрывается пользователем. Это включает в себя загрузку, выгрузку, скрытие страниц и изменение видимости.

если это ваш вариант использования, вы можете вместо этого взглянуть на navigator.sendBeacon

На странице также можно отключить синхронизацию req либо с помощью заголовков http, либо с помощью атрибута allow iframe.

Бесконечный
источник
Обратите внимание, что вы должны заключить ваш await fetch(url)вызов в try... catchблок, чтобы перехватить любые асинхронные ошибки.
Иноастия
4
функция foo была преобразована в обещание, просто используя слово asyncв начале, так что вы можете просто foo().catch(...)поймать его
Endless
Chrome уже запретил асинхронный ajax внутри страницы, beforeunloadно затем отменил изменение обратно после отрицательного отзыва bugs.chromium.org/p/chromium/issues/detail?id=952452
Алекс,
26

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

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

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

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

С 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
    })(); 
}
Spenhouet
источник
3
Обратите внимание, что вы можете просто перейти async functionк beforecreate beforecreate: async function(....и удалить асинхронную функцию, вызываемую самим собой
Даниэль Кром
12

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

paulo62
источник
7

Это пример:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
searching9x
источник
1
Какой смысл использовать async falseрешение на основе обещаний. Это только блокирует браузер без какой-либо выгоды.
Ушел кодирование
2
@GoneCoding бывают ситуации, когда требуется выполнить код в точной последовательности, в этой ситуации мы можем использовать асинхронное false
Nikki
1
@Nikki: Это абсолютно не способ запустить их последовательно. Используйте асинхронные обещания ( ajaxвызовы которых уже возвращаются) и .then()или done()(как это уже показано). Блокировка браузера - очень плохой пользовательский опыт. Как я уже сказал, наличие async: falseв этом примере - пустая трата времени.
Ушел
4

Во-первых, мы должны понять, когда мы используем $ .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
    });
Шео Дайал Сингх
источник
2

это моя простая реализация запросов 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();
Фелипе Маркиз
источник
2

Поскольку 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;
};
Джеффри
источник
-1

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

$.get({
  url: url,// mandatory
  data: data,
  success: success,
  dataType: dataType,
  async:false // to make it synchronous
});
Anupam
источник
1
почему это понижено?
alias51
-2

Набор asyn:false запрос ajax, чтобы сделать его синхронным.

Вот пример кода:

 $.ajax({
    url: 'some_url',
    type: "GET",
    async: false,
    cache: false,
    dataType: "JSON",
    data: { 
      "p1": v1, 
      "p2": v2
    },
    success: function(result) {
    }  
  }
});

Это может сделать экран не отвечает.

Усман Афзал
источник