Generatory – niezrozumiałe fragmenty

Specyfikacja wersji roboczej ECMAScript 6 przyniosła wiele radości dla współczesnego programisty JavaScriptu. W poprzednim poście pisaliśmy o nowych klasach kolekcji i pętlach iteracji for..of. W tym poście będziemy omawiać element, który idzie w parze z pętlami for..of: funkcje generatora.

W internecie jest już host świetnych materiałów, w których wyjaśniamy, dlaczego i jak używać generatorów. Mówiąc w skrócie, generatory to funkcje specjalne, które tworzą iteratory, a iteratory to obiekty z metodą next(), którą można wywołać w celu uzyskania wartości. W funkcji generatora słowo kluczowe yield podaje wartość parametru next(). Użycie funkcji yield zawiesza wykonanie funkcji generatora, zachowując stan do momentu ponownego wywołania next(). Następnie kod uruchamia się z powrotem do poprzedniego stanu i trwa do momentu zakończenia działania funkcji yield (lub do zakończenia działania funkcji generatora). Istnieje kilka kanonicznych przypadków użycia funkcji generatora, np. powtarzanie liczb w sekwencji Fibonacciego.

Przy podstawach przyjrzyjmy się bliżej przykładowi JavaScriptu, który uwzględnia niektóre z zagrożeń pracy z generatorami. W całej wiadomości znajduje się wiele komentarzy, a przed przeczytaniem kodu możesz wypróbować wersję opublikowanej:

Jakie wnioski można wyciągnąć z kodu?

Po pierwsze, zbudowanie generatora powoduje utworzenie unikalnego iteratora z określonym stanem i można przekazywać do niego parametry, które kontrolują jego działanie.

Po drugie, możesz przekazać parametr podczas wywoływania metody next() iteratora, a ta wartość zostanie przypisana do elementu znajdującego się po lewej stronie instrukcji yield z poprzedniego wywołania iteratora. To znakomity sposób zmieniania danych wyjściowych iteratora – w tym przypadku używamy go do kontrolowania, czy otrzymane słowo jest zapisane wielkimi literami. Jeśli chcesz wpłynąć na pierwszą otrzymaną wartość, zrób to za pomocą parametru konstruktora generatora.

Generatory mogą też generować albo iteratory ograniczone lub nieskończone. Jeśli korzystasz z iteratora nieskończonego, upewnij się, że występuje jakiś warunek terminala na podstawie wartości yielded – bardzo łatwo przypadkowo zapisać pętle nieskończone, zwłaszcza gdy używasz for..of do iteracji. Jeśli korzystasz z iteratora skończonego przez wywołania next(), właściwość .done zwracanego obiektu informuje, czy iteracja została zakończona.

Mamy nadzieję, że ten przykład, podobnie jak inne zasoby dostępne w internecie, wzbudzi zainteresowanie i zachęca do zastanowienia się nad wykorzystaniem generatorów we własnym kodzie. Wersje przeglądarki Firefox od wersji 31 oraz Chrome od 39 obsługują generatory natywnie. Projekt Regenerator zapewnia obsługę generatora w innych przeglądarkach. Jedną z możliwości jest też korzystanie z narzędzia Traceur.

Dziękujemy Erikowi Arvidssonowi za pomoc w odczytaniu tego artykułu.