Wydaje się, że prawie każdy wykonuje żądania asynchroniczne za pomocą XMLHttpRequest, ale oczywiście fakt, że istnieje możliwość wykonywania żądań synchronicznych, wskazuje, że może to być uzasadniony powód. Więc jaki może być ten ważny powód?
Wydaje się, że prawie każdy wykonuje żądania asynchroniczne za pomocą XMLHttpRequest, ale oczywiście fakt, że istnieje możliwość wykonywania żądań synchronicznych, wskazuje, że może to być uzasadniony powód. Więc jaki może być ten ważny powód?
Odpowiedzi:
Myślę, że mogą stać się bardziej popularne w miarę postępu standardów HTML 5. Jeśli aplikacja internetowa ma dostęp do pracowników internetowych, mógłbym przewidzieć, że programiści będą korzystali z dedykowanego pracownika internetowego do wykonywania synchronicznych żądań, aby, jak powiedział Jonathan, upewnić się, że jedno żądanie wystąpi przed drugim. Przy obecnej sytuacji jednego wątku jest to mniej niż idealny projekt, ponieważ blokuje się do zakończenia żądania.
Synchroniczne XHR są przydatne do zapisywania danych użytkownika. Jeśli obsługujesz beforeunload
zdarzenie, możesz przesłać dane na serwer, gdy użytkownik zamyka stronę.
Gdyby zostało to zrobione przy użyciu opcji async, strona może zostać zamknięta przed zakończeniem żądania. Wykonanie tej czynności synchronicznie gwarantuje ukończenie lub niepowodzenie żądania w oczekiwany sposób.
Poniższe sugeruje - ale nie udało się dostarczyć - że wraz z pojawieniem się lepszej asynchronicznej obsługi żądań, naprawdę nie ma powodu, aby używać żądań synchronicznych, chyba że celowo blokuje użytkownikom możliwość robienia czegokolwiek do momentu zakończenia żądania - brzmi złośliwie: )
Chociaż może to zabrzmieć źle, mogą się zdarzyć sytuacje, w których ważne jest, aby żądanie (lub seria żądań) wystąpiło przed opuszczeniem strony przez użytkownika lub przed wykonaniem czynności - blokowanie wykonania innego kodu (np. Blokowanie przycisku Wstecz) ewentualnie zmniejszyć liczbę błędów / konserwacji źle zaprojektowanego systemu; To powiedziawszy, nigdy nie widziałem tego na wolności i podkreślam, że należy tego unikać.
Biblioteki, podobnie jak obietnice , udają synchroniczność, łącząc procesy za pomocą wywołań zwrotnych. Odpowiada to większości potrzeb programistycznych, w których pożądane jest uporządkowanie zdarzeń nieblokujących, które umożliwiają przeglądarkom zachowanie responsywności dla użytkownika (dobry UX).
Jak stwierdzono w dokumentacji Mozilli, są przypadki, w których musisz używać żądań synchronicznych; jednak wymieniono również obejście, które w takich przypadkach używa sygnału nawigacyjnego (niedostępnego w przeglądarce IE / Safari). Chociaż jest to eksperymentalne, jeśli kiedykolwiek osiągnie akceptację standardów, może wbić gwóźdź do trumny z synchronicznymi żądaniami.
Chcesz wykonywać połączenia synchroniczne w dowolnym przetwarzaniu podobnym do transakcji lub wszędzie tam, gdzie wymagana jest dowolna kolejność operacji.
Na przykład, powiedzmy, że chcesz dostosować zdarzenie, aby wylogowało Cię po odtworzeniu piosenki. Jeśli operacja wylogowania nastąpi jako pierwsza, utwór nigdy nie zostanie odtworzony. Wymaga to synchronizacji żądań.
Innym powodem może być praca z usługą sieciową, zwłaszcza podczas wykonywania operacji matematycznych na serwerze.
Przykład: serwer ma zmienną o wartości 1.
Step (1) Perform Update: add 1 to variable Step (2) Perform Update: set variable to the power of 3 End Value: variable equals 8
Jeśli Krok (2) występuje jako pierwszy, to wartość końcowa wynosi 2, a nie 8; dlatego kolejność operacji ma znaczenie i konieczna jest synchronizacja.
Bardzo rzadko zdarza się, że połączenie synchroniczne może być uzasadnione we wspólnym przykładzie z rzeczywistego świata. Być może po kliknięciu zaloguj się, a następnie kliknięciu części witryny, która wymaga zalogowania użytkownika.
Jak powiedzieli inni, spowoduje to związanie przeglądarki, więc trzymaj się z dala od niej, gdzie możesz.
Jednak zamiast wywołań synchronicznych użytkownicy często chcą zatrzymać aktualnie ładowane zdarzenie, a następnie wykonać inną operację. W pewnym sensie jest to synchronizacja, ponieważ pierwsze zdarzenie kończy się przed rozpoczęciem drugiego. Aby to zrobić, użyj metody abort () na obiekcie połączenia xml.
Powiedziałbym, że jeśli rozważasz zablokowanie przeglądarki użytkownika, gdy żądanie zostanie zakończone, użyj żądania synchronicznego.
Jeśli celem jest serializacja żądań, można to osiągnąć za pomocą żądań asynchronicznych, uruchamiając wywołanie zwrotne onComplete poprzedniego żądania w następnej kolejności.
Istnieje wiele rzeczywistych przypadków, w których blokowanie interfejsu użytkownika jest dokładnie pożądanym zachowaniem.
Weź aplikację z wieloma polami, a niektóre pola muszą zostać zweryfikowane przez wywołanie xmlhttp do zdalnego serwera, podając jako dane wejściowe wartość tego pola i inne wartości pól.
W trybie synchronicznym logika jest prosta, blokowanie doświadczane przez użytkownika jest bardzo krótkie i nie ma problemu.
W trybie asynchronicznym użytkownik może zmieniać wartości innych pól podczas walidacji pierwszego. Te zmiany wywołają inne wywołania xmlhttp z wartościami z pola początkowego, które nie zostały jeszcze zweryfikowane. Co się stanie, jeśli wstępna weryfikacja się nie powiedzie? Czysty bałagan. Jeśli tryb synchronizacji stanie się przestarzały i zabroniony, logika aplikacji stanie się koszmarem. Zasadniczo aplikacja musi zostać ponownie napisana, aby zarządzać blokadami (np. Wyłączyć inne elementy podczas procesów walidacji). Złożoność kodu ogromnie wzrasta. Niezastosowanie się do tego może doprowadzić do awarii logiki, a ostatecznie do uszkodzenia danych.
Zasadniczo pytanie brzmi: co jest ważniejsze, niezablokowane doświadczenie interfejsu użytkownika lub ryzyko uszkodzenia danych? Odpowiedź powinna pozostać u programisty aplikacji, a nie do W3C.
Widzę zastosowanie synchronicznych żądań XHR, które mają być używane, gdy zasób w zmiennej lokalizacji musi zostać załadowany przed innymi statycznymi zasobami na stronie, które zależą od pierwszego zasobu, aby w pełni funkcjonować. W rzeczywistości implementuję takie żądanie XHR w małym własnym podprojekcie, podczas gdy zasoby JavaScript znajdują się w różnych lokalizacjach na serwerze w zależności od zestawu określonych parametrów. Kolejne zasoby JavaScript opierają się na tych zasobach zmiennych i takie pliki MUSZĄ zostać załadowane przed załadowaniem innych plików zależnych, dzięki czemu aplikacja jest kompletna.
Ta podstawa pomysłów naprawdę rozszerza się na odpowiedź vol7ron. Procedury oparte na transakcjach to naprawdę jedyny czas, w którym należy wysyłać synchroniczne żądania. W większości innych przypadków wywołania asynchroniczne są lepszą alternatywą, w której po wywołaniu DOM jest aktualizowany w razie potrzeby. W wielu przypadkach, takich jak systemy oparte na użytkownikach, możesz zablokować pewne funkcje dla „nieautoryzowanych użytkowników”, dopóki sami się nie zalogują. Te funkcje, po wywołaniu asynchronicznym, są odblokowywane poprzez procedurę aktualizacji DOM.
Muszę w końcu powiedzieć, że zgadzam się z punktami większości osób w tej sprawie: wszędzie tam, gdzie to możliwe, należy unikać synchronicznych żądań XHR, ponieważ w sposobie działania przeglądarka blokuje się z połączeniami synchronicznymi. Podczas implementacji żądań synchronicznych należy je wykonywać w taki sposób, aby przeglądarka była normalnie zablokowana, na przykład w sekcji NAGŁÓWEK, zanim faktycznie nastąpi załadowanie strony.
Od 2015 roku aplikacje javascript na komputery stacjonarne stają się coraz bardziej popularne. Zwykle w tych aplikacjach podczas ładowania plików lokalnych (i ładowanie ich za pomocą XHR jest całkowicie poprawną opcją), prędkość ładowania jest tak duża, że nie ma sensu nadmiernie komplikować kodu z asynchronizacją. Oczywiście mogą istnieć przypadki, w których asynchronizacja jest drogą do zrobienia (żądanie zawartości z Internetu, ładowanie naprawdę dużych plików lub ogromnej liczby plików w jednej partii), ale poza tym synchronizacja działa dobrze (i jest znacznie łatwiejsza w użyciu) .
W pewnych okolicznościach jQuery używa wewnętrznie synchronicznego AJAX. Podczas wstawiania kodu HTML zawierającego skrypty przeglądarka ich nie wykona. Skrypty należy uruchamiać ręcznie. Te skrypty mogą dołączać moduły obsługi kliknięć. Załóżmy, że użytkownik klika element przed dołączeniem modułu obsługi, a strona nie będzie działać zgodnie z oczekiwaniami. Dlatego, aby zapobiec sytuacjom wyścigu, do pobierania tych skryptów zostanie użyty synchroniczny AJAX. Ponieważ synchroniczny AJAX skutecznie blokuje wszystko inne, można mieć pewność, że skrypty i zdarzenia będą wykonywane we właściwej kolejności.
Powód:
Powiedzmy, że masz aplikację Ajax, która musi wykonać pół tuzina HTTP, aby załadować różne dane z serwera, zanim użytkownik będzie mógł wykonać jakąkolwiek interakcję.
Oczywiście chcesz, aby było to uruchamiane podczas ładowania.
Wywołania synchroniczne działają bardzo dobrze w tym przypadku bez dodatkowej złożoności kodu. To jest proste i nieskomplikowane.
Wada:
Jedyną wadą jest to, że przeglądarka blokuje się do momentu załadowania wszystkich danych lub przekroczenia limitu czasu. Jeśli chodzi o omawianą aplikację Ajax, nie stanowi to większego problemu, ponieważ aplikacja jest bezużyteczna, dopóki wszystkie początkowe dane nie zostaną załadowane.
Alternatywny?
Jednak wiele przeglądarek blokuje wszystkie okna / karty, gdy javascript jest zajęty w którymkolwiek z nich, co jest głupim problemem projektowym przeglądarki - ale w rezultacie blokowanie w możliwie wolnej sieci nie jest grzeczne, jeśli uniemożliwia użytkownikom korzystanie z innych kart podczas oczekiwania na załadowanie strony Ajax.
Wygląda jednak na to, że pobieranie synchroniczne zostało i tak usunięte lub ograniczone w najnowszych przeglądarkach. Nie jestem pewien, czy to dlatego, że ktoś zdecydował, że zawsze byli źli, czy też pisarze przeglądarek byli zdezorientowani przez projekt roboczy WC na ten temat.
http://www.w3.org/TR/2012/WD-XMLHttpRequest-20120117/#the-open-method wygląda na to, że (patrz sekcja 4.7.3) nie możesz ustawić limitu czasu podczas korzystania z trybu blokowania . Wydaje mi się to sprzeczne z intuicją: za każdym razem, gdy blokuje się IO, uprzejmie jest ustawić rozsądny limit czasu, więc po co zezwalać na blokowanie IO, ale nie z limitem czasu określonym przez użytkownika?
Uważam, że blokowanie IO odgrywa kluczową rolę w niektórych sytuacjach, ale musi być poprawnie wdrożone. Chociaż niedopuszczalne jest, aby jedna karta lub okno przeglądarki blokowała wszystkie inne karty lub okna, jest to wada projektowa przeglądarki. Wstyd tam, gdzie należy się wstyd. Ale w niektórych przypadkach jest całkowicie dopuszczalne, aby pojedyncza karta lub okno nie reagowało przez kilka sekund (tj. Przy użyciu blokowania IO / HTTP GET) w niektórych sytuacjach - na przykład podczas ładowania strony, być może dużo danych musi być zanim cokolwiek będzie można zrobić. Czasami prawidłowo zaimplementowany kod blokujący to najczystszy sposób.
Oczywiście równoważną funkcję w tym przypadku można uzyskać za pomocą asynchronicznego pobierania http, ale jaki rodzaj głupiej procedury jest wymagany?
Myślę, że spróbuję czegoś w ten sposób:
Podczas ładowania dokumentu wykonaj następujące czynności: 1: Ustaw 6 globalnych zmiennych flag „Gotowe”, zainicjuj je na 0. 2: Wykonaj wszystkie 6 pobrań w tle (zakładając, że kolejność nie ma znaczenia)
Następnie wywołania zwrotne zakończenia dla każdego z 6 http get ustawiłyby odpowiednie flagi „Gotowe”. Ponadto każde wywołanie zwrotne sprawdzałoby wszystkie inne flagi gotowe, aby zobaczyć, czy wszystkie 6 HTTP zostało zakończone. Ostatnie wywołanie zwrotne do zakończenia, po zobaczeniu, że wszystkie inne zakończyły się, wywoływałby funkcję INIT REAL, która następnie ustawiałaby wszystko, teraz, gdy wszystkie dane zostały pobrane.
Jeśli kolejność pobierania miała znaczenie - lub jeśli serwer sieciowy nie był w stanie zaakceptować wielu żądań w tym samym czasie - potrzebujesz czegoś takiego:
W onload () zostanie uruchomione pierwsze pobieranie http. W jego wywołaniu zwrotnym zostanie uruchomiony drugi. W swoim wywołaniu zwrotnym, trzeci - i tak dalej, i tak dalej, przy czym każde wywołanie zwrotne uruchamia następny HTTP GET. Kiedy ostatni powrócił, wywołałby prawdziwą procedurę init ().
Co się stanie, jeśli wykonasz wywołanie synchroniczne w kodzie produkcyjnym?
Niebo spada.
Nie, poważnie, użytkownik nie lubi zablokowanej przeglądarki.
Używam go do walidacji nazwy użytkownika, podczas sprawdzania, czy nazwa użytkownika już nie istnieje.
Wiem, że lepiej byłoby zrobić to asynchronicznie, ale wtedy powinienem użyć innego kodu dla tej konkretnej reguły walidacji. Lepiej wyjaśniam. Moja konfiguracja walidacji używa pewnych funkcji walidacji, które zwracają wartość true lub false, w zależności od tego, czy dane są prawidłowe.
Ponieważ funkcja musi powrócić, nie mogę używać technik asynchronicznych, więc po prostu ustawiam tę synchronizację i mam nadzieję, że serwer odpowie na tyle szybko, aby nie było zbyt zauważalne. Gdybym użył wywołania zwrotnego AJAX, musiałbym obsłużyć resztę wykonania inaczej niż inne metody walidacji.
false
) i po prostu ustawić ją na wartość true lub false, gdy serwer odpowie. po zmianie pola resetujesz flagę i ponownie dzwonisz do serwera.
Czasami masz działanie, które zależy od innych. Na przykład akcja B może zostać uruchomiona tylko wtedy, gdy A jest zakończona. Podejście synchroniczne jest zwykle używane w celu uniknięcia warunków wyścigu . Czasami użycie wywołania synchronicznego jest prostszą implementacją niż utworzenie złożonej logiki do sprawdzania każdego stanu wywołań asynchronicznych, które są od siebie zależne.
Problem z tym podejściem polega na tym, że „blokujesz” przeglądarkę użytkownika do czasu zakończenia akcji (do czasu powrotu żądania, zakończenia, załadowania itp.). Dlatego uważaj, używając go.
Używam wywołań synchronicznych podczas tworzenia kodu - cokolwiek zrobiłeś, gdy żądanie było przesyłane do iz serwera, może zaciemnić przyczynę błędu.
Kiedy to działa, robię to asynchronicznie, ale staram się dołączyć licznik przerwania i wywołania zwrotne awarii, ponieważ nigdy nie wiadomo ...
SYNC vs ASYNC: Jaka jest różnica?
Zasadniczo sprowadza się to do tego:
console.info('Hello, World!');
doSomething(function handleResult(result) {
console.info('Got result!');
});
console.info('Goodbye cruel world!');
Kiedy doSomething
jest synchroniczne , wypisze:
Hello, World!
Got result!
Goodbye cruel world!
W przeciwieństwie do tego, jeśli doSomething
jest asynchroniczny , wypisze:
Hello, World!
Goodbye cruel world!
Got result!
Ponieważ funkcja doSomething
wykonuje swoją pracę asynchronicznie, zwraca przed wykonaniem pracy. Wynik uzyskujemy więc dopiero po wydrukowaniuGoodbye cruel world!
Jeśli jesteśmy zależni od wyniku wywołania asynchronicznego, musimy umieścić zależny kod w wywołaniu zwrotnym:
console.info('Hello, World!');
doSomething(function handleResult(result) {
console.info('Got result!');
if (result === 'good') {
console.info('I feel great!');
}
else {
console.info('Goodbye cruel world!');
}
});
W związku z tym sam fakt, że dwie lub trzy rzeczy muszą się wydarzyć po kolei, nie jest powodem, aby robić je synchronicznie (chociaż kod synchronizacji jest łatwiejszy do pracy dla większości ludzi).
DLACZEGO SYNCHRONICZNY XMLHTTPREQUEST?
Są sytuacje, w których potrzebujesz wyniku przed zakończeniem wywoływanej funkcji. Rozważ ten scenariusz:
function lives(name) {
return (name !== 'Elvis');
}
console.info('Elvis ' + (lives('Elvis') ? 'lives!' : 'has left the building...');
Załóżmy, że nie mamy kontroli nad kodem wywołującym ( console.info
linią) i musimy zmienić funkcję, lives
aby zapytać serwer ... Nie ma sposobu, abyśmy mogli wykonać żądanie asynchroniczne do serwera od wewnątrz lives
i nadal mieć odpowiedź przed lives
zakończeniem. Więc nie wiedzielibyśmy, czy wrócić, true
czyfalse
. Jedynym sposobem uzyskania wyniku przed zakończeniem funkcji jest wykonanie żądania synchronicznego.
Jak Sami Samhuri
wspomina w swojej odpowiedzi, bardzo realnym scenariuszem, w którym możesz potrzebować odpowiedzi na żądanie serwera przed zakończeniem funkcji, jest onbeforeunload
zdarzenie, ponieważ jest to ostatnia funkcja z Twojej aplikacji, która kiedykolwiek zostanie uruchomiona przed zamknięciem okna.
NIE POTRZEBUJĘ SYNCHRONIZACJI POŁĄCZEŃ, ALE KORZYSTAM Z NICH JAKIEŚ SĄ ŁATWIEJSZE
Proszę nie. Synchroniczne połączenia blokują przeglądarkę i sprawiają, że aplikacja nie reaguje. Ale masz rację. Kod asynchroniczny jest trudniejszy. Istnieje jednak sposób, aby sobie z tym poradzić. Nie tak proste jak synchronizacja kodu, ale zbliża się: Obietnica s.
Oto przykład: dwa wywołania asynchroniczne powinny zakończyć się pomyślnie, zanim trzeci segment kodu zostanie uruchomiony:
var carRented = rentCar().then(function(car){
gasStation.refuel(car);
});
var hotelBooked = bookHotel().then(function(reservation) {
reservation.confirm();
});
Promise.all([carRented, hotelBooked]).then(function(){
// At this point our car is rented and our hotel booked.
goOnHoliday();
});
Oto jak byś wdrożył bookHotel
:
function bookHotel() {
return new Promise(function(resolve, reject){
if (roomsAvailable()) {
var reservation = reserveRoom();
resolve(reservation);
}
else {
reject(new Error('Could not book a reservation. No rooms available.'));
}
});
}
Zobacz także: Pisz lepiej JavaScript z obietnicami .
XMLHttpRequest
jest tradycyjnie używany do żądań asynchronicznych. Czasami (w przypadku debugowania lub określonej logiki biznesowej) chcesz zmienić wszystkie / kilka wywołań asynchronicznych na jednej stronie w celu synchronizacji.
Chciałbyś to zrobić bez zmiany wszystkiego w kodzie JS. Flaga async / sync daje taką możliwość, a jeśli została zaprojektowana poprawnie, wystarczy zmienić tylko jedną linię w kodzie / zmienić wartość jednej w var
czasie wykonywania.
Firefox (i prawdopodobnie wszystkie przeglądarki inne niż IE) nie obsługuje asynchronicznego XHR timeOut.
WebWorkers HTML5 obsługują limity czasu. Tak więc możesz chcieć zawinąć żądanie synchronizacji XHR do WebWorker z limitem czasu, aby zaimplementować asynchroniczne XHR z zachowaniem limitu czasu.
Korzystanie z synchronicznych żądań HTTP jest powszechną praktyką w branży reklam mobilnych.
Firmy (zwane też „wydawcami”), które tworzą aplikacje, często wyświetlają reklamy w celu generowania przychodów. W tym celu instalują w swojej aplikacji pakiety SDK reklam. Istnieje wiele (MoPub, Ogury, TapJob, AppNext, Google Ads AdMob).
Te pakiety SDK będą wyświetlać reklamy w widoku internetowym.
Wyświetlając reklamę użytkownikowi, musi to być płynne doświadczenie, zwłaszcza podczas odtwarzania wideo. W żadnym momencie nie powinno być buforowania ani ładowania.
Do rozwiązania tego precaching
służy. Gdzie media (zdjęcia / filmy / itp.) Są ładowane synchronicznie w tle widoku internetowego.
Dlaczego nie zrobić tego asynchronicznie?
onload
zdarzenia, aby wiedzieć, kiedy reklama jest „gotowa” do wyświetlenia użytkownikowiWraz z wycofaniem synchronicznych żądań XMLHttpRequests firmy reklamowe będą najprawdopodobniej zmuszone do zmiany standardu w przyszłości, chyba że można określić inny sposób.
Synchroniczny XHR może być bardzo przydatny do (nieprodukcyjnego) wewnętrznego tworzenia narzędzi i / lub frameworka. Wyobraź sobie na przykład, że chcesz synchronicznie załadować bibliotekę kodu przy pierwszym dostępie, na przykład:
get draw()
{
if (!_draw)
{
let file;
switch(config.option)
{
case 'svg':
file = 'svgdraw.js';
break;
case 'canvas':
file = 'canvasdraw.js';
break;
default:
file = 'webgldraw.js';
}
var request = new XMLHttpRequest();
request.open('GET', file, false);
request.send(null);
_draw = eval(request.responseText);
}
return _draw;
}
Zanim wpadniesz w oszołomienie i ślepo zwymiotujesz zło eval, pamiętaj, że jest to tylko do lokalnych testów. W przypadku kompilacji produkcyjnych _draw byłby już ustawiony.
Twój kod może więc wyglądać następująco:
foo.drawLib.draw.something(); //loaded on demand
To tylko jeden przykład czegoś, co nie byłoby możliwe bez synchronizacji XHR. Możesz załadować tę bibliotekę z góry, tak, lub wykonać obietnicę / wywołanie zwrotne, ale nie możesz załadować biblioteki synchronicznie bez synchronizacji XHR. Pomyśl, jak bardzo tego typu rzeczy mogą oczyścić Twój kod ...
Ograniczenia tego, co możesz z tym zrobić dla narzędzi i struktur (działających lokalnie), są ograniczone tylko przez twoją wyobraźnię. Chociaż wydaje się, że w świecie JavaScript wyobraźnia jest nieco ograniczona.
Oto jeden dobry powód. Chciałem następnie wykonać żądanie http, w zależności od wyniku, wywołać click () na wejściu type = file. Nie jest to możliwe w przypadku asynchronicznego xhr lub pobierania. Wywołanie zwrotne traci kontekst „akcja użytkownika”, więc wywołanie click () jest ignorowane. Synchroniczna xhr uratowała mój boczek.
onclick(event){
//here I can, but I don't want to.
//document.getElementById("myFileInput").click();
fetch("Validate.aspx", { method : "POST", body: formData, credentials: "include" })
.then((response)=>response.json())
.then(function (validResult) {
if (validResult.success) {
//here, I can't.
document.getElementById("myFileInput").click();
}
});
}