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

1079

У меня есть виджет 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-интерфейса в браузере?

  • 25
    Правильный способ решить эту проблему - переписать обещания точки расширения вашего виджета. Этот способ позволит вам легко настроить асинхронное действие (например, ajax-запрос) как beforecreate .
  • 2
    Я предлагаю функцию Саджак. У нас есть буква Т? Да, Ванна, дай мне 3 Ти.
Показать ещё 4 комментария
Теги:
asynchronous

14 ответов

1053
Лучший ответ

Из документации 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
    });
}
  • 92
    Точно, невозможно использовать get (), post (), load () для синхронных вызовов. Только ajax () имеет параметр «async», который может быть установлен в «false».
  • 36
    @ SLA80 Нет. Начиная с jQuery 1.1: stackoverflow.com/questions/6849686/…
Показать ещё 11 комментариев
239

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

jQuery.ajaxSetup({async:false});

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

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

jQuery.ajaxSetup({async:true});

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

  • 55
    Это действительно плохая идея, зачем вы устанавливаете ее на глобальном уровне? А потом будет вынужден сбросить его после?
  • 6
    По крайней мере, это лучшая плохая идея. вместо того, чтобы говорить: «НЕТ $.ajax() КРОМЕ $.ajax() ». ;)
Показать ещё 1 комментарий
134

Отличное решение! Я заметил, что когда я попытался реализовать его, если бы я вернул значение в предложение 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;
}
  • 14
    Это потому, что вы возвращали значение из обратного вызова, а не из getWhatever . Таким образом, вы ничего не вернули, т.е. undefined из вашего getWhatever .
  • 4
    это абсолютно неправильно. Вы не можете быть уверены, что асинхронный вызов завершился при возврате 'strReturn'.
Показать ещё 1 комментарий
86

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


//example use
var msg=getURL("message.php");
alert(msg);
  • 8
    Обратите внимание, что, однако, вы можете получить это: «Синхронный XMLHttpRequest в основном потоке устарел из-за его пагубных последствий для конечного пользователя».
  • 4
    @Hari Как выполнить синхронный вызов ajax с использованием jQuery без использования основного потока?
Показать ещё 1 комментарий
21

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

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

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

  • Ajax-вызов в ваш собственный домен - и выполните междоменный вызов на стороне сервера
  • Измените свой код для работы асинхронно
  • Используйте библиотеку "секвенсер функций", такую ​​как Frame.js(этот ответ)
  • Заблокируйте пользовательский интерфейс вместо блокировки выполнения (этот ответ) (мой любимый способ)
19

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

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

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

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

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

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

async function foo() {
  var res = await fetch(url)
  console.log(res.ok)
  var json = await res.json()
  console.log(json)
}
  • 0
    Обратите внимание, что вы должны обернуть свой вызов await fetch(url) в блок try... catch чтобы перехватить любые асинхронные ошибки.
  • 2
    функция foo была преобразована в обещание, просто используя вначале слово async , поэтому вы можете просто выполнить foo().catch(...) чтобы перехватить его
10

Я использовал ответ, данный 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 является ложным. Если вы должны были установить async: true, возвращаемый объект jqxhr недействителен во время вызова AJAX, только позже, когда асинхронный вызов будет завершен, но слишком поздно установить переменную ответа.

9

С async: false вы получаете заблокированный браузер. Для неблокирующего синхронного решения вы можете использовать следующее:

ES6/ECMAScript2015

С ES6 вы можете использовать генератор и 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

С ES7 вы можете просто использовать asyc wait:

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
    })(); 
}
  • 2
    Обратите внимание, что вы можете просто передать async function в beforecreate beforecreate: async function(.... и удалить вызываемую асинхронную функцию самостоятельно
6

Это пример:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
  • 0
    Какой смысл использовать async false с решением на основе обещаний. Это только блокирует браузер без какой-либо выгоды.
  • 0
    @GoneCoding есть ситуации, когда вы хотите выполнить код в точной последовательности, в этой ситуации мы можем использовать async false
Показать ещё 1 комментарий
5

Клиентский код JavaScript на JavaScript

function isValidLogin() {
    var flag = false;
    var Response = $.ajax({
        type: "POST",
        dataType: "json",
        contentType: "application/json; charset=utf-8",
        url: "UPSLabelFormCA.aspx/IsValidLogin",
        async: false
    }).responseText;

    var toJson = jQuery.parseJSON(Response);
    if (toJson.d[0].Message == 'SUCCESS') {
        flag = true;
    }
    return flag;
}

Код сервера ASP.NET

[WebMethod]
public static List<ShipInfo> IsValidLogin()
{
    List<ShipInfo> loginStatus = new List<ShipInfo>();
    if (HttpContext.Current.User.Identity.IsAuthenticated && HttpContext.Current.Session["Email"] != null)
    {
        loginStatus.Add(new ShipInfo() { Success = true, Message = "SUCCESS" });
    }
    else
    {

        loginStatus.Add(new ShipInfo() { Success = false, Message = "FAILED" });
    }
    return loginStatus;

}
2

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

Поскольку синхронная операция 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 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;
};
0

это моя простая реализация для запросов 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();

Ещё вопросы

Сообщество Overcoder
Наверх
Меню