Fejezetek: 1. Aszinkron 2. Böngésző API-k 3. Biztonság
1. fejezet

Aszinkron programozás

Event Loop, Callback, Promise, async/await és hibakezelési minták – a böngészőbeli JavaScript működésének alapjai.

1.1 Az Event Loop működése

JavaScript egyszálú természete

A JavaScript egyszálú (single-threaded) nyelv – egyszerre csak EGY műveletet hajt végre. Ennek ellenére képes aszinkron műveletekre (hálózati kérések, időzítők, felhasználói események) anélkül, hogy blokkolná a felhasználói felületet. Ezt az Event Loop mechanizmus teszi lehetővé.

A főbb komponensek

Call Stack
Szinkron végrehajtás
Web APIs
setTimeout, fetch, DOM
Task Queue
Macrotask-ek
Microtask Queue
Promise .then()
Event Loop
Koordinátor
KomponensFeladatPélda
Call StackSzinkron kód végrehajtása (LIFO)Függvényhívások, változó-hozzárendelés
Web APIsAszinkron műveletek kezelése a böngészőbensetTimeout, fetch, addEventListener
Macrotask QueueAszinkron callback-ek várakozásasetTimeout, setInterval, I/O
Microtask QueueMagasabb prioritású aszinkron feladatokPromise .then(), MutationObserver
Event LoopKoordinálja a Stack és a Queue-k közötti munkátFolyamatos ellenőrzés és áthelyezés

Az Event Loop algoritmusa

  1. Végrehajtja az összes szinkron kódot a Call Stack-en.
  2. Kiüríti a Microtask Queue-t (minden Promise .then, queueMicrotask).
  3. Kivesz egy feladatot a Macrotask Queue-ból és végrehajtja.
  4. Visszatér a 2. lépéshez (microtask-ek újra kiürülnek).
  5. Ha van renderelési igény, a böngésző frissíti a képernyőt.
ℹ Megjegyzés
A Microtask Queue MINDIG a Macrotask Queue előtt ürül ki. Ezért a Promise.then() hamarabb fut, mint a setTimeout(fn, 0).

Végrehajtási sorrend – szemléltető példa

console.log("1: Szinkron");

setTimeout(function() {
  console.log("4: setTimeout (Macrotask)");
}, 0);

Promise.resolve().then(function() {
  console.log("3: Promise .then (Microtask)");
});

console.log("2: Szinkron");

// Kimenet:
// 1: Szinkron
// 2: Szinkron
// 3: Promise .then (Microtask)
// 4: setTimeout (Macrotask)
✔ Tipp
A végrehajtási sorrend mindig: Szinkron → Microtask-ek (Promise) → Macrotask-ek (setTimeout). Ez az Event Loop alapszabálya.

1.2 Callback függvények

Mi az a callback?

A callback egy függvény, amelyet paraméterként adunk át egy másik függvénynek, és az a megfelelő időben meghívja. Ez volt a JavaScript első aszinkron mintája.

// Egyszerű callback: error-first pattern
function fetchData(url, callback) {
  const xhr = new XMLHttpRequest();
  xhr.open("GET", url);

  xhr.onload = function() {
    if (xhr.status === 200) {
      callback(null, JSON.parse(xhr.responseText));
    } else {
      callback(new Error("HTTP " + xhr.status));
    }
  };

  xhr.onerror = function() {
    callback(new Error("Hálózati hiba"));
  };

  xhr.send();
}

// Használat:
fetchData("/api/user", function(err, data) {
  if (err) return console.error(err);
  console.log(data.name);  // "János"
});

Callback Hell (callback pokol)

Ha több aszinkron művelet egymástól függ, a callback-ek egymásba ágyazódnak és a kód olvashatatlanná válik:

✘ Callback Hell
getUser(userId, function(err, user) {
  getOrders(user.id, function(err, orders) {
    getOrderDetails(orders[0].id, function(err, details) {
      getProduct(details.productId, function(err, product) {
        console.log(product.name);
        // 4 szint mély... és minden szinten
        // külön hibakezelés kellene!
      });
    });
  });
});

A callback hell problémái: mély beágyazás, nehéz hibakezelés (minden szinten külön if (err)), nehéz karbantartás. Ezeket oldja meg a Promise (1.3).

1.3 Promise (Ígéret)

A Promise három állapota

ÁllapotJelentésKövetkező lépés
pendingFolyamatban – még nincs eredményVárakozás
fulfilledSikeres – az eredmény elérhető.then() fut le
rejectedSikertelen – hiba történt.catch() fut le

Promise létrehozása és láncolás

const promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    const success = true;
    if (success) resolve({ name: "János" });
    else reject(new Error("Hiba történt"));
  }, 1000);
});

// Láncolás: .then() => .catch() => .finally()
promise
  .then(function(data) { console.log(data.name); })
  .catch(function(err) { console.error(err); })
  .finally(function() { console.log("Kész."); });

Callback hell => Promise lánc

