Frontend
October 15, 2023

Работа с асинхронностью в JavaScript: Callbacks, Promises, Async/Await

Асинхронность в JavaScript

Асинхронность позволяет выполнять несколько операций одновременно, не дожидаясь завершения предыдущих. Это сильно ускоряет работу программ. Давай разберёмся, как же использовать асинхронность в JavaScript.

Callback-функции

Callback - это просто функция, которая вызывается по завершении какой-то операции. Например:

function printResult(result) {
  console.log(result); 
}

getDataFromServer(printResult);

Здесь printResult - это callback-функция. Она будет вызвана getDataFromServer, когда данные получены.

Callback-функции удобно использовать для асинхронных вызовов, но есть одна проблема. Представь цепочку таких вызовов:

step1(function(result1) {

  step2(result1, function(result2) {

    step3(result2, function(result3) {
      ...
    });

  });

});

Получается callback-ад! Трудно читать и поддерживать такой код.

Promises (Обещания)

Promise - это объект, который обещает в будущем вернуть результат асинхронной операции. Используется так:

let promise = doSomethingAsync();

promise.then(result => {
  // обработка результата 
});

Promise имеет несколько состояний:

  • Pending - обещание ещё не выполнено
  • Fulfilled - обещание выполнено успешно, есть результат
  • Rejected - обещание завершилось с ошибкой

Мы можем ловить ошибки через .catch():

promise
  .then(result => { /* обработка результата */ })
  .catch(error => { /* обработка ошибки */ });

Promises позволяют избежать callback-ада:

doStep1()
  .then(result1 => {
    return doStep2(result1);
  })
  .then(result2 => {
    return doStep3(result2);
  })
  .catch(error => {
    // обработка ошибки
  });

Гораздо чище и понятнее!

Async/Await

Async/await - это "синтаксический сахар" на базе промисов. Позволяет писать асинхронный код так, как будто он синхронный.

Чтобы сделать функцию асинхронной, используем async:

async function doSomething() {
  // ...
}

А await позволяет ДОЖДАТЬСЯ результата промиса:

async function getResult() {
  let promise = doSomethingAsync();
  
  let result = await promise; // дождёмся результата

  return result;
}

Ошибки перехватываются так:

async function process() {
  try {
    let result = await doSomething(); 
  } catch(error) {
    // ... обработка ошибки
  }
}

Async/await делает асинхронный код похожим на синхронный, что упрощает понимание логики.

Сравнение подходов

Давай сравним подходы для разных сценариев.

Для простых асинхронных вызовов удобны callback-функции. Но стоит избегать вложенных коллбеков.

Promises хороши для цепочек асинхронных операций. Позволяют избежать коллбеков и читаются линейно.

А async/await подходит для сложной асинхронной логики. Делает код похожим на синхронный и проще для понимания.

Также async/await - это синтаксический сахар для промисов. Поэтому можно комбинировать оба подхода.

Заключение

В JavaScript для асинхронности есть callback-функции, промисы и async/await. Callback-функции просты, но могут привести к коллбек-аду. Promises избавляют от вложенных коллбеков и хороши для цепочек асинхронных вызовов. А async/await делает асинхронный код похожим на синхронный. Это упрощает понимание логики. Главное - выбрать подход, который лучше всего подходит для конкретной задачи.