Jak mogę uzyskać od jQuery wykonanie synchronicznego, a nie asynchronicznego żądania Ajax?


1207

Mam widget JavaScript, który zapewnia standardowe punkty rozszerzenia. Jednym z nich jest beforecreatefunkcja. Powinien powrócić, falseaby zapobiec tworzeniu elementu.

Dodałem wywołanie Ajax do tej funkcji za pomocą 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);
  });
}

Chcę jednak zapobiec tworzeniu elementu przez mój widget, więc powinienem powrócić falsew funkcji macierzystej, a nie w wywołaniu zwrotnym. Czy istnieje sposób wykonania synchronicznego żądania AJAX przy użyciu jQuery lub innego interfejsu API przeglądarki?


30
Właściwym sposobem rozwiązania tego jest przepisanie obietnicy użycia punktu rozszerzenia widżetu. W ten sposób można łatwo ustawić akcję asynchroniczną (np. Żądanie ajax) jako beforecreate.
— Kos

3
Proponuję funkcję Sajaka. Czy mamy literę T? Tak vanna, daj mi 3 T.
— Cody O'Dell,

2
@Kos jest na miejscu. Synchronizacja XHR nie jest tutaj wymagana
— Michael Westcott,

1
Jeśli ludzie proszą mnie o 1 radę podczas uruchamiania javascript, mówię: zaakceptuj asynchroniczny charakter javascript, nie próbuj z tym walczyć.
— Emmanuel Opóźnienie,

3
To pytanie jest jak pytanie „jak przechowywać hasła użytkowników w postaci zwykłego tekstu?” Pewnie, że jest odpowiedź, ale nie rób tego.
— Vectorjohn,

Odpowiedzi:


1165

Z dokumentacji jQuery : określasz opcję asynchroniczną na false, aby uzyskać synchroniczne żądanie Ajax. Następnie oddzwonienie może ustawić niektóre dane przed kontynuowaniem funkcji matki.

Oto jak wyglądałby Twój kod, gdyby został zmieniony zgodnie z sugestią:

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
Dokładnie, nie można używać get (), post (), load () do wywołań synchronicznych. Tylko ajax () ma parametr „async”, który można ustawić na „false”.
— SLA80

39
@ SLA80 Nie. Od jQuery 1.1: stackoverflow.com/questions/6849686/...
— StuperUser

16
@qualidafial mój komentarz dotyczy niepoprawnego komentarza SLA80; możliwe jest użycie get (), post (), load () dla wywołań synchronicznych.
— StuperUser

9
async false nie jest już obsługiwany dla jQuery> = 1.8. Zobacz api.jquery.com/jQuery.ajax
— ken

22
@ken async: falsejest nadal obsługiwany w jQuery> = 1.8. Zdolność do używania async: falsez jqXHR ( $.Deferred) jest przestarzała. Innymi słowy, należy zastosować nowsze opcje powodzenia / błędu / pełnego wywołania zwrotnego zamiast starszych metod, np jqXHR.done().
— Ben Johnson

255

Możesz przełączyć konfigurację Ajax jQuery w tryb synchroniczny, dzwoniąc

jQuery.ajaxSetup({async:false});

A następnie wykonuj połączenia Ajax za pomocą jQuery.get( ... );

Następnie po prostu włącz go ponownie

jQuery.ajaxSetup({async:true});

Myślę, że działa to tak samo, jak sugeruje @Adam, ale może być pomocne dla kogoś, kto chce zmienić konfigurację swojej jQuery.get()lub jQuery.post()bardziej złożonej jQuery.ajax()składni.


56
To naprawdę zły pomysł, dlaczego miałbyś ustawić go na poziomie globalnym? A potem trzeba go rozbroić?
— Juan Mendes

11
Przynajmniej jest to najlepszy zły pomysł. zamiast mówić: „NIE MA ŻADNEGO WYJĄTKU $.ajax()”. ;)
— Rzassar

136

Doskonałe rozwiązanie! Zauważyłem, gdy próbowałem go zaimplementować, że jeśli zwróciłem wartość w klauzuli sukcesu, to wróciła jako niezdefiniowana. Musiałem zapisać go w zmiennej i zwrócić tę zmienną. Oto metoda, którą wymyśliłem:

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
To dlatego, że zwracałeś wartość z wywołania zwrotnego, a nie z getWhatever. W ten sposób nic nie zwróciłeś, tj. undefinedOd swojego getWhatever.
— Lekkość ściga się na orbicie

