Въведение в асинхронния JavaScript

JavaScript е single-threaded, но Event Loop му позволява да обработва асинхронни операции ефективно. Разбирането на тези концепции е критично за всеки frontend разработчик.

JavaScript Async

Event Loop визуализация

┌─────────────────────────────────────────────────────────────────────────────┐
│                          JAVASCRIPT EVENT LOOP                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│    ┌─────────────────┐                                                      │
│    │   Call Stack    │  ◄── Изпълнява се синхронен код                     │
│    │                 │                                                      │
│    │  function()     │                                                      │
│    │  main()         │                                                      │
│    └────────┬────────┘                                                      │
│             │                                                               │
│             ▼                                                               │
│    ┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐     │
│    │  Microtask      │ ──► │  Render         │ ──► │  Macrotask      │     │
│    │  Queue          │     │  (if needed)    │     │  Queue          │     │
│    │                 │     │                 │     │                 │     │
│    │  - Promises     │     │  - Style calc   │     │  - setTimeout   │     │
│    │  - queueMicro   │     │  - Layout       │     │  - setInterval  │     │
│    │  - MutationObs  │     │  - Paint        │     │  - I/O events   │     │
│    └─────────────────┘     └─────────────────┘     └─────────────────┘     │
│                                                                             │
│    Приоритет: Microtasks > Render > Macrotasks                             │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Callbacks (старият начин)

// Callback hell
getUser(userId, (user) => {
  getOrders(user.id, (orders) => {
    getOrderDetails(orders[0].id, (details) => {
      console.log(details);
    });
  });
});

Promises

// Promise states: pending → fulfilled/rejected
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = Math.random() > 0.5;
    if (success) {
      resolve('Data loaded!');
    } else {
      reject(new Error('Failed to load'));
    }
  }, 1000);
});

// Consuming promises
promise
  .then(data => console.log(data))
  .catch(error => console.error(error))
  .finally(() => console.log('Done'));

Promise combinators

// Promise.all - всички трябва да успеят
const [users, products] = await Promise.all([
  fetch('/api/users'),
  fetch('/api/products')
]);

// Promise.allSettled - чака всички, независимо от резултата
const results = await Promise.allSettled([p1, p2, p3]);
results.forEach(r => {
  if (r.status === 'fulfilled') console.log(r.value);
  else console.error(r.reason);
});

// Promise.race - първият който завърши
const fastest = await Promise.race([p1, p2, p3]);

// Promise.any - първият който успее
const firstSuccess = await Promise.any([p1, p2, p3]);

Async/Await

async function fetchUserData(userId) {
  try {
    const user = await getUser(userId);
    const orders = await getOrders(user.id);
    const details = await getOrderDetails(orders[0].id);
    return details;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

// Паралелно изпълнение
async function fetchAllData() {
  const [users, products, orders] = await Promise.all([
    fetchUsers(),
    fetchProducts(),
    fetchOrders()
  ]);
  return { users, products, orders };
}

AbortController

const controller = new AbortController();

async function fetchWithTimeout(url, timeout = 5000) {
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, { signal: controller.signal });
    clearTimeout(timeoutId);
    return response.json();
  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error('Request timed out');
    }
    throw error;
  }
}

Error Handling Patterns

// Wrapper function
async function safeAsync(promise) {
  try {
    const data = await promise;
    return [data, null];
  } catch (error) {
    return [null, error];
  }
}

// Usage
const [user, error] = await safeAsync(fetchUser(id));
if (error) {
  handleError(error);
  return;
}
console.log(user);

Заключение

Разбирането на асинхронния JavaScript е фундаментално за модерната уеб разработка. Използвайте async/await за четим код, Promise combinators за паралелни операции и AbortController за timeout management.


Брой думи: 3,145