Chcę używać (natywnych) obietnic w mojej aplikacji frontendowej do wykonywania żądania XHR, ale bez całej głupoty ogromnego frameworka.
Chcę wrócić do mojego XHR obietnicę, ale to nie działa (co daje mi: Uncaught TypeError: Promise resolver undefined is not a function
)
function makeXHRRequest (method, url, done) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function() { return new Promise().resolve(); };
xhr.onerror = function() { return new Promise().reject(); };
xhr.send();
}
makeXHRRequest('GET', 'http://example.com')
.then(function (datums) {
console.log(datums);
});
javascript
xmlhttprequest
promise
SomeKittens
źródło
źródło
Odpowiedzi:
Zakładam, że wiesz, jak złożyć natywną prośbę XHR (możesz odświeżyć tutaj i tutaj )
Ponieważ każda przeglądarka, która obsługuje natywne obietnice, będzie również obsługiwać
xhr.onload
, możemy pominąć wszelkieonReadyStateChange
wygłupy. Cofnijmy się i zacznijmy od podstawowej funkcji żądania XHR za pomocą wywołań zwrotnych:Hurra! Nie wiąże się to z niczym strasznie skomplikowanym (jak niestandardowe nagłówki lub dane POST), ale wystarczy, abyśmy posunęli się naprzód.
Konstruktor obietnicy
Możemy zbudować taką obietnicę:
Konstruktor obietnicy przyjmuje funkcję, która otrzyma dwa argumenty (nazwijmy je
resolve
ireject
). Można je traktować jako oddzwanianie, jeden dla sukcesu, a drugi dla niepowodzenia. Przykłady są niesamowite, zaktualizujmy zamakeRequest
pomocą tego konstruktora:Teraz możemy skorzystać z mocy obietnic, łącząc wiele połączeń XHR (i
.catch
spowoduje to błąd przy każdym połączeniu):Możemy to jeszcze poprawić, dodając zarówno parametry POST / PUT, jak i niestandardowe nagłówki. Użyjmy obiektu opcji zamiast wielu argumentów z podpisem:
makeRequest
teraz wygląda mniej więcej tak:Bardziej kompleksowe podejście można znaleźć w MDN .
Alternatywnie możesz użyć interfejsu API pobierania ( polyfill ).
źródło
responseType
, uwierzytelnianie, poświadczenia,timeout
… Aparams
obiekty powinny obsługiwać obiekty BLOB / bufory iFormData
instancjexhr.status
ixhr.statusText
po błędzie, ponieważ w tym przypadku są puste.resolve(xhr.response | xhr.responseText);
W większości przeglądarek repsonse jest w responseText w międzyczasie.Może to być tak proste, jak poniższy kod.
Należy pamiętać, że ten kod będzie
reject
wyzwalał wywołanie zwrotne tylko wtedy, gdyonerror
zostanie wywołany ( tylko błędy sieciowe ), a nie wtedy, gdy kod stanu HTTP oznacza błąd. Wyklucza to również wszystkie inne wyjątki. Zajmowanie się nimi powinno zależeć od ciebie, IMO.Ponadto zaleca się wywoływanie
reject
wywołania zwrotnego z wystąpieniemError
zdarzenia, a nie z samym zdarzeniem, ale dla uproszczenia pozostawiłem to, co jest.Przywoływanie może być następujące:
źródło
xhr.send(requestBody)
Dla każdego, kto szuka tego teraz, możesz użyć funkcji pobierania . Ma całkiem dobre wsparcie .
Najpierw użyłem odpowiedzi @ SomeKittens, ale potem odkryłem,
fetch
że robi to dla mnie po wyjęciu z pudełka :)źródło
fetch
funkcji, ale GitHub opublikował polifill .fetch
ponieważ nie obsługuje jeszcze anulowania.Myślę, że możemy uczynić najwyższą odpowiedź bardziej elastyczną i wielokrotnego użytku, nie tworząc
XMLHttpRequest
obiektu. Jedyną korzyścią z tego jest to, że nie musimy sami pisać 2 lub 3 wierszy kodu, a ma to ogromną wadę polegającą na tym, że zabieramy dostęp do wielu funkcji API, takich jak ustawianie nagłówków. Ukrywa także właściwości oryginalnego obiektu przed kodem, który ma obsłużyć odpowiedź (zarówno w przypadku sukcesów, jak i błędów). Możemy więc uczynić bardziej elastyczną, szerzej stosowaną funkcję, akceptującXMLHttpRequest
obiekt jako dane wejściowe i przekazując go jako wynik .Ta funkcja przekształca dowolny
XMLHttpRequest
obiekt w obietnicę, domyślnie traktując kody stanu inne niż 200 jako błąd:Ta funkcja bardzo naturalnie wpasowuje się w łańcuch
Promise
s, bez poświęcania elastycznościXMLHttpRequest
interfejsu API:catch
został pominięty powyżej, aby uprościć przykładowy kod. Zawsze powinieneś go mieć i oczywiście możemy:A wyłączenie obsługi kodu statusu HTTP nie wymaga dużych zmian w kodzie:
Nasz kod wywołujący jest dłuższy, ale koncepcyjnie nadal łatwo jest zrozumieć, co się dzieje. I nie musimy odbudowywać całego interfejsu API żądania internetowego, aby obsługiwać jego funkcje.
Możemy dodać kilka wygodnych funkcji, aby uporządkować nasz kod, a także:
Następnie nasz kod staje się:
źródło
Moim zdaniem odpowiedź jpmc26 jest prawie idealna. Ma jednak pewne wady:
POST
-requests na ustawienie treści żądania.send
funkcja jest ukryta w funkcji.Małpa łatająca obiekt xhr rozwiązuje następujące problemy:
Teraz użycie jest tak proste, jak:
Oczywiście wprowadza to inną wadę: łatanie małp szkodzi wydajności. Nie powinno to jednak stanowić problemu przy założeniu, że użytkownik czeka głównie na wynik xhr, że samo żądanie zajmuje rząd wielkości dłużej niż ustawienie połączenia i żądania xhr nie są często wysyłane.
PS: I oczywiście, jeśli celujesz w nowoczesne przeglądarki, użyj funkcji pobierania!
PPS: W komentarzach zaznaczono, że ta metoda zmienia standardowe API, co może być mylące. Dla większej przejrzystości można wstawić inną metodę na obiekt xhr
sendAndGetPromise()
.źródło
send
wywołanie, ale może też mylić czytelników, którzy wiedzą, żesend
nie ma wartości zwracanej. Użycie bardziej wyraźnych wywołań wyjaśnia, że została wywołana dodatkowa logika. Moja odpowiedź musi zostać dostosowana do obsługi argumentówsend
; jednak prawdopodobnie lepiej gofetch
teraz użyć .