Eric Elliott

Postępuj zgodnie z

23 stycznia 2017 · 11 min czytać

Zdjęcia Kabun (CC BY NC SA 2.,0)

„Master the JavaScript Interview” jest serią postów mających na celu przygotowanie kandydatów do typowych pytań, które mogą napotkać podczas ubiegania się o średniej i wyższej pozycji JavaScript. Są to pytania, których często używam w prawdziwych wywiadach.

obietnica jest obiektem, który może w przyszłości wygenerować pojedynczą wartość: albo wartość rozwiązana, albo powód, dla którego nie została rozwiązana (np. wystąpił błąd sieciowy)., Obietnica może być w jednym z 3 możliwych stanów: spełniona, odrzucona lub oczekująca. Użytkownicy Promise mogą dołączać wywołania zwrotne, aby obsłużyć spełnioną wartość lub powód odrzucenia.

obietnice są chętne, co oznacza, że obietnica zacznie wykonywać każde zadanie, które jej podasz, gdy tylko konstruktor obietnicy zostanie wywołany. Jeśli potrzebujesz leniwego, sprawdź obserwatory lub zadania.

niekompletna Historia obietnic

wczesne implementacje obietnic i przyszłości (podobna / pokrewna idea) zaczęły pojawiać się w językach takich jak MultiLisp i współbieżny Prolog już w latach 80., Słowo „obietnica” zostało wymyślone przez Barbarę Liskow i Liubę Shrirę w 1988 roku.

gdy pierwszy raz usłyszałem o obietnicach w JavaScript, Node był zupełnie nowy, a społeczność dyskutowała o najlepszym sposobie obsługi asynchronicznych zachowań. Społeczność przez jakiś czas eksperymentowała z obietnicami, ale ostatecznie ustaliła błąd standardowy węzła-pierwsze wywołania zwrotne.

mniej więcej w tym samym czasie Dojo dodało obietnice poprzez odroczone API. Rosnące zainteresowanie i aktywność w końcu doprowadziły do nowo utworzonych obietnic / specyfikacji zaprojektowanej w celu uczynienia różnych obietnic bardziej interoperacyjnymi.,

asynchroniczne zachowania jQuery były refakturowane wokół obietnic. obsługa jQuery promise miała niezwykłe podobieństwa do Deferred Dojo i szybko stała się najczęściej używaną implementacją promise w JavaScript ze względu na ogromną popularność jQuery — przez pewien czas. Nie obsługiwał on jednak dwukanałowego (spełnionego/odrzuconego) zachowania łańcucha & zarządzania wyjątkami, na które ludzie liczyli, aby budować narzędzia na obietnicach.,

pomimo tych słabości, jQuery oficjalnie złożył obietnice JavaScript mainstream, a lepsze samodzielne biblioteki obietnic, takie jak Q, When I Bluebird, stały się bardzo popularne. niezgodność implementacji jQuery motywowała kilka ważnych wyjaśnień w specyfikacji promise, która została przepisana i przemianowana na specyfikację Promises/a+.,

ES6 przyniósł obietnice/a+ zgodnePromise global, a niektóre bardzo ważne API zostały zbudowane na szczycie nowego standardu wsparcia obietnicy: zwłaszcza WHATWG Fetch spec i async Functions standard (szkic etapu 3 w momencie pisania tego tekstu).

obietnice opisane tutaj są te, które są zgodne ze specyfikacją Promises/A+, z naciskiem na standard ECMAScriptPromise.

jak działają obietnice

obietnica jest obiektem, który może być zwracany synchronicznie z funkcji asynchronicznej., Będzie w jednym z 3 możliwych stanów:

obietnica jest rozliczana, jeśli nie jest oczekująca (została rozwiązana lub odrzucona). Czasami ludzie używają rozwiązania i ugody, aby oznaczać to samo: nie oczekujące.

