Promisy w JavaScripcie

24 sierpnia 2016 Bez kategorii No comments

promise-states

To jasne, że kod wykonywany synchronicznie jest prostszy w pisaniu i debugowaniu, ale wystarczy sobie wyobrazić jak wyglądałyby dzisiejsze aplikacji webowe bez możliwości wykonywania kodu asynchronicznie (np ładowanie danych z zewnętrznych resources’ów). JavaScript daje nam narzędzie ułatwiające tworzenie asynchronicznych wywołań – Promise API.  Promise to coś takiego co reprezentuje ewentualny rezultat asynchronicznej operacji.

Każdy promise może znajdować się w 3 stanach:

  • pending – operacja w toku
  • fulfilled – operacja zakończona powodzeniem
  • rejected – operacja nie powiodła się

 

Podstawowa różnica pomiędzy wywołaniem synchronicznym i asynchronicznym:

 

 

Jak stworzyć promisa bez frameworka typu jQuery czy Angular ?

 

Rezultat wywołania then można zwracać do kolejnej operacji:

 

Można również przekazywać promisa:

Jest do bardzo przydatne szczególnie wtedy kiedy musimy wywołać wiele asynchronicznych strzałów do backendu, a wartość zwrócona z jednego requesta jest potrzebna do wywołania następnego z nich.

 

Promise.all

Wyobraźmy sobie jeszcze sytuacje w którym musimy wykonać wiele requestów, a aplikacja ma rozpocząć działanie dopiero wtedy gdy wszystkie są zakończone:

 

Promise.race

Możemy również odpalić wiele requestów, ale chcieć uzyskać rezultat tylko najszybciej zakończonego:

 

Guava – podstawy

18 lipca 2016 Bez kategorii No comments

Guava to bardzo przydatna biblioteka do Javy stworzona przez Google. Pomimo pojawienia się JDK 8, które dostarcza nam więcej utilsów, Guava nadal pozostaje podstawową biblioteką do Javy, upraszczającą codzienne programowanie, wykorzystywanie najpopularniejszych algorytmów i sprawiającą że kod wygląda bardziej “elegancko”. Poniżej przedstawienie kilku podstawowych ficzerów Guavy.

 

Unikanie NullPointerException

 

Wartość null jest powszechnie spotykana w Javie. Programiści często zapominają zabezpieczyć program przed wystąpieniem null’a (np dla informacji pobieranych z bazy danych). Guava pozwala nam “opakować” potencjalnie niebezpieczny fragment kodu w ten sposób:

 

 

Możemy również od razu wyznaczyć wartość, która ma zostać zwrócona w przypadku pojawienia się null:

 

 

Sortowanie kolekcji

 

Guava wspiera nas również przy sortowaniu np list. Możemy skorzystać z dwóch zdefiniowanych typów sortowania: natural i usingToString() lub zdefiniować własne sortowanie:

 

 

Przydatnym również jest tzw chaining, czyli opakowywanie jednego typu sortowania w inny, aby uzyskać ostateczny efekt:

 

 

Myślę, że jest to tak bardzo czytelne, że nie wymaga komentarza.

 

Equals i hashCode

 

W Javie każda klasa modelująca dane (np encja) powinna mieć zaimplementowaną metodę equals, która służy oczywiście do porównywania obiektów. Z kontraktu equals-hashCode wynika, że implementacja equals powinna pociągnąć za sobą również dodanie metody hashCode (złamanie kontraktu może skutkować np niepoprawnym sortowaniem kolekcji). Guava również i tutaj dostarcza nam bardzo fajne utilsowe metody, które pozwalają łatwo i bez bólu zaimplementować wspomniane wyżej metody:

 

 

 

Pamiętajmy, że większość profesjonalnych IDE ma wbudowaną opcję wygenerowania tych metod na podstawie podanego przez nas szablonu (np z Guava).

 

Baza danych w przeglądarce

29 maja 2016 HTML5, JavaScript No comments

W raz z pojawieniem się HTML5 dostaliśmy nowe narzędzia mogące rozwiązać odwieczne problemy z przechowywaniem danych po stronie przeglądarki (offline). Mam tu na myśli localStorage i sessionStorage. Zaletą tych API jest możliwość rozszerzania powierzchni dostępnej na dane użytkownika (domyślnie 5 MB w Chrome) w stosunku do ograniczonego rozmiaru cookies wynoszącego 4096 bajtów. Dodatkową wadą cookies jest również to że są przesyłane w nagłówku HTTP podczas każdego requestu, co w przypadku składowania większej ilości danych nie jest wskazane.

storage
LocalStorage w przeciwieństwie to sessionStorage pamięta wszystkie dane dopóki ich ręcznie nie usuniemy, np. poprzez wyczyszczenie cache przeglądarki.
Dane zgromadzone w localStorage są dostępne w przeglądrce jako window.localStorage, gdzie od razu można zauważyć, że zapisane obiekty i tablice są serializowane do stringów, co prowadzi do tego, że wykonywanie skomplikowanych operacji na pamięci wewnętrznej przeglądarki jest co najmniej upierdliwe. Specyfikacja HTML5 zapewnia nam w tym miejscu dwa API, które możemy wykorzystać do potraktowania localStorage jako transakcyjnej bazy danych. Są nimi WebSQL oraz IndexedDB, które zostało dodane na późniejszym etapie prac nad HTML5 i jest niestety wspierane tylko przez najnowsze przeglądarki. W mojej opinii najlepszym sposobem na zarządzanie localStorage przy pomocy IndexedDB lub WebSQL jest wykorzystanie jednej z kilku dostępnych w sieci bibliotek. Ja przećwiczyłem Dexie.js , którą gorąco polecam (możemy ją pobrać zarówno npm’em jak i bower’em).
Poniżej przykład dodawania i odczytywania danych:

Wygląda to bardzo przejrzyście, prawda ?
Na koniec chciałbym wspomnieć jeszcze o pracy aplikacji w trybie offline (brak połączenia z backendem). HTML5 pozwala na zdefiniowanie pliku cache manifest, który definiuje źródła wymagane do pracy aplikacji oraz reguły postępowania w przypadku ich braku. Przykładowo mamy plik JS który odpowiada za zapis danych do bazy (backendowej). W sytuacji kiedy jest ona niedostępna możemy ten plik podmienić innym, który zapisze nasze dane do localStorage, dopóki nie będzie wznowione połączenie z Internetem. Do prawidłowego działania cache manifest wymagana jest odpowiednia konfiguracja serwera HTTP. W moim przypadku był to Apache, więc cała konfiguracja sprowadziła się do odkomentowania linii w httpd.conf:

Przykładowy plik cache manifest wygląda tak:

Pliczek ten musimy podpiąć do naszego index.html w ten sposób:

Podczas testowania trybu offline w chromie polecam URL: chrome://appcache-internals. Przyda się jeżeli chcemy uniknąć problemów z aktualizowaniem zawartości naszych plików JS :).

Reasumując mam nadzieję, że ten artykuł zapobiegnie tworzeniu kolejnych aplikacji które bezmyślnie “uderzają” ajaxem do backendu w celu pobrania danych zmieniających się raz na rok.

Jak działa wirtualna maszyna Java

24 kwietnia 2016 Java No comments , , , ,

Bardzo wielu developerów tworzy bardzo udane projekty w Javie bez wiedzy jak działa wirtualna maszyna (JVM). Mimo wszystko uważam, że warto przynajmniej trochę orientować się “jak to działa pod spodem”.

 

Na początek kilka słów wstępu. W językach takich jak C++ kompilator tłumaczy kod aplikacji bezpośrednio do formatu binarnego. Wadą takiego rozwiązania jest ścisła zależność od systemu operacyjnego na którym ma działać nasza aplikacja. Proces kompilacji Javy jest podobny, przy czym kod programisty jest tłumaczony na pewien byt pośredni – kod bajtowy. On z kolei jest interpretowany przez JVM (Java Virtual Machine), która “załatwia” za nas cały problem różnorodności sprzętu na którym uruchamiamy aplikację (WORA – Write Once Run Anywhere)

Aby wirtualna maszyna Java mogła emulować rzeczywisty komputer musi posiadać/realizować kilka zasadniczych funkcji:

 

  1. Rejestry – w odróżnieniu od architektury x86 rejestry nie są wykorzystywane do przechowywania argumentów (np. podczas operacji arytmetycznych). W JVM rejestry są wykorzystywane do zapamiętania stanu wykonania aplikacji i są one aktualizowane po każdej wykonanej linii kodu. Warto dodać że wszystkie rejestry mają długość 32 bit.
  2. Stos – każda instrukcja kodu bajtowego bierze parametry ze stosu, wykonuje na nich operację i zwraca rezultat. Stos działa w oparciu o LIFO, co sprawia że “spodziewa” się parametrów w określonym miejscu i porządku. Należy wspomnieć tutaj o tzw. stack frame, który jest elementem każdej metody w kodzie i oznacza ile stosu potrzeba na przechowywanie zmiennych lokalnych, środowiskowych i na wykonywane operacje.
  3. Środowisko wykonywania – tłumaczenie kodu bajtowego na natywny (dla danego OS) jest realizowane na 2 sposoby:
    1. Interpreter – szybko tłumaczy kod, wolno go wykonuje
    2. JIT (Just-In-Time) kompilator – cały kod jest najpierw tłumaczony do postaci natywnej, a następnie w całości wykonywany. Jest to często spotykany efekt “udławienia” się Javy przy starcie aplikacji.

Jaka jest różnica ? Jeżeli kod ma być wykonywany tylko raz, bardziej optymalnie jest go interpretować linijka po linijce. JIT jest wykorzystywany wtedy gdy dana metoda jest odpalana więcej razy niż pewien zadany poziom.

  1. Zarządzanie pamięcią – podczas uruchomienia aplikacji w Javie programista może zadeklarować minimalną i maksymalną ilość pamięci RAM (sterty) której może używać program. W celu zapobiegania przekroczeniu tego limitu (co i tak często się zdarza) architekci Javy wymyślili coś takiego jak Garbage Collector. Odpowiada on za wyszukiwanie i usuwanie obiektów (komórek pamięci) do których nie prowadzą żadne referencje. Dzięki temu nasza pamięć jest regularnie “odśmiecana” a programista nie musi martwić się o jej manualną dealokacją z poziomu kodu.
  2. Obszar stałych i metod – JVM wyodrębnia osobne miejsce w pamięci do przechowywania stałych klasy i skompilowanych do kodu bajtowego metod.
  3. Zbiór instrukcji – instrukcje kodu bajtowego są całkiem podobne do instrukcji Assemblera. Można to łatwo podejrzeć przy wykorzystaniu narzędzia dostarczonego razem z JDK czyli tzw. disassemblerem.

Klasa Test.java:

Kompilujemy ją komendą javac Test.java i wykonujemy polecenie javap na pliku Test.class

 

 

Podsumowując – większa świadomość tego jak jest kompilowany i działa program w Javie może przełożyć się na lepsze zrozumienie pewnych procesów i zwracanie większej uwagi na dbałość o wydajność tworzonych programów, co przekłada się bezpośrednio na komfort pracy użytkownika.