Javascript – obsługa asynchroniczności.

article-thumbnail

Znamy już podstawy javascript’u. Wiemy też, że jest on synchroniczny czyli polecenia wykonują się po kolei, linia po linii. Nastręcza to jednak kilku trudności. Otóż jeżeli znajdzie się skrypt, który potrzebuje dużo czasu na wykonanie np. wygenerowanie reklamy, wówczas strona jest zawieszona, czyli żadna inna funkcjonalność się nie wykona. Użytkownik klika przycisk, a tu zonk. Nic się nie dzieje, mimo że wszystko jest zaimplementowane poprawnie. Aby temu zapobiec zaimplementowano w silnikach przeglądarek obsługę asynchroniczności. Istnieją trzy sposoby jak obsługiwać działania asynchroniczne.

CallBack

Callback to najstarszy sposób obsługi asynchroniczności. Opiera się on na przesyłaniu funkcji jako argumentu do funkcji asynchronicznej. Następnie funkcja asynchroniczna po wykonaniu wszystkich operacji, uruchamia otrzymaną w argumencie metodę. Wiem, że brzmi to dziwnie, ale inaczej nie da się tego ująć. Poniższy kod może to trochę rozjaśni.

function asynchronicznaFunkcja(callback)
{
  //działania asynchroniczne np. łączenie z bazą danych
  callback();
}

function zrobNastepnie()
{
  console.log("zrobione");
}

asynchronicznaFunkcja(zrobNastepnie);

Metoda ta jest niezbyt intuicyjna i tym bardziej problematyczna, im więcej funkcji chcemy przesłać. Jeżeli na rezultacie funkcji asynchronicznej chcemy wykonać powiedzmy 3 operacje (dla bardzo prostych programów to zazwyczaj wystarcza) To musimy przesłać aż trzy callbacki, a następnie je zagnieździć. Nie dość, że łatwo w tym popełnić błąd, to jeszcze jest kompletnie nie czytelne. W śród programistów możemy usłyszeć na to określenie CallBack Hell.

Promise

Obiekt klasy Promise otrzymuje w konstruktorze funkcję, która wykonuje działania asynchroniczne. W zależności od wykonania funkcji obiekt przyjmie jeden z trzech stanów.

  1. Pending -> jest to stan, który otrzymujemy gdy funkcja wciąż się wykonuje. W tym stanie, nie mamy jeszcze dostępu do rezultatu funkcji.
  2. Rejected -> ten stan otrzymamy, gdy nie uda się wykonać funckji asynchrnicznej. Gdy otrzymamy ten stan, może się wykonać metoda catch(), która służy do obsługi błędów. Musimy jednak najpierw jej wskazać, co ma zrobić. Słowem musimy przekazać jej funkcję która obsłuży błąd.
  3. FullFilled -> to stan otrzymywany gdy funkcja asynchroniczna wykona się bez błędu. W tym stanie mogą wykonywać się metody then(). One również przyjmują funkcje jako argumenty, z tą różnicą że zapis jest czytelniejszy. Warto zaznaczyć, że te metody mogą zwracać obiekt klasy Promise, stąd możemy wykonać łańcuch metod then();

Mniej więcej może to wyglądać tak:

const promise = new Promise(()=>{
  //adziałanie asynchroniczne.
})

promise.catch(()=>console.log("Error"))
promise.then((wynik)=>console.log(wynik).then(()=>zrob_nastepne())

Async / Await

Najnowszym i moim zdaniem najwygodniejszym sposobem poradzenia sobie z asynchronicznością, jest zastosowanie składni async await. Najpierw musimy zdefiniować funkcję asynchroniczną. Używamy do tego wspomnianego słowa async, umieszczonego przed nazwą funkcji (lub w przypadku funkcji strzałkowej przed nawiasem ). Wewnątrz tej funkcji możemy użyć polecenia await, które zablokuje wykonywanie się dalszych fragmentów funkcji do momentu, aż działanie asynchroniczne nie zostanie zakończone. Na przykład

async function asynchronicznaFunkcja()
{
  const result = await dzialanieAsynchroniczne();
  return result;
}

asynchronicznaFunkcja();

Musimy jednak tutaj zwrócić uwagę na dwie rzeczy. Po pierwsze await możemy użyć tylko wewnątrz funkcji asynchronicznej. Po drugie funkcja asynchroniczna zawsze zwróci obiekt klasy Promise. Nie możemy też zapomnieć o obsłudze błędów. Zapraszam do osobnego wpisu w tym temacie.