Przejdź do głównej zawartości

Asynchronicznosc w JavaScript

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.

Kod synchroniczny - wykonywany linia po linii, kolejna instrukcja czeka na zakonczenie poprzedniej:

console.log('1');
console.log('2');
console.log('3');
// Wynik: 1, 2, 3

Kod 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, 2

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)│
└─────────────────────┘ └─────────────────────┘

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
});
});
});

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));

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łanie
pobierzDane().then(orders => console.log(orders));
CechaCallbackPromiseAsync/Await
CzytelnoscNiskaSredniaWysoka
Obsługa błędówTrudna.catch()try/catch
LancuchowanieCallback hell.then()await
DebugowanieTrudneSrednieŁatwe
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;
}
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 };
}
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
}
// Ź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')
]);
  • 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ń