Chciałbym dzisiaj przedstawić problem z jakim możecie się spotkać podczas obsługi zdarzeń. Naturą języka jest asynchroniczność, spotykana jest w wielu miejscach. Asynchroniczne zapytania do serwera, czyli AJAX, to technika bardzo popularna w dzisiejszych czasach.
Pewnym rodzajem asynchroniczności są też zdarzenia aplikacji. Częsty problem początkujących developerów objawia się tym,
że próbują oni dostać się do DOMu, kiedy nie jest on jeszcze załadowany.
Trzeba wtedy skorzystać z window.onload
(albo lepszej formy window.addEventListener('DOMContentLoaded', ...')
.
Ewentualnie można skorzystać z load
(window.addEventListener('load', ...)
jeśli event DOMContentLoaded
nie jest wspierany.
Customowe eventy
Customowe eventy, to bardzo popularny wzorzec w aplikacjach webowych. Zdarzenia te nie są wyzwalane przez interakcję użytkownika, tylko przez samą aplikację. Dobrym przykładem może być zapytanie do serwera.
Zerknijmy na taki kod:
Mamy zapytanie do serwera, gdzie na zakończenie wyzwolone zostanie odpowiednie zdarzenie.
Kiedy zapytanie się powiedzie, zostanie wyzwolone zdarzenie success
,
w przypadku błędu - error
.
Chcielibyśmy teraz skorzystać z funkcji ajax
, aby pobrać dane dotyczące użytkownika.
Wykonujemy więc taki kod:
Nasłuchujemy na 2 zdarzenia:
- na poprawne pobranie danych z serwera wyświetlamy profil użytkownika
- kiedy zapytania zakończy się niepowodzeniem wyświetlamy błąd załadowania profilu
Na pierwszy rzuta oka nie ma tutaj nic dziwnego. A jednak. Co jeśli skorzytamy z funkcji ajax
w innym miejscu?
Chcemy pobrać zawartość kategorii produktów w sklepie internetowym, korzystając oczywiście z funkcji ajax
.
Mamy problem. Dlaczego? Bo nie wyrejestrowaliśmy handlerów z pierwszego zapytania.
Powiecie: "No ale przecież używamy tam once
, to handler sam się wyrejestruje po wykonaniu zdarzenia."
Zgadza się, ale tylko użyty handler się wyrejestruje. Nigdy nie będzie sytuacji, że zapytanie uruchomi
dwa zdarzenie success
i error
.
Rozwiązanie problemu
Jest kilka rozwiązań opisywanego przeze mnie problemu. Najlepsze (dla użytego kodu) jest takie, aby w dowolnym handlerze wyrejestrować oba handlery.
Najlepsze rozwiązanie leży u podstaw tego kodu. Nie należy zapisywać się
na zdarzenia obiektu współdzielonego. Wtedy nie będzie problemu.
Lepsze rozwiązania w tym przypadku to używanie Promise
-ów
albo callback
-ów.
Testowy projekt
Stworzyłem na potrzeby tego artykułu projekt na GitHubie. Chciałem pokazać Wam jak na faktycznym kodzie, objawia się opisywany przeze mnie problem.
Wystarczy uruchomić przykład w przeglądarce (plik demo/index.html
), albo w terminalu uruchomić polecenie node index.js
,
aby zobaczyć opisywany problem. Poniżej zrzut ekranu z błędem.