po rozliczeniu obietnicy nie można Wywołanie resolve() lubreject() ponownie nie będzie miało wpływu. Niezmienność ustalonej obietnicy jest ważną cechą.

rodzime obietnice JavaScript nie ujawniają Stanów obietnic. Zamiast tego masz traktować obietnicę jak czarną skrzynkę., Tylko funkcja odpowiedzialna za stworzenie obietnicy będzie miała wiedzę o statusie obietnicy lub dostęp do rozwiązania lub odrzucenia.

oto funkcja, która zwraca obietnicę, która zostanie rozwiązana po określonym opóźnieniu czasowym:

wait — promise przykład na CodePen

Nasz wait(3000) wywołanie będzie czekać 3000ms (3 sekundy), a następnie zalogować 'Hello!'., Wszystkie obietnice zgodne ze specyfikacją definiują.then() metodę, której używasz do przekazywania programów obsługi, które mogą przyjmować wartość rozwiązaną lub odrzuconą.

konstruktor ES6 promise przyjmuje funkcję. Funkcja ta przyjmuje dwa parametry, resolve() I reject(). W powyższym przykładzie używamy tylko resolve() , więc zostawiłem reject() poza listą parametrów. Następnie wywołujemy setTimeout(), aby utworzyć opóźnienie i wywołujemy resolve() po zakończeniu.,

możesz opcjonalnieresolve() lubreject()z wartościami, które zostaną przekazane do funkcji wywołania zwrotnego dołączonych do.then().

Kiedy reject() z wartością, zawsze mijam Error obiekt. Ogólnie rzecz biorąc, chcę dwóch możliwych stanów rozdzielczości: normalnej szczęśliwej ścieżki lub wyjątku-wszystkiego, co powstrzyma normalną szczęśliwą ścieżkę przed wydarzeniem. Podanie obiektu Error powoduje, że jest to jawne.,

ważne zasady obietnic

standard obietnic został zdefiniowany przez społeczność specyfikacji Promises / A+. Istnieje wiele implementacji zgodnych ze standardem, w tym JavaScript standard ECMAScript promises.

obietnice zgodne ze specyfikacją muszą być zgodne z określonym zestawem reguł:

  • obietnica lub „thenable” jest obiektem, który dostarcza zgodną ze standardami metodę.then().
  • oczekująca obietnica może przejść do stanu spełnionego lub odrzuconego.,
  • spełniona lub odrzucona obietnica jest rozliczana i nie może przejść do żadnego innego stanu.
  • gdy obietnica zostanie rozliczona, musi mieć wartość (która może być undefined). Wartość ta nie może się zmienić.

zmiana w tym kontekście odnosi się do porównania tożsamości (===). Obiekt może być użyty jako spełniona wartość, a właściwości obiektu mogą ulegać mutacji.,

każda obietnica musi dostarczyć .then() metodę z następującym podpisem:

promise.then(
onFulfilled?: Function,
onRejected?: Function
) => Promise

.then() metoda musi być zgodna z tymi zasadami:

  • obie onFulfilled() I onRejected() są opcjonalne.
  • Jeśli podane argumenty nie są funkcjami, muszą być ignorowane.
  • onFulfilled() zostanie wywołane po spełnieniu obietnicy, z wartością obietnicy jako pierwszym argumentem.,
  • onRejected() zostanie wywołane po odrzuceniu obietnicy, z powodem odrzucenia jako pierwszym argumentem. Powodem może być każda prawidłowa wartość JavaScript, ale ponieważ odrzucenia są zasadniczo synonimem WYJĄTKÓW, zalecam używanie obiektów błędu.
  • anionFulfilled() anionRejected() nie można wywoływać więcej niż jeden raz.
  • .then() można wywołać wiele razy na tej samej obietnicy. Innymi słowy, obietnica może być używana do agregowania wywołań zwrotnych.,
  • .then() musi zwrócić nową obietnicę,promise2.
  • Jeśli onFulfilled() lub onRejected() Zwraca wartość x I x jest obietnicą, promise2 zablokuje się z (przyjąć ten sam stan i wartość co) x. W przeciwnym raziepromise2 zostanie spełniona wartośćx.,
  • Jeśli onFulfilledlub onRejectedrzuca wyjątek e, promise2musi zostać odrzucony za pomocą ejako powód.
  • JeślionFulfilled nie jest funkcją ipromise1 jest spełnione,promise2 musi być spełnione z tą samą wartością copromise1.,
  • JeślionRejected nie jest funkcją ipromise1 jest odrzucona,promise2 musi zostać odrzucona z tego samego powodu copromise1.