6
to jest absolutnie złe. Nie możesz być pewien, że asynchroniczne wywołanie zakończyło się podczas zwracania „strReturn”.
— Thomas Fritz

61
Jest to wywołanie synchroniczne (async: false).
— James w Indy,

85

We wszystkich tych odpowiedziach nie ma sensu, aby wykonanie wywołania Ajax za pomocą async: false spowodowało zawieszenie się przeglądarki do momentu zakończenia żądania Ajax. Korzystanie z biblioteki kontroli przepływu rozwiąże ten problem bez zawieszania przeglądarki. Oto przykład z 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
Połączenia synchroniczne są przydatne, jeśli chcesz zbudować szybką wiązkę testową dla zaplecza REST i wolisz prostotę niż piekło wywołania zwrotnego.
— 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);

10
Pamiętaj jednak, że możesz otrzymać: „Synchroniczne XMLHttpRequest w głównym wątku jest przestarzałe ze względu na jego szkodliwy wpływ na doświadczenie użytkownika końcowego”.
— Hari

5
@Hari Jaki jest sposób wykonywania synchronicznego wywołania ajax za pomocą jQuery bez użycia głównego wątku?
— Jose Nobile,

7
Nie widzę, aby ta odpowiedź miała coś do zaoferowania, jest to tylko zaakceptowana odpowiedź zawarta w funkcji i udzielona cztery lata później. Źle jest doradzać ludziom C + P, ponieważ funkcja nie wskazuje, co robi. „Więc getURLvs get? Dlaczego zawiesi się moja przeglądarka?” itp.
— moopet

27

Uwaga: Nie należy używać z async: falsepowodu tych komunikatów ostrzegawczych:

Począwszy od Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), żądania synchroniczne w głównym wątku zostały wycofane ze względu na negatywne skutki dla wygody użytkownika.

Chrome ostrzega nawet o tym w konsoli:

Synchroniczne XMLHttpRequest w głównym wątku jest przestarzałe ze względu na jego szkodliwy wpływ na doświadczenie użytkownika końcowego. Aby uzyskać więcej pomocy, sprawdź https://xhr.spec.whatwg.org/ .

Może to spowodować uszkodzenie strony, jeśli robisz coś takiego, ponieważ może przestać działać każdego dnia.

Jeśli chcesz to zrobić w sposób, który nadal wydaje się być synchroniczny, ale nadal nie blokuje, powinieneś użyć async / await i prawdopodobnie również jakiegoś ajax, który jest oparty na obietnicach, takich jak nowy interfejs API Fetch

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

Edycja chrome pracuje nad Niedozwoleniem synchronizacji XHR podczas zamykania strony, gdy strona jest nawigowana lub zamykana przez użytkownika. Obejmuje to przed załadowaniem, rozładowaniem, ukryciem strony i zmianą widoczności.

jeśli jest to przypadek użycia to może chcesz przyjrzeć się navigator.sendBeacon zamiast

Strona może również wyłączyć żądanie synchronizacji z nagłówkami HTTP lub atrybutem iframe


Pamiętaj, że powinieneś zawinąć swoje await fetch(url)połączenie w try... catchblok, aby wychwycić wszelkie błędy asynchroniczne.
— inostia

4
funkcja foo została przekonwertowana na obietnicę, po prostu używając słowa asyncna początku, więc możesz po prostu go foo().catch(...)złapać
— Endless

Chrome już zabronił asynchronizacji wewnątrz strony, beforeunloadale potem cofnął zmianę po negatywnej opinii bugs.chromium.org/p/chromium/issues/detail?id=952452
— Alex

26

Pamiętaj, że jeśli wykonujesz wywołanie Ajax między domenami (używając JSONP ) - nie możesz tego zrobić synchronicznie, asyncflaga zostanie zignorowana przez jQuery.

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

Do połączeń JSONP możesz użyć:

  1. Ajax-call do własnej domeny - i wykonaj połączenie między domenami po stronie serwera
  2. Zmień kod, aby działał asynchronicznie
  3. Użyj biblioteki „sekwencera funkcji”, takiego jak Frame.js (ta odpowiedź )
  4. Zablokuj interfejs użytkownika zamiast blokować wykonanie (ta odpowiedź ) (mój ulubiony sposób)

12

Z async: falseciebie dostać się zablokowany przeglądarkę. W przypadku nieblokującego rozwiązania synchronicznego można użyć:

ES6 / ECMAScript2015

Z ES6 możesz korzystać z generatora i biblioteki 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

Z ES7 możesz po prostu użyć asyc czekać:

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

3
Zauważ, że możesz po prostu przejść async functiondo wcześniejszego utworzenia beforecreate: async function(....i usunąć samodzielnie wywoływaną funkcję asynchroniczną
— Daniel Krom,

12

Użyłem odpowiedzi udzielonej przez Carcione i zmodyfikowałem ją, aby używała 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');
    }    
}

Dodałem typ danych „JSON” i zmieniłem tekst .responseText na responseJSON .

Pobrałem również status za pomocą właściwości statusText zwróconego obiektu. Zauważ, że jest to stan odpowiedzi Ajax, a nie to, czy JSON jest poprawny.

Back-end musi zwrócić odpowiedź w poprawnym (poprawnie uformowanym) JSON, w przeciwnym razie zwracany obiekt nie zostanie zdefiniowany.

Przy udzielaniu odpowiedzi na oryginalne pytanie należy wziąć pod uwagę dwa aspekty. Jeden mówi Ajaxowi, aby działał synchronicznie (ustawiając async: false ), a drugi zwraca odpowiedź za pomocą instrukcji return funkcji wywołującej, a nie w funkcji wywołania zwrotnego.

Próbowałem też z POST i zadziałało.

Zmieniłem GET na POST i dodałem dane: 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;
}

Zauważ, że powyższy kod działa tylko w przypadku, gdy asynchronizacja jest fałszywa . Jeśli miałbyś ustawić async: true, zwrócony obiekt jqxhr nie byłby prawidłowy w momencie powrotu wywołania AJAX, tylko później, gdy zakończy się wywołanie asynchroniczne, ale jest o wiele za późno, aby ustawić zmienną odpowiedzi .


7

To jest przykład:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});

1
Jaki jest sens korzystania async falsez rozwiązania opartego na obietnicach. To tylko blokuje przeglądarkę bez żadnych korzyści.
— Gone Coding

2
@GoneCoding są sytuacje, w których chcemy wykonać kod w dokładnie sekwencji, w takiej sytuacji możemy użyć asynchronicznego fałszu
— Nikki

1
@Nikki: To absolutnie nie jest sposób, aby uruchamiać je sekwencyjnie. Użyj obietnic asynchronicznych (które ajaxwywołania już zwracają) i .then()lub done()(jak już pokazuje). Blokowanie przeglądarki jest bardzo słabym doświadczeniem użytkownika. Jak powiedziałem, posiadanie async: falsew tym przykładzie jest całkowitą stratą czasu.
— Gone Coding

4

Po pierwsze powinniśmy zrozumieć, kiedy używamy $ .ajax i kiedy używamy $ .get / $. Post

Kiedy wymagamy niskiego poziomu kontroli nad żądaniem ajax, takim jak ustawienia nagłówka żądania, ustawienia buforowania, ustawienia synchroniczne itp., Powinniśmy wybrać $ .ajax.

$ .get / $. post: Gdy nie wymagamy kontroli niskiego poziomu nad żądaniem ajax. Wystarczy pobrać / wysłać dane na serwer. To jest skrót

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

i dlatego nie możemy używać innych funkcji (synchronizacja, pamięć podręczna itp.) z postem $ .get / $.

Stąd dla kontroli niskiego poziomu (synchronizacji, pamięci podręcznej itp.) Nad żądaniem ajax, powinniśmy wybrać $ .ajax

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

2

to moja prosta implementacja dla żądań ASYNC z jQuery. Mam nadzieję, że pomoże to każdemu.

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

Ponieważ XMLHttpReponsedziałanie synchroniczne jest przestarzałe, wymyśliłem następujące rozwiązanie, które się zawija XMLHttpRequest. Pozwala to na uporządkowanie zapytań AJAX, a jednocześnie jest asynchroniczne z natury, co jest bardzo przydatne w przypadku tokenów CSRF do jednorazowego użytku.

Jest również przezroczysty, więc biblioteki takie jak jQuery będą działać płynnie.

/* 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

Ponieważ pierwotne pytanie dotyczyło jQuery.get, warto tutaj wspomnieć, że (jak wspomniano tutaj ) można użyć async: falsew, $.get()ale najlepiej go unikać , ponieważ asynchroniczny XMLHTTPRequestjest przestarzały (a przeglądarka może ostrzec):

$.get({
  url: url,// mandatory
  data: data,
  success: success,
  dataType: dataType,
  async:false // to make it synchronous
});

1
dlaczego jest to przegłosowane?
— alias51

-2

Ustaw asyn:falsew żądaniu ajax, aby było synchroniczne.

Oto przykładowy kod:

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

Może to spowodować brak reakcji ekranu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.