Середнійpromisesasyncawaitasynchronouscallbacks

Як працюють Promise та async/await в JavaScript?

Детальний розбір асинхронності в JavaScript: від колбеків до async/await

Як працюють Promise та async/await в JavaScript?

Асинхронність - це ключовий аспект JavaScript, який дозволяє виконувати операції без блокування основного потоку виконання.

Еволюція асинхронності

1. Колбеки (Callbacks)

Перший спосіб роботи з асинхронним кодом:

function fetchData(callback) {
  setTimeout(() => {
    callback(null, 'Дані отримано');
  }, 1000);
}

fetchData((error, data) => {
  if (error) {
    console.error(error);
  } else {
    console.log(data);
  }
});

Проблема: Callback Hell при вкладених операціях:

fetchUser(userId, (error, user) => {
  if (error) return handleError(error);
  
  fetchPosts(user.id, (error, posts) => {
    if (error) return handleError(error);
    
    fetchComments(posts[0].id, (error, comments) => {
      if (error) return handleError(error);
      // Вкладеність стає неконтрольованою
    });
  });
});

2. Promise

Promise вирішує проблему callback hell:

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Дані отримано');
    }, 1000);
  });
}

fetchData()
  .then(data => console.log(data))
  .catch(error => console.error(error));

Створення Promise:

const myPromise = new Promise((resolve, reject) => {
  const success = Math.random() > 0.5;
  
  setTimeout(() => {
    if (success) {
      resolve('Операція успішна');
    } else {
      reject(new Error('Операція провалена'));
    }
  }, 1000);
});

Ланцюжки Promise:

fetchUser(userId)
  .then(user => fetchPosts(user.id))
  .then(posts => fetchComments(posts[0].id))
  .then(comments => {
    console.log('Всі дані отримано:', comments);
  })
  .catch(error => {
    console.error('Помилка:', error);
  });

Async/Await

Синтаксичний цукор над Promise, який робить асинхронний код схожим на синхронний:

Основний синтаксис

async function fetchUserData(userId) {
  try {
    const user = await fetchUser(userId);
    const posts = await fetchPosts(user.id);
    const comments = await fetchComments(posts[0].id);
    
    return { user, posts, comments };
  } catch (error) {
    console.error('Помилка при отриманні даних:', error);
    throw error;
  }
}

Паралельне виконання

// Послідовно (повільно)
async function getDataSequential() {
  const user = await fetchUser(1);     // 1 секунда
  const posts = await fetchPosts(1);   // 1 секунда  
  // Загалом: 2 секунди
}

// Паралельно (швидко)
async function getDataParallel() {
  const [user, posts] = await Promise.all([
    fetchUser(1),
    fetchPosts(1)
  ]);
  // Загалом: 1 секунда
}

Методи Promise

Promise.all()

Чекає завершення всіх Promise:

const promises = [
  fetch('/api/users'),
  fetch('/api/posts'),
  fetch('/api/comments')
];

Promise.all(promises)
  .then(responses => {
    // Всі запити завершені
    return Promise.all(responses.map(r => r.json()));
  })
  .then(data => {
    console.log('Всі дані:', data);
  })
  .catch(error => {
    // Якщо хоча б один запит провалився
    console.error('Помилка:', error);
  });

Promise.allSettled()

Чекає завершення всіх Promise, незалежно від результату:

const promises = [
  fetchUser(1),
  fetchUser(999), // може провалитися
  fetchUser(2)
];

Promise.allSettled(promises)
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Promise ${index}: успіх`, result.value);
      } else {
        console.log(`Promise ${index}: помилка`, result.reason);
      }
    });
  });

Promise.race()

Повертає результат першого завершеного Promise:

const timeout = new Promise((_, reject) => 
  setTimeout(() => reject(new Error('Timeout')), 5000)
);

const dataFetch = fetch('/api/data');

Promise.race([dataFetch, timeout])
  .then(response => response.json())
  .catch(error => {
    if (error.message === 'Timeout') {
      console.log('Запит занадто довгий');
    }
  });

Поширені помилки

1. Забування await

// Неправильно
async function badExample() {
  const data = fetchData(); // Повертає Promise, не дані
  console.log(data); // Promise object
}

// Правильно  
async function goodExample() {
  const data = await fetchData();
  console.log(data); // Фактичні дані
}

2. Не обробка помилок

// Неправильно
async function withoutErrorHandling() {
  const data = await fetchData(); // Може кинути помилку
  return data;
}

// Правильно
async function withErrorHandling() {
  try {
    const data = await fetchData();
    return data;
  } catch (error) {
    console.error('Помилка:', error);
    return null;
  }
}

3. Послідовне виконання замість паралельного

// Повільно
async function sequential() {
  const user1 = await fetchUser(1);
  const user2 = await fetchUser(2);
  return [user1, user2];
}

// Швидко
async function parallel() {
  const [user1, user2] = await Promise.all([
    fetchUser(1),
    fetchUser(2)
  ]);
  return [user1, user2];
}

Розуміння асинхронності критично важливе для ефективної роботи з JavaScript.