Asynchroniczność w JavaScript

JavaScript

readTime

3 min

Asynchroniczność w JavaScript

Jeśli zaczynasz programować w JavaScript i zastanawiasz się, jak działa asynchroniczność w JavaScript, to trafiłeś we właściwe miejsce.

W tym artykule wyjaśnię Ci, czym jest asynchroniczny kod, jak go obsługiwać i dlaczego jest tak ważny w nowoczesnym tworzeniu aplikacji.

Przygotuj się na podróż po świecie asynchroniczności, gdzie nauczysz się korzystać z callbacków, promise i async/await w JS.


Co to jest kod synchroniczny w JavaScript?

Zanim zanurzymy się w asynchroniczność, musisz zrozumieć, czym jest kod synchroniczny.

W JavaScript kod jest wykonywany linia po linii, co oznacza, że każda linijka musi zostać zakończona, zanim przystąpimy do wykonania następnej.

Przykład kodu synchronicznego

javascript
console.log("Start");
for (let i = 0; i < 1000000000; i++) {
  // Simulating a heavy operation
}
console.log("End");

W powyższym przykładzie najpierw wyświetlamy 'Start', następnie wykonuje się ciężka operacja (pętla for), a na końcu 'End'. W czasie wykonywania pętli reszta kodu jest zablokowana i nie może kontynuować działania.

Problemy z kodem synchronicznym

Kod synchroniczny może prowadzić do problemów, gdy jakaś operacja wykonuje się dłużej.

Na przykład, jeśli pobieramy duży plik lub wykonujemy skomplikowane obliczenia, interfejs użytkownika może zostać zamrożony. To oznacza, że użytkownik nie może wchodzić w interakcje z aplikacją, co jest frustrujące.

Asynchroniczność w JavaScript

Aby rozwiązać te problemy, wprowadzono asynchroniczność.

Asynchroniczny kod pozwala na wykonywanie innych zadań w trakcie trwania długotrwałych operacji. Dzięki temu aplikacja pozostaje responsywna, a użytkownik może nadal z niej korzystać.

JavaScript nie jest asynchroniczny z natury, ale dzięki mechanizmom takim jak callbacki, promise i async/await możemy obsługiwać asynchroniczność w naszych aplikacjach.

Callbacki – Pierwszy Krok w Asynchroniczności

Co to jest callback?

Callback to funkcja, którą przekazujemy jako argument do innej funkcji, aby została wywołana po zakończeniu pewnej operacji.

To podstawowy sposób na obsługiwanie asynchronicznego kodu w JavaScript.

Przykład z callbackiem

javascript
function fetchData(callback) {
  setTimeout(() => {
    const data = "Data has been fetched";
    callback(data);
  }, 2000);
}

fetchData((result) => {
  console.log(result);
});

W tym przykładzie funkcja fetchData symuluje asynchroniczną operację pobierania danych, która trwa 2 sekundy. Po jej zakończeniu wywołuje przekazany callback z wynikiem.


Callback Hell – Gdy Callbacki Przestają Wystarczać

Gdy zaczynamy zagnieżdżać wiele callbacków, możemy napotkać problem znany jako callback hell.

Kod staje się trudny do zrozumienia i utrzymania.

Przykład Callback Hell

javascript
fetchData((data) => {
  processData(data, (processedData) => {
    saveData(processedData, () => {
      console.log("Operation completed");
    });
  });
});

Takie zagnieżdżenie prowadzi do "piramidy zagłady", gdzie kod jest przesunięty coraz dalej w prawo, stając się nieczytelnym.


Promise w JavaScript – Nowoczesne podejście do asynchroniczności

Aby rozwiązać problem callback hell, w ES6 wprowadzono Promise.

Promise to obiekt reprezentujący przyszły wynik operacji asynchronicznej. Umożliwia pisanie bardziej czytelnego i łatwiejszego w utrzymaniu kodu.

Tworzenie obiektu Promise

javascript
const promise = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve("Operation successful");
  } else {
    reject("Operation failed");
  }
});

Obiekt Promise może być w jednym z trzech stanów:

  • Pending (oczekujący)
  • Fulfilled (zrealizowany)
  • Rejected (odrzucony)

Obsługiwanie Obiektów Promise za Pomocą then() i catch()

Aby obsłużyć wynik Promise, używamy metod then() i catch().

Przykład obsługi Promise

javascript
promise
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.error(error);
  });
  • then() jest wywoływane, gdy Promise zostanie zrealizowany.
  • catch() jest wywoływane, gdy Promise zostanie odrzucony.

Async/Await – Nowy Sposób na Asynchroniczny Kod

Async/Await to nowoczesny sposób na pisanie asynchronicznego kodu, który wygląda jak kod synchroniczny.

Ułatwia to czytanie i pisanie kodu, eliminując potrzebę używania wielu then().

Funkcja asynchroniczna

Aby użyć await, funkcja musi być oznaczona jako async.

Przykład z async/await

javascript
async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Error:", error);
  }
}

fetchData();

W tym przykładzie funkcja asynchroniczna fetchData pobiera dane z API i wyświetla je.

Obsługa Błędów w Async/Await

W przypadku async/await, obsługę błędów realizujemy za pomocą bloku try...catch.

Przykład obsługi błędów

javascript
async function processData() {
  try {
    const data = await fetchDataFromServer();
    // Process data
  } catch (error) {
    console.error("An error occurred:", error);
  }
}

Jeśli operacja asynchroniczna zakończy się niepowodzeniem, błąd zostanie przechwycony w bloku catch.

Event Loop – Jak JavaScript Zarządza Asynchronicznością

Aby zrozumieć, jak JavaScript wykonuje operacje asynchroniczne, musimy poznać Event Loop.

Jak działa Event Loop?

JavaScript jest językiem jednowątkowym, ale dzięki Event Loop może obsługiwać asynchroniczność.

  • Call Stack – stos wywołań funkcji.
  • Callback Queue – kolejka funkcji, które czekają na wywołanie.
  • Event Loop – mechanizm, który przenosi funkcje z Callback Queue do Call Stack, gdy jest pusty.

Dzięki temu asynchroniczny kod nie blokuje działania aplikacji.

Asynchroniczność w Node.js

Node.js to środowisko uruchomieniowe dla JavaScript, które umożliwia uruchamianie kodu poza przeglądarką.

Przykład asynchronicznego czytania pliku

javascript
const fs = require("fs");

fs.readFile("file.txt", "utf8", (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(data);
});

console.log("File reading initiated");

W tym przykładzie Node.js czyta zawartość pliku asynchronicznie, nie blokując reszty kodu.

Podsumowanie

Asynchroniczność w JavaScript to kluczowy koncept, który pozwala na tworzenie wydajnych i interaktywnych aplikacji.

Dzięki zrozumieniu callbacków, promise i async/await, możesz skutecznie obsługiwać asynchroniczny kod w swoich projektach.

Pamiętaj, że JavaScript jest jednym z najpopularniejszych języków do tworzenia stron internetowych, a znajomość asynchroniczności jest niezbędna dla każdego programisty.

authorImg

Witek Pruchnicki

Z pasją dzielę się wiedzą o programowaniu i nie tylko na różne sposoby