Łańcuchowanie obietnic

ponieważ.then() zawsze zwraca nową obietnicę, możliwe jest łańcuchowanie obietnic z precyzyjną kontrolą sposobu i miejsca obsługi błędów. Obietnice pozwalają naśladować normalne zachowanie kodu synchronicznego try/catch.,

podobnie jak kod synchroniczny, łańcuchowanie spowoduje powstanie sekwencji, która będzie uruchamiana szeregowo., Innymi słowy, możesz wykonać:

fetch(url)
.then(process)
.then(save)
.catch(handleErrors)
;

oto przykład złożonego łańcucha obietnic z wieloma odrzuceniami:

przykład zachowania łańcucha obietnic na CodePen

obsługa błędów

zauważ, że obietnice mają zarówno sukces, jak i obsługę błędów, i bardzo często widzisz kod, który to robi:

save().then(
handleSuccess,
handleError
);

ale co się stanie, jeśli handleSuccess() wyrzuci błąd?, Obietnica zwrócona z .then() zostanie odrzucona, ale nie ma tam nic, aby złapać odrzucenie-co oznacza, że błąd w aplikacji zostanie połknięty. UPS!

z tego powodu niektórzy ludzie uważają powyższy kod za anty-wzorzec i zalecają zamiast tego:

save()
.then(handleSuccess)
.catch(handleError)
;

różnica jest subtelna, ale ważna. W pierwszym przykładzie zostanie przechwycony błąd pochodzący z operacji save(), ale błąd pochodzący z funkcji handleSuccess() zostanie połknięty.,

Without .catch(), an error in the success handler is uncaught.

In the second example, .catch() will handle rejections from either save(), or handleSuccess().

With .catch(), both error sources are handled., (źródło diagramu)

oczywiście błądsave()może być błędem sieciowym, podczas gdy błądhandleSuccess() może być spowodowany tym, że programista zapomniał obsłużyć określony kod statusu. A jeśli chcesz inaczej sobie z nimi radzić? Możesz zdecydować się na obsługę obu:

save()
.then(
handleSuccess,
handleNetworkError
)
.catch(handleProgrammerError)
;

cokolwiek wolisz, polecam zakończenie wszystkich łańcuchów obietnic za pomocą .catch(). Warto to powtórzyć:

polecam zakończenie wszystkich łańcuchów obietnic .catch().,

jak anulować obietnicę?

jedną z pierwszych rzeczy, które użytkownicy new promise często zastanawiają się nad tym, jak anulować obietnicę. Oto pomysł: po prostu Odrzuć obietnicę z „anulowanym” jako powodem. Jeśli musisz poradzić sobie z tym inaczej niż „normalny” błąd, wykonaj rozgałęzianie w programie obsługi błędów.

oto kilka typowych błędów, które ludzie popełniają, gdy rzucają własną obietnicę:

dodawanie .,cancel () do obietnicy

dodanie.cancel() sprawia, że obietnica jest niestandardowa, ale narusza również inną zasadę obietnic: tylko funkcja, która tworzy obietnicę, powinna być w stanie rozwiązać, odrzucić lub anulować obietnicę. Ujawnienie go łamie tę enkapsulację i zachęca ludzi do pisania kodu, który manipuluje obietnicą w miejscach, które nie powinny o tym wiedzieć. Unikaj spaghetti i złamanych obietnic.

