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?

Artem Tichomirow
źródło
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
    });
}
Adam Bellaire
źródło
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.

Sydwell
źródło
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;
}
James w Indy
źródło
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();
}
BishopZ
źródło
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);
Carcione
źródło
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

Nieskończony
źródło
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)
Serge Shultz
źródło
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
    })(); 
}
Spenhouet
źródło
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 .

paulo62
źródło
7

To jest przykład:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
szukanie 9x
źródło
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
    });
Sheo Dayal Singh
źródło
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();
Felipe Marques
źródło
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;
};
Geoffrey
źródło
-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
});
Anupam
źródło
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.

Usman Afzal
źródło