w Praktyce
10 godz. 44 min · Node.js · Full-stack i Programowanie
Piotr PalarzWeb DeveloperW kursie tym poznasz platformę Node.js od podstaw, aż po bardziej zaawansowane koncepcje. Zaczniemy od omówienia czym jest Node, a także jakie może być zastosowanie tej technologii. Następnie przejdziemy przez proces instalacji i napisanie swojego pierwszego skryptu. Już na samym początku dokładnie omówimy tworzenie własnych modułów, gdyż jest to wiedza niezbędna, by dobrze zrozumieć funkcjonowanie Node. Następnie dowiesz się czym są zdarzenia, jak działa model “publish / subscribe”, a także czym jest “event-driven development”. Chwilę później omówimy pracę z buforami, stream’ami, a także ze standardowym wejściem i wyjściem. Ta wiedza pozwoli Ci zrozumieć jak tworzone aplikacje mogą otrzymywać od użytkownika dane oraz jak je wyświetlają.
Niezwykle ważnym rozdziałem będzie ten, poświęcony pracy z systemem plików. Szczególnie dla dewelopera, pracującego dotychczas z językiem JavaScript wyłącznie w środowisku przeglądarki internetowej. Node pozwala bowiem tworzyć nowe pliki, zapisywać w nich dane oraz odczytywać i usuwać pliki i katalogi. Przy wszystkich tych operacjach, niezwykle ważna jest jednak asynchroniczna natura Node. Dowiesz się jak działa, a także jakie są jej zalety. Na tym etapie nie zabraknie oczywiście praktycznego projektu, którym będzie aplikacja typu CLI (Command Line Interface). Pozwoli nam ona grupowo zmienić nazwy wielu plików, według podanego wzorca.
W kolejnym rozdziale poruszymy fundamentalną dla Node kwestię, mianowicie tworzenie serwerów sieciowych. Zaczniemy od najniższego poziomu, tworząc prosty serwer bazujący na protokole TCP/IP. Wiedza w jaki sposób Node radzi sobie z takim zadaniem, pozwoli później lepiej zrozumieć wyższe warstwy, takie jak np. HTTP czy WebSockets. Chwilę później stworzymy swój pierwszy serwer HTTP, do którego będzie można wysyłać zapytania wprost z przeglądarki internetowej. Omówimy również jak w Node wykorzystać bezpieczne szyfrowanie HTTPS.Na tym etapie będziesz już miał solidną wiedzę jak pracować z Node, jednak zanim przejdziemy dalej, zatrzymamy się, by omówić jak dokładnie działa Node.js, a także jak ta platforma została zbudowana. Dwie dość obszerne lekcje, będą być może jednymi z najważniejszych w tym kursie. Dowiesz się w nich czym jest, a także jak działa jednowątkowa pętla zdarzeń, czym jest proces i wątek, a także zobaczysz kod źródłowy Node.js i wszystkie bloki budulcowe, z jakich Node się składa. To naszym zdaniem niezwykle ważny temat, często jednak pomijany w wielu materiałach. Dzięki dogłębnemu zrozumieniu jednowątkowej natury języka JavaScript i asynchroniczności Node, będziesz mógł tworzyć dużo lepsze aplikacje.
Node.js posiada kilkanaście wbudowanych modułów, z których w dowolnej chwili można skorzystać. Wszystkie jednak dają nam dużą, niskopoziomową kontrolę, ale czasami sporo kodu należy napisać, by zrealizować jakieś zadanie. Jest to jednak świadomy wybór twórców Node, gdyż całą resztę świetnych rozwiązań, dopisuje nieustannie ogólnoświatowa społeczność deweloperów. Zdecydowana większość z modułów ląduje w rejestrze npm. Jest to genialny package manager, który pozwoli nam wyszukiwać, pobierać i aktualizować niezliczoną ilość modułów. Dowiesz się zatem jak korzystać z npm, a chwilę później zaczniemy wykorzystywać zgromadzone tam moduły w dalszej pracy.Jednym z najpopularniejszych modułów jest express.js, który pozwala w bardzo prosty sposób tworzyć serwery HTTP. Grzechem byłoby zatem z niego nie skorzystać. Dowiesz się wszystkiego co niezbędne, by zacząć tworzyć własne aplikacje bazujące na protokole HTTP z użyciem express.js, a także kilku dodatków do tego systemu. Zobaczysz jak routować zapytania, jak korzystać z middleware czy serwować statyczne pliki. Następnie omówimy współpracę z bazą danych MongoDB i z jej wykorzystaniem stworzymy API typu REST. Stworzona aplikacja pozwoli odczytywać dane z bazy i przesyłać je do klienta w formacie JSON, a także dodawać nowe dane, aktualizować i usuwać je. Wszystko to zgodnie z dobrymi praktykami pracy z REST.
Chwilę później czeka Cię kolejny praktyczny projekt - skracacz linków. Stworzymy wspólnie aplikację, która pozwoli skrócić dowolny adres URL do krótkiej formy. Oczywiście będzie działać również w drugą stronę! Kiedy przejdziemy pod skrócony adres, przekieruje nas do odpowiedniej witryny. Node.js to platforma wręcz stworzona do tworzenia aplikacji typu real-time, dlatego w kolejnym rodziale podejmiemy temat technologii WebSockets. Dowiesz się jakie są jej zalety, a także jak pracować z nią po stronie serwera i klienta. Tutaj czeka nas kolejny praktyczny, niezwykle ciekawy projekt. Wykonamy bowiem czat grupowy. Aplikacja ta pozwoli dowolnej ilości użytkowników podłączyć się do czatu podając swój nick, a następnie wysyłać widoczne dla wszystkich wiadomości. Zaimplementujemy nawet takie rozwiązania jak wyświetlanie statusów o dołączeniu kogoś do czatu, a także o jego opuszczeniu.W przedostatnim rodziale tego kursu podejmiemy tematykę dobrych praktyk pracy z Node.js. Na początku omówimy najważniejsze konstrukcje nowej specyfikacji EcmaScript 2015, które są znakomicie wspierane w Node. Chwilę później omówimy jak korzystanie z Promises uprości, a także ulepszy nasz kod. Wśród dobrych praktyk nie zabraknie również informacji o debugowaniu aplikacji. Zobaczysz sprawdzone sposoby, by znaleźć błędy lub lepiej, krok po kroku, zrozumieć jak działa napisany wcześniej kod. Dowiesz się również jak pracować z błędami, by Twoje aplikacje działały w sposób przewidywalny.
Ostatni rozdział w całości został poświęcony temu, co zwykle jest pomijane w innych materiałach, mianowicie wdrażaniu aplikacji do produkcji. Przez cały ten kurs pracować będziemy lokalnie, lecz kiedy aplikacje są już gotowe, wypadałoby udostępnić je światu. Wdrażanie aplikacji napisanych z użyciem Node.js nie jest jednak tak oczywiste, jak np. wgrywanie WordPress’a u wybranego hostingodawcy. Ty będziesz miał jednak możliwość zobaczyć, jak wdrożyć napisany przez nas grupowy czat na platformie Heroku. Jest to bardzo popularny serwis działający jako PaaS (Platform as a Service). Za darmo będziesz mógł w ciągu kilku chwil uruchomić swoją aplikację.Zobaczysz o co należy zadbać, by wszystko poszło gładko. Serwisy typu PaaS dbają o bardzo wiele aspektów wdrażania i serwowania naszych aplikacji, takich jak bezpieczeństwo oraz nieustanną dostępność. Mają jednak pewne ograniczenia. Z tego powodu, dowiesz się również jak wdrożyć swoją aplikację na serwerze wirtualnym VPS z systemem Ubuntu Server. Takie rozwiązanie daje nam całkowitą kontrolę, ale także obarczone jest większą odpowiedzialnością. Zaczniemy od instalacji na serwerze platformy Node.js, systemu kontroli wersji GIT, a także innych niezbędnych modułów
Następnie zobaczysz jak skonfigurować swoje lokalne środowisko tak, by za pomocą GIT’a wysyłać kod do zdalnego serwera, a potem jednym poleceniem wdrażać go do produkcji. Dowiesz się również jak jednocześnie serwować wersję produkcyjną oraz developmencką. W rodziale tym poruszymy również inne kwestie, takie jak procesy potomne czy tworzenie klastrów. Dzięki tej wiedzy, będziesz mógł maksymalnie wykorzystać dostępne zasoby serwera. Na sam koniec tego kursu rzucimy okiem również na inne, nie przedstawione wcześniej zastosowania Node, a także nakreślimy dalszą drogę nauki w tym zakresie.
Kurs ten jest dla wszystkich osób, które dobrze czują się w technologiach frontendowych, tj. HTML, CSS i JavaScript, a teraz chcą rozpocząć swoją przygodę z back-endem. Im zatem lepiej znasz język JavaScript, tym więcej wyciśniesz z Node, natomiast nie jest wymagana bardzo zaawansowana wiedza z zakresu tego języka.
W tej lekcji po części kontynuujemy temat z lekcji poprzedniej, dlatego że również będziemy zajmować
się Promise, ale tym razem z użyciem bardzo popularnej biblioteki Q.
Ta biblioteka posłuży nam do ubogacenia jednej z wcześniej stworzonych przez nas aplikacji, a konkretnie
restowego API, które wykorzystywało MongoDB.
Mam nadzieję, że ten przykład pamiętasz.
Jeżeli nie, to na pewno za moment odświeżysz sobie pamięć.
Na początek będę chciał zainstalować wszystkie dependencies.
Wpiszemy zatem npm install. I teraz zanim uruchomimy naszą aplikację nodemonem, to chcę ci pokazać stronę
tego projektu.
Biblioteka nazywa się Q i jest bardzo popularna.
Możemy to zobaczyć choćby po ilości pobrań.
Wczoraj prawie 300 tysięcy razy, a więc i my teraz do tego będziemy chcieli się dołożyć.
Możesz zobaczyć tutaj na początek opis. Czyli widzimy, jaki jest problem w Node.js często z tymi zagnieżdżonymi callbackami,
a jak ładnie może to wyglądać przy użyciu właśnie tego typu biblioteki,
generalnie kiedy korzystamy z Promise. Jeżeli chodzi o taki kod, to zostało to nazwane callback hell i nawet
mamy specjalną stronę poświęconą temu zagadnieniu, która jest pod adresem callbackhell.com. Spróbujmy
zatem ten problem rozwiązać i przejdziemy sobie tutaj i zainstalujemy tę bibliotekę, czyli npm install
i save. Teraz możemy standardowo uruchomić nasz serwer nodemonem.
Ok. Przejdźmy sobie do kodu i na początek będziemy chcieli skorzystać z tej biblioteki.
Zrobimy to w users. Zerknij sobie na te przykłady, jeżeli nie pamiętasz dokładnie, jak ta aplikacja działała.
Będę chciał tutaj na samej górze zaimportować sobie poprzez require bibliotekę Q. Nazwiemy sobie stałą dużą
literą Q, a tutaj z małej litery.
Tak nazywa się ten moduł. I teraz zanim zmienimy cokolwiek w tym pliku, to będę chciał ci pokazać, w jaki
sposób chciałbym, abyśmy korzystali z Promise i z biblioteki Q, a później tutaj sobie to zaimplementujemy.
Dlatego przejdziemy do pliku index.js.
I tutaj mamy taką ścieżkę, która pozwala nam wczytać wszystkich użytkowników, a poniżej mamy taką, która
pozwala nam wczytać jednego wybranego użytkownika.
Zobaczmy, jak to wyglądało.
Tutaj po odświeżeniu mamy listę i po kliknięciu przechodzimy do strony pojedynczego użytkownika. I dokładnie
to będę chciał zmienić z użyciem Promise.
Zauważ, co my tutaj robiliśmy.
Otóż korzystaliśmy z metody get i przekazywaliśmy jej najpierw id, a później callback do wykonania.
Kiedy pobierzemy dane z bazy danych ten callback, czyli cała ta funkcja, przechodziła nam tutaj do getUser
i wpadała pod cb.
Teraz będę chciał się tego pozbyć - ale tutaj będziemy pracować za chwilę - a tutaj zmienić kod, aby wyglądało
to następująco.
Otóż chciałbym również odwoływać się do users.get.
Natomiast nie chcę tutaj podawać callback, ale chcę skorzystać z Promise, czyli podobnie w przypadku biblioteki Q
to wygląda, jak widziałeś w lekcji poprzedniej.
Możemy napisać then i to jest funkcja, która wykona się, kiedy będziemy mieli z takiej Promise resolve, a następnie
catch wtedy, kiedy będziemy mieli error.
I tutaj chciałbym sobie ten błąd zapisać. Zaraz z tego będziemy korzystać, chociaż w zasadzie należy przekazać
funkcję.
Tutaj możemy zakończyć, a tutaj wpiszemy sobie funkcję anonimową.
Ona będzie przyjmowała data, czyli dokładnie to, co podawaliśmy do callback jako drugi parametr i będziemy
chcieli tutaj zrobić to samo, co mieliśmy wcześniej. Skopiuję, wkleję tutaj. Czyli tutaj już nie musimy sprawdzać,
czy był błąd, bo ta funkcja wykona się tylko wtedy, kiedy nie było błędu, a tutaj sobie obsłużymy
przypadek z błędem i będziemy chcieli skorzystać z takiego zapisu.
Zauważ, o ile ten zapis jest czystszy i krótszy.
Natomiast teraz jeszcze bardziej go sobie skrócimy, korzystając z arrow functions w ECMAScript6, które pokazywałem ci już wcześniej.
Dlatego zamiast pisać function i tutaj data, to znowu wytnę sobie
to wszystko i pokażę ci skrócony zapis. Otóż możemy napisać data i zrobić to w ten oto sposób.
Tutaj nie może być średnika, ale tak będzie to wyglądać poprawnie.
Czyli nie muszę nawet tworzyć bloku takiego kodu, bo wszystko mamy tutaj tak naprawdę w jednym wywołaniu.
To jest poprawna anonimowa funkcja stworzona za pomocą arrow function i tutaj będę chciał zrobić podobnie.
Czyli chcę mieć funkcję, która przyjmuje error i następnie wykonuje nam taki kod.
Zauważ, o ile krótsza jest teraz ta funkcja i tak naprawdę to jest wszystko.
Teraz będziemy musieli tylko zaktualizować sobie funkcję getUser, aby działała w taki sposób, żeby to
wszystko działało poprawnie.
Tak naprawdę nie musimy tutaj wiele zmieniać.
Na początek utworzymy sobie nową zmienną, którą nazwę def i skorzystamy z Q, które mamy zaimportowane
na górze. I następnie metody defer. To zwróci nam specjalny obiekt i na końcu będziemy chcieli coś zwrócić.
Dlatego że zauważ - ta funkcja getUser musi zwrócić coś, tak jak ją tutaj wykonamy, bo to jest ta funkcja,
co zawiera metody then i catch.
No i właśnie to coś jest to Promise i możemy to zwrócić. Odwołamy się do def.promise
i w ten sposób zwrócimy właśnie taki obiekt Promise.
I teraz pozostało nam tylko tutaj zmienić te wywołania.
Czyli jeżeli będziemy mieli błąd, to chcemy zrobić def.reject.
Zauważ, że wygląda to tak jak w tych natywnych Promises, które widziałeś w lekcji poprzedniej.
A jeżeli będzie wszystko ok, to def.resolve.
Natomiast tutaj już nie podajemy tego null, ale po prostu user. Czyli prześledźmy raz jeszcze.
Jeżeli będzie błąd, to mamy def.reject i error.
I wtedy wykona się catch i ten error wpadnie tutaj.
Natomiast jeżeli będzie resolve, czyli wszystko w porządku, to user wpadnie nam tutaj pod data i powinno
to wszystko działać.
Dlatego teraz mogę tutaj odświeżyć i zauważ, że wynik jest dokładnie taki sam.
A to oznacza, że zmieniliśmy sobie zapis, ale działanie ciągle jest takie samo.
Jak zatem widzisz, tutaj w index znacznie sobie skróciliśmy nasz kod.
Tutaj tak naprawdę się nie skrócił.
Delikatnie się wydłużył, ale warto jest to w ten sposób zrobić
nawet jeżeli będą to dwie dodatkowe linijki kodu. Teraz możemy zrobić to samo, jeżeli chodzi o funkcję
list, którą mamy w tym miejscu. Możemy przejść do strony głównej.
i to właśnie tutaj jest ten widok renderowany.
Znowu nie będziemy tutaj chcieli mieć callback, tak jak to robiliśmy.
Ale chciałbym mieć taki zapis.
Zatem users.list i następnie chciałbym się odwołać do then. I tutaj podać sobie funkcję, znowu będziemy
mogli tutaj podać arrow function.
Jaki mamy problem?
Otóż tutaj funkcja nasza przyjmowała error, a także users.
I tutaj nie sprawdzaliśmy już if, czy był błąd, czy nie było błędu, tylko robiliśmy tutaj taki skrócony
zapis.
Więc teraz jeżeli sobie wykonam then, to to się wykona tylko wtedy, jeżeli nie było błędu, a jeżeli będziemy
mieli catch, to znaczy, że był błąd. I musiałbym ten kod powtórzyć. Bo zerknijmy sobie do metody list. Ona się znajduje
tutaj niżej. Ona tak samo będzie nam wywoływać callback z errorem albo bez erroru. Zaraz sobie to zmienimy na Promise.
Natomiast o co mi chodzi? O to, że jeżeli tutaj sobie wstawię funkcję, która będzie mieć tylko jeden parametr,
bo będziemy mogli w reject albo w resolve podać tylko jeden parametr, to nie nazwę go ani error, ani users, bo nie
wiem, co pod nim będzie.
Nazwę go data i następnie zrobimy sobie taki oto blok kodu. To problem pojawi się taki, że tutaj nie ma
sensu sprawdzać, czy był błąd, bo nawet to nie zostało tutaj przekazane.
A ta metoda na pewno wykona się wyłącznie wtedy, kiedy będziemy mieli tutaj resolve.
Dlatego chciałbym od razu tutaj usunąć callbacki, zrobić sobie dokładnie to samo, co wcześniej, wyżej,
czyli będziemy mieli obiekt defer, Q.defer. Na końcu będziemy chcieli zwrócić promise.
No i tutaj jeżeli był błąd, to zrobimy sobie def.reject,
Przekażemy ten błąd.
Jeżeli nie było błędu, to def.resolve i z jednym parametrem, czyli users. Teraz będzie to dobrze widać.
Czyli jeżeli wszystko było ok, to będziemy mieli def.resolve i wykona się ta funkcja.
Dlatego teraz musiałbym to wszystko powielić w następujący sposób i then zmienić na catch.
I tutaj musielibyśmy sobie zrobić coś takiego.
Czyli tu wiemy, że na pewno był error.
Czyli chcemy przekazać pustą tablicę. Tutaj natomiast wiemy, że nie było błędu. Możemy przekazać users,
ale nie mamy już users, tylko zmienną nazwałem data.
No ale jak widzisz, trochę tego kodu sobie zduplikowaliśmy.
Dlatego teraz pokażę ci prostszy sposób, jak możemy to zrobić.
Tutaj wrócę do takiej formy, jak była przed momentem. To sobie usuniemy. To również usunę, a całą tę funkcję
będę chciał stąd wyciąć. I wstawimy ją sobie tutaj u góry. Nazwę zmienną send i przypiszę tutaj całą tę funkcję.
Jeżeli to dziwnie wygląda, to możesz sobie to zamienić na funkcję anonimową.
Możemy to w zasadzie zrobić, nie ma żadnego problemu. Więc możemy nawet napisać function.send i funkcja
ta będzie przyjmować data w ten sposób.
I teraz o co mi chodzi?
Otóż możemy tutaj na then przypisać data i następnie na catch również data.
Ostatnia rzecz, która nas tutaj interesuje, to fakt, że mamy tutaj ten error, czyli jeżeli tutaj będzie reject, to
będziemy mieli error. Jeżeli resolve, to users. Tutaj nazwałem to data. Czyli pod data będzie albo error, albo będziemy
mieli users. Czyli tutaj jeżeli wszystko jest ok, chcę przekazać data, a tutaj chcę sprawdzić, czy mamy błąd.
W jaki sposób możemy sprawdzić, czy pod data jest błąd przekazany w tym miejscu?
Otóż odwołamy się operatorem instanceof, który jest w JavaScript.
Sprawdzimy, czy data instanceof error, czyli taki operator powie nam, czy ten obiekt jest instancją klasy
error. Jeżeli tutaj będzie zwrócony błąd z MongoDB, to będzie on w instancji klasy error i wszystko będzie w
porządku. Moglibyśmy również tutaj odwołać się do wbudowanego modułu Node, który nazywa się util i on
ma taką metodę jak utilIsError.
Natomiast nie ma sensu go tutaj specjalnie importować, jeżeli potrzebujemy wyłącznie takiego prostego
zapisu.
Więc teraz mam nadzieję, że wiesz o co chodzi. Jesteśmy tutaj i mamy .then(data).
Jeżeli tutaj będzie def.resolve(users), to wywoła się ta funkcja send i ona sobie tutaj sprawdzi, czy data
to jest błąd.
Nie. Więc wywołamy sobie to, co jest w tym miejscu.
I wszystko będzie ok.
Natomiast w przypadku reject to będzie obiekt błędu. Znowu wykona się ta sama funkcja.
Tutaj będziemy mieli true, dlatego podstawimy do widoku pustą tablicę. Zobaczmy, czy to wszystko działa. Odświeżam.
Mamy jakiś błąd: data is not defined w 26 linijce. Zerknijmy.
No właśnie. Na pewno to już zauważyłeś. Tutaj powinienem napisać send. Czyli chcę przypisać po prostu funkcję
obsługi tego zdarzenia dla tej promise. Odświeżmy. I teraz wszystko działa jak należy.
Jak widzisz, znacznie znowu uprościliśmy sobie kod i stworzyliśmy taką funkcję, która wywołuje się i tutaj, i tutaj,
aby nie powtarzać kodu.
Na koniec chciałbym ci tylko przypomnieć, że mamy tutaj całe nasze API w tym katalogu i w tym pliku, które
właśnie znowu korzystało z callbacków, czyli z tego starego sposobu.
I chciałbym to teraz wszystko przerobić na Promises, dlatego najpierw przejdę do users i każdą z funkcji,
którą tutaj mamy, będę chciał przerobić dokładnie w taki sam sposób, jak już ci pokazałem. Czyli callback
nie będzie potrzebny, a tutaj będziemy sobie tworzyć obiekt defer poprzez Q.defer, na końcu zwracać promise
i zamieniać wszystkie wywołania callback na def.reject oraz def.resolve, usuwając ten parametr null.
Ja teraz to zrobię i za sekundę wracam. Ok. Gotowe.
Tak to będzie wyglądać dla wszystkich funkcji. Możesz sobie tutaj zerknąć i przepisać to u siebie.
Natomiast teraz będziemy chcieli jeszcze nasze jsonowe API zaktualizować w tym pliku. Znowu - żeby nie korzystać
z callbacków, czyli np. users.list i tutaj callback. To będziemy chcieli to przerobić w następujący sposób. Mianowicie:
users.list -
wiemy, że z tego zostanie zwrócona promise, więc będziemy mogli zrobić then, przyjąć sobie tutaj data.
Czyli wiemy, że wszystko było ok.
Zrobimy to z taką arrow function.
No i kiedy mieliśmy data, to mieliśmy takie wywołanie. Możemy to wrzucić tutaj. Pod spodem zrobimy catch.
Przyjmuje error, więc zrobimy err i cały ten zapis, który jest tutaj, również możemy sobie tutaj wrzucić
i trochę go uprościć.
Już ci pokażę, na czym sprawa polega.
Otóż sformatujemy sobie to w ten sposób. Mianowicie to, co tutaj zrobiłem, to zamiast mieć dwa razy res.status
i potem res.json, to połączyłem sobie res.status i potem od razu json, bo możemy to robić w ten sposób.
Zauważ, o ile ta funkcja stała się teraz krótsza i będzie to działać również poprawnie.
Dlatego teraz pozostałe będę chciał w ten sam sposób zaktualizować do użycia z tym nowym API, które tutaj
uzupełniłem.
I za moment wracam. Ok. I gotowe.
Tym razem będzie to wyglądać następująco.
Jak widzisz, to wszystko już działa.
Możemy sobie jeszcze przetestować, abyśmy mieli pewność, że wszystko jest ok.
Kopiujemy sobie ID takiego pojedynczego użytkownika i teraz będziemy chcieli skorzystać z naszej ścieżki api
i zobaczyć, czy wszystko działa tak, jak należy.
Jak widzisz json jest poprawnie zwracany. W ten sposób mogliśmy za pomocą biblioteki Q znacznie
ułatwić sobie pisanie naszego kodu.
I jak widzisz, jest on po pierwsze czystszy,
po drugie działa trochę lepiej niż działał wcześniej.
No i w tym pliku zaoszczędziliśmy sporo linijek kodu i zyskaliśmy na czytelności.
A np. w tym pliku niewiele się musiało zmienić, abyśmy mogli nasze API dostosować właśnie do korzystania
z Promise.
Polecam ci przejść jeszcze na stronę dokumentacji Q.
Jeżeli pojedziesz tutaj do npm, to następnie będziesz mógł przejść tutaj niżej i przejść do Q API reference
i tutaj przeczytać sobie o wszystkich metodach, dlatego że jest ich jeszcze więcej. I z takiej biblioteki korzysta
się znacznie lepiej niż z tych natywnych Promises.
Dlatego zachęcam cię, abyś na każdym etapie pisania swojego kodu zastanawiał się, czy być może nie warto
byłoby skorzystać z Promise.