✔ Promise lánc
getUser(userId)
  .then(function(user) { return getOrders(user.id); })
  .then(function(orders) { return getOrderDetails(orders[0].id); })
  .then(function(details) { return getProduct(details.productId); })
  .then(function(product) { console.log(product.name); })
  .catch(function(err) { console.error("Hiba:", err); });
// Lapos, egyetlen .catch() az összes hibára!

Segédmetódusok – párhuzamos végrehajtás

MetódusViselkedésMikor használjuk?
Promise.all()Mind kell – bármelyik reject => egész rejectPárhuzamos lekérdezések, ahol MIND szükséges
Promise.allSettled()Megvárja az összeset, eredménytől függetlenülFüggetlen műveletek, részleges siker is jó
Promise.race()Az első befejezett nyer (fulfilled VAGY rejected)Timeout implementálás
Promise.any()Az első SIKERES nyer (rejected-et kihagyja)Leggyorsabb válaszadó kiválasztása
// Promise.all – párhuzamos lekérdezés
var results = await Promise.all([
  fetch("/api/users").then(function(r) { return r.json(); }),
  fetch("/api/products").then(function(r) { return r.json(); }),
  fetch("/api/orders").then(function(r) { return r.json(); }),
]);

var users = results[0], products = results[1], orders = results[2];
// Mindhárom PÁRHUZAMOSAN fut – nem egymás után!

1.4 async / await

Szintaktikai cukor a Promise felett

Az async/await a Promise-ek szinkron KINÉZETŰ kezelését teszi lehetővé. Az async függvény mindig Promise-t ad vissza, az await megvárja a feloldódást.

async function loadDashboard() {
  var user    = await getUser(userId);
  var orders  = await getOrders(user.id);
  var details = await getOrderDetails(orders[0].id);
  var product = await getProduct(details.productId);
  console.log(product.name);
}

Hibakezelés: try...catch...finally

async function fetchUser(id) {
  try {
    var res = await fetch("/api/users/" + id);
    if (!res.ok) throw new Error("HTTP " + res.status);
    return await res.json();
  } catch (error) {
    console.error("Hiba:", error.message);
    showErrorToast(error.message);
  } finally {
    hideLoadingSpinner();
  }
}

Párhuzamos vs. szekvenciális await

✘ Szekvenciális (lassú!)
// Egymás UTÁN fut – ha mindegyik 1 mp => 3 mp össz!
var users    = await fetch("/api/users");
var products = await fetch("/api/products");
var orders   = await fetch("/api/orders");
✔ Párhuzamos (gyors!)
// PÁRHUZAMOSAN indul mind => max 1 mp!
var results = await Promise.all([
  fetch("/api/users"),
  fetch("/api/products"),
  fetch("/api/orders"),
]);
⚠ Figyelem
Ha a kérések NEM függenek egymástól, MINDIG használjuk a Promise.all()-t! A szekvenciális await feleslegesen lassítja az alkalmazást.

1.5 Hibakezelési minták összefoglalása

Három korszak – három minta

MintaSzintaxisElőnyHátrány
Callbackfunction(err, data)Egyszerű, régi böngészőkCallback hell, nehéz hibakezelés
Promise.then().catch()Láncolás, egyetlen .catch()Még mindig sok .then()
async/awaittry/catchOlvasható, szinkron kinézetES2017+ kell (ma nem gond)

Callback: error-first pattern

// XMLHttpRequest callback mintával
var xhr = new XMLHttpRequest();
xhr.open("GET", "/api/data");
xhr.onload = function() {
  if (xhr.status === 200) {
    var data = JSON.parse(xhr.responseText);
    renderData(data);
  } else {
    showError("HTTP hiba: " + xhr.status);
  }
};
xhr.onerror = function() {
  showError("Hálózati hiba");
};
xhr.send();

Promise: .catch() és .finally()

fetch("/api/data")
  .then(function(res) {
    if (!res.ok) throw new Error("HTTP " + res.status);
    return res.json();
  })
  .then(function(data) { renderData(data); })
  .catch(function(err) { showError(err.message); })
  .finally(function() { hideSpinner(); });

async/await: try...catch...finally

async function loadData() {
  try {
    var res = await fetch("/api/data");
    if (!res.ok) throw new Error("HTTP " + res.status);
    var data = await res.json();
    renderData(data);
  } catch (err) {
    showError(err.message);
  } finally {
    hideSpinner();
  }
}

Globális hibakezelés

// Nem kezelt hibák elkapása
window.addEventListener("error", function(event) {
  console.error("Globális hiba:", event.message);
  logToServer({ type: "error", message: event.message });
});

// Nem kezelt Promise rejection
window.addEventListener("unhandledrejection", function(event) {
  console.error("Nem kezelt rejection:", event.reason);
  event.preventDefault();
  logToServer({ type: "rejection", reason: event.reason });
});
✔ Összefoglalás
Új kódban mindig async/await + try/catch-et használjunk. A .then()/.catch() rövid láncoknál hasznos. A callback minta elavult – csak régebbi API-knál (pl. XMLHttpRequest) találkozunk vele.