Asynchronicznosc w JavaScript
Wprowadzenie
Dział zatytułowany „Wprowadzenie”JavaScript jest językiem jednowatkowym - może wykonywac tylko jedna operacje na raz. Jednak aplikacje webowe musza obsługiwać wiele żądań jednoczesnie: pobieranie danych z serwera, animacje, reakcje na klikniecia użytkownika.
Asynchronicznosc to mechanizm, który pozwala JavaScriptowi wykonywac długotrwałe operacje bez blokowania głównego watku. Dzieki temu interfejs użytkownika pozostaje responsywny.
Kluczowe pojecia
Dział zatytułowany „Kluczowe pojecia”Synchronicznosc vs Asynchronicznosc
Dział zatytułowany „Synchronicznosc vs Asynchronicznosc”Kod synchroniczny - wykonywany linia po linii, kolejna instrukcja czeka na zakonczenie poprzedniej:
console.log('1');console.log('2');console.log('3');// Wynik: 1, 2, 3Kod asynchroniczny - niektore operacje wykonuja się “w tle”, a ich wynik jest obsługiwany później:
console.log('1');setTimeout(() => console.log('2'), 0);console.log('3');// Wynik: 1, 3, 2Event Loop (petla zdarzen)
Dział zatytułowany „Event Loop (petla zdarzen)”Event Loop to mechanizm zarzadzajacy wykonywaniem kodu asynchronicznego:
┌─────────────────────────────────────────────────────────┐│ CALL STACK ││ (stos wywołań - kod synchroniczny) │└───────────────────────────┬─────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────┐│ EVENT LOOP ││ (sprawdza czy stos jest pusty) │└───────────────────────────┬─────────────────────────────┘ │ ┌─────────────────┴─────────────────┐ ▼ ▼┌─────────────────────┐ ┌─────────────────────┐│ MICROTASK QUEUE │ │ CALLBACK QUEUE ││ (Promise, then) │ │ (setTimeout, events)│└─────────────────────┘ └─────────────────────┘Ewolucja asynchronicznosci w JS
Dział zatytułowany „Ewolucja asynchronicznosci w JS”1. Callbacks (najstarsza metoda)
Dział zatytułowany „1. Callbacks (najstarsza metoda)”Callback to funkcja przekazywana jako argument do innej funkcji:
function pobierzDane(callback) { setTimeout(() => { const dane = { imie: 'Jan', wiek: 25 }; callback(dane); }, 1000);}
pobierzDane(function(wynik) { console.log(wynik.imie); // Jan});Problem: Callback Hell - zagniezdzanie wielu callbackow:
pobierzUzytkownika(id, function(user) { pobierzZamowienia(user.id, function(orders) { pobierzSzczegoly(orders[0].id, function(details) { // Piramida kodu - trudna do czytania i debugowania }); });});2. Promise (ES6)
Dział zatytułowany „2. Promise (ES6)”Promise to obiekt reprezentujacy przyszły wynik operacji asynchronicznej:
const obietnica = new Promise((resolve, reject) => { setTimeout(() => { const sukces = true; if (sukces) { resolve({ dane: 'OK' }); } else { reject(new Error('Błąd')); } }, 1000);});
obietnica .then(wynik => console.log(wynik.dane)) .catch(błąd => console.error(błąd.message));Stany Promise:
- pending - oczekujacy (początkowy)
- fulfilled - spełniony (resolve)
- rejected - odrzucony (reject)
Lancuchowanie Promise:
fetch('/api/user') .then(response => response.json()) .then(user => fetch(`/api/orders/${user.id}`)) .then(response => response.json()) .then(orders => console.log(orders)) .catch(error => console.error(error));3. Async/Await (ES2017)
Dział zatytułowany „3. Async/Await (ES2017)”Składnia upraszczajaca prace z Promise - kod wyglada jak synchroniczny:
async function pobierzDane() { try { const response = await fetch('/api/user'); const user = await response.json();
const ordersResponse = await fetch(`/api/orders/${user.id}`); const orders = await ordersResponse.json();
return orders; } catch (error) { console.error('Błąd:', error); }}
// WywołaniepobierzDane().then(orders => console.log(orders));Porownanie metod
Dział zatytułowany „Porownanie metod”| Cecha | Callback | Promise | Async/Await |
|---|---|---|---|
| Czytelnosc | Niska | Srednia | Wysoka |
| Obsługa błędów | Trudna | .catch() | try/catch |
| Lancuchowanie | Callback hell | .then() | await |
| Debugowanie | Trudne | Srednie | Łatwe |
Przykłady praktyczne
Dział zatytułowany „Przykłady praktyczne”Fetch API z async/await
Dział zatytułowany „Fetch API z async/await”async function pobierzUzytkownikow() { const response = await fetch('https://api.example.com/users');
if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); }
const users = await response.json(); return users;}Rownolegle wykonywanie (Promise.all)
Dział zatytułowany „Rownolegle wykonywanie (Promise.all)”async function pobierzWszystko() { const [users, products, orders] = await Promise.all([ fetch('/api/users').then(r => r.json()), fetch('/api/products').then(r => r.json()), fetch('/api/orders').then(r => r.json()) ]);
return { users, products, orders };}Obsługa timeout
Dział zatytułowany „Obsługa timeout”function fetchWithTimeout(url, timeout = 5000) { return Promise.race([ fetch(url), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout) ) ]);}Najczestsze błędy
Dział zatytułowany „Najczestsze błędy”// Źle - sekwencyjnie (wolniej)const a = await fetch('/api/a');const b = await fetch('/api/b');
// Dobrze - rownolegle (szybciej)const [a, b] = await Promise.all([ fetch('/api/a'), fetch('/api/b')]);Podsumowanie
Dział zatytułowany „Podsumowanie”- JavaScript jest jednowatkowy, ale asynchroniczny
- Event Loop zarzadza kolejka żądań asynchronicznych
- Callbacks sa przestarzałe - prowadza do callback hell
- Promise reprezentuje przyszły wynik operacji
- Async/await to najczytelniejsza składnia dla kodu asynchronicznego
- Promise.all pozwala na rownolegle wykonywanie żądań