zapominając o sprzątaniu

niektórzy sprytni ludzie zorientowali się, że istnieje sposób na użyciePromise.race() jako mechanizmu anulowania., Problem z tym polega na tym, że kontrola anulowania jest pobierana z funkcji, która tworzy obietnicę, która jest jedynym miejscem, w którym można przeprowadzić odpowiednie czynności porządkowe, takie jak wyczyszczenie timeoutów lub zwolnienie pamięci przez wyczyszczenie odniesień do danych itp…

zapominając obsłużyć odrzuconą obietnicę anulowania

Czy wiesz, że Chrome wyrzuca komunikaty ostrzegawcze na całą konsolę, gdy zapomnisz obsłużyć odrzucenie obietnicy? UPS!

wycofana propozycja anulowania tc39 zaproponowała osobny kanał komunikacyjny do anulowania., Zastosowano również nową koncepcję zwaną tokenem anulowania. Moim zdaniem rozwiązanie znacznie nadciążyło specyfikację obietnicy, a jedyną cechą, która zapewniłaby, że spekulacje nie wspierają bezpośrednio, jest rozdzielenie odrzuceń i odwołań, co, IMO, nie jest konieczne na początek.

czy chcesz zrobić przełączanie w zależności od tego, czy jest wyjątek, czy anulowanie? Tak, oczywiście. To zadanie obietnicy? Moim zdaniem nie, nie jest.,

ponowne rozważenie anulowania obietnicy

ogólnie przekazuję wszystkie informacje potrzebne do określenia sposobu rozwiązania / odrzucenia / anulowania w czasie tworzenia obietnicy. W ten sposób nie ma potrzeby stosowania metody .cancel() na obietnicy. Być może zastanawiasz się, skąd możesz wiedzieć, czy zamierzasz anulować w czasie tworzenia obietnicy.

” Jeśli jeszcze nie wiem, czy anulować, Jak będę wiedział, co przekazać, gdy stworzę obietnicę?,”

gdyby tylko istniał jakiś obiekt, który mógłby stać się potencjalną wartością w przyszłości…

wartość, którą przekazujemy, aby określić, czy anulować, czy nie, może być samą obietnicą. Oto jak to może wyglądać:

Cancellable wait — try it on CodePen

używamy domyślnego przypisania parametrów, aby powiedzieć, że domyślnie nie anuluje. To sprawia, że parametr cancel jest dogodnie opcjonalny., Następnie ustawiamy timeout tak jak wcześniej, ale tym razem przechwytujemy identyfikator timeouta, abyśmy mogli go wyczyścić później.

używamy metody cancel.then() do obsługi anulowania i czyszczenia zasobów. Będzie to działać tylko wtedy, gdy obietnica zostanie anulowana, zanim będzie miała szansę rozwiązać. Jeśli anulujesz za późno, straciłeś swoją szansę. Ten pociąg odjechał ze stacji.

Uwaga: być może zastanawiasz się, do czego służy funkcjanoop(). Słowo noop oznacza no-op, co oznacza funkcję, która nic nie robi., Bez tego V8 wyrzuci Ostrzeżenia: UnhandledPromiseRejectionWarning: Unhandled promise rejection. Dobrym pomysłem jest zawsze obsługiwanie odrzuceń obietnic, nawet jeśli twój handler to noop().

Abstracting Promise Cancellation

To jest w porządku dlawait() timer, ale możemy abstrakcyjny ten pomysł dalej hermetyzować wszystko, co musisz pamiętać:

  1. Odrzuć obietnicę anulowania domyślnie — nie chcemy anulować lub rzucać błędów, jeśli żadna obietnica anulowania nie zostanie przekazana.
  2. pamiętaj, aby wykonać czyszczenie, gdy odrzucasz anulowanie.,
  3. pamiętaj, że funkcja czyszczenia onCancel może sama spowodować błąd i ten błąd będzie wymagał obsługi. (Zauważ, że obsługa błędów jest pominięta w powyższym przykładzie wait — łatwo o tym zapomnieć!)

stwórzmy anulowalne narzędzie promise, którego możesz użyć do zawinięcia dowolnej obietnicy., Na przykład, aby obsłużyć żądania sieciowe, itp… podpis będzie wyglądał następująco:

speculation(fn: SpecFunction, shouldCancel: Promise) => Promise

funkcja SpecFunction jest taka sama jak funkcja, którą można przekazać do konstruktora Promise, z jednym wyjątkiem — wymaga onCancel() handler:

SpecFunction(resolve: Function, reject: Function, onCancel: Function) => Void

zauważ, że ten przykład jest tylko ilustracją, aby dać ci streszczenie, jak to działa. Istnieją inne przypadki krawędzi, które należy wziąć pod uwagę., Na przykład w tej wersji, handleCancel zostanie wywołane, jeśli anulujesz obietnicę po jej uregulowaniu.

zaimplementowałem utrzymywaną wersję produkcyjną tej wersji z przypadkami edge ' a jako biblioteką open source.

użyjmy ulepszonej abstrakcji bibliotecznej, aby przepisać odwołalnewait() narzędzie sprzed., Najpierw zainstaluj spekulacje:

npm install --save speculation

teraz możesz zaimportować i używać:

To trochę upraszcza, ponieważ nie musisz się martwić o wnoop(), wychwytywanie błędów wonCancel(), funkcja lub inne przypadki krawędzi. Te szczegóły zostały usunięte przez speculation(). Sprawdź to i nie krępuj się używać go w prawdziwych projektach.,

Dodatki natywnej obietnicy JS

natywnyPromise obiekt ma kilka dodatkowych rzeczy, które mogą Cię zainteresować:

  • Promise.reject() zwraca odrzuconą obietnicę.
  • Promise.resolve() zwraca rozwiązaną obietnicę.
  • Promise.race() pobiera tablicę (lub dowolną iterowalną) i zwraca obietnicę, która rozwiązuje się z wartością pierwszej rozwiązanej obietnicy w iterowalnej, lub odrzuca z powodu pierwszej obietnicy, która odrzuca.,
  • Promise.all() pobiera tablicę (lub dowolną iterowalną) i zwraca obietnicę, która rozwiązuje się, gdy wszystkie obietnice w argumencie iterowalnym zostały rozwiązane, lub odrzuca z powodu pierwszej przekazanej obietnicy, która odrzuca.

podsumowanie

obietnice stały się integralną częścią kilku idiomów w JavaScript, w tym standardu WHATWG Fetch używanego dla większości nowoczesnych żądań ajax oraz standardu funkcji asynchronicznych używanego do synchronizacji kodu asynchronicznego.,

funkcje asynchroniczne są etapem 3 w momencie pisania tego tekstu, ale przewiduję, że wkrótce staną się bardzo popularnym, bardzo powszechnie stosowanym rozwiązaniem do programowania asynchronicznego w JavaScript — co oznacza, że nauka doceniania obietnic będzie jeszcze ważniejsza dla programistów JavaScript w najbliższej przyszłości.

na przykład, jeśli używasz Redux, proponuję sprawdzić redux-Saga: bibliotekę używaną do zarządzania efektami ubocznymi w Redux, która zależy od funkcji asynchronicznych w całej dokumentacji.,

Mam nadzieję, że nawet doświadczeni użytkownicy obietnic lepiej zrozumieją, czym są obietnice i jak działają, i jak lepiej z nich korzystać po przeczytaniu tego.

poznaj serię

  • co to jest zamknięcie?
  • Jaka jest różnica między dziedziczeniem klasy a dziedziczeniem prototypowym?
  • co to jest czysta funkcja?
  • czym jest skład funkcji?
  • czym jest Programowanie funkcyjne?
  • co to jest obietnica?
  • umiejętności miękkie

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *