Learn JavaScript

async - объявление асинхронной функции в JavaScript

async в JavaScript используют, чтобы объявить асинхронную функцию. Такая функция всегда возвращает Promise, даже если внутри возвращается обычное значение.

Ключевое слово async часто используют вместе с await, но важно понимать: именно async делает функцию асинхронной и разрешает использовать внутри неё await.

Синтаксис

Чаще всего async используют так:

async function fn() {
  // код
}

Можно писать и в других формах:

const fn = async function () {
  // код
};

const arrowFn = async () => {
  // код
};

Во всех случаях результат один и тот же: функция начинает возвращать Promise.

Что возвращает async-функция

Любая функция с async всегда возвращает объект Promise.

async function fn() {
  return 42;
}

const result = fn();

console.log(result instanceof Promise); // true

Это одно из главных правил, которое нужно хорошо запомнить.

Простой пример

async function getMessage() {
  return "Привет";
}

getMessage().then((value) => {
  console.log(value); // "Привет"
});

Хотя внутри возвращается обычная строка, снаружи функция всё равно работает как промис.

Обычный return превращается в успешный Promise

Если async-функция возвращает обычное значение, JavaScript автоматически оборачивает его в успешно завершённый промис.

async function fn() {
  return "ok";
}

fn().then((value) => {
  console.log(value); // "ok"
});

Это поведение можно мысленно сравнить с Promise.resolve(...), хотя в коде вы просто пишете return.

throw превращается в отклонённый Promise

Если внутри async-функции выбросить ошибку, результатом будет не обычное падение наружу, а rejected Promise.

async function fn() {
  throw new Error("boom");
}

fn().catch((error) => {
  console.log(error.message); // "boom"
});

Поэтому ошибки из async-функций обычно ловят через catch() или try...catch вместе с await.

Если вернуть другой Promise, итоговая функция примет его состояние

Это тоже важный нюанс.

async function fn() {
  return Promise.resolve("inner");
}

fn().then((value) => {
  console.log(value); // "inner"
});

Здесь наружу не вернётся Promise внутри Promise. Итоговая функция дождётся внутреннего результата.

async позволяет использовать await

Это одно из главных практических назначений ключевого слова.

async function loadUser() {
  const response = await fetch("/api/user");
  const user = await response.json();

  return user;
}

Без async такой код с await внутри функции писать нельзя.

async сам по себе не ждёт результат

Это очень важный момент.

async не останавливает выполнение автоматически и не делает код “ожидающим” сам по себе. За ожидание отвечает именно await.

async function fn() {
  return "ok";
}

fn();

Здесь функция асинхронная, но никакого ожидания внутри неё нет. Она просто возвращает Promise.

Код до первого await выполняется синхронно

Это нюанс, который часто упускают.

console.log("start");

async function fn() {
  console.log("inside");
  return 42;
}

fn().then((value) => {
  console.log(value); // 42
});

console.log("end");

Результат будет таким:

start
inside
end
42

То есть тело функции начинает выполняться сразу, а обработчик результата всё равно срабатывает асинхронно.

async-функция может быть и без await

Это допустимо.

async function fn() {
  return "ok";
}

Такой код корректен, но иногда выглядит лишним. Если внутри нет await и не нужен именно Promise, то async может быть не нужен.

С другой стороны, иногда async оставляют специально, чтобы функция всегда возвращала промис и имела единый интерфейс.

Где это особенно полезно

async особенно удобен, когда:

  • нужно работать с промисами в более читаемом стиле
  • внутри функции есть await
  • важно, чтобы функция всегда возвращала Promise
  • нужно удобно обрабатывать ошибки через try...catch

Пример:

async function loadData() {
  try {
    const response = await fetch("/api/data");
    const data = await response.json();

    return data;
  } catch (error) {
    console.log("Ошибка загрузки:", error);
    throw error;
  }
}

Такой стиль обычно читается проще, чем длинная цепочка из нескольких then().

Чем async отличается от обычной функции

Сравнение стоит запомнить.

Обычная функция:

function getValue() {
  return 42;
}

console.log(getValue()); // 42

async-функция:

async function getValue() {
  return 42;
}

console.log(getValue()); // Promise { 42 }

Снаружи это уже другой интерфейс: результат нужно получать через await или then().

Частые ошибки

Думать, что async-функция возвращает обычное значение

async function getNumber() {
  return 5;
}

const result = getNumber();
console.log(result); // Promise { 5 }

Снаружи это не число 5, а промис с этим значением.

Забывать await или then() при вызове

async function getUser() {
  return { name: "Анна" };
}

const user = getUser();
console.log(user.name); // undefined

Здесь user - это промис, а не готовый объект.

Думать, что throw из async-функции работает как обычный выброс наружу

async function fn() {
  throw new Error("boom");
}

fn();

Здесь появляется отклонённый промис. Если его не обработать, будет необработанная ошибка промиса.

Считать, что весь код внутри async-функции сразу становится асинхронным

async function fn() {
  console.log("step 1");
  console.log("step 2");
}

fn();

Этот код выполняется сразу и синхронно, потому что внутри нет await.

Использовать async, когда промис не нужен

async function sum(a, b) {
  return a + b;
}

Так писать можно, но если функция не должна возвращать промис и внутри нет асинхронной логики, async может только усложнить интерфейс.

Быстрая памятка

  • async объявляет асинхронную функцию
  • такая функция всегда возвращает объект Promise
  • обычный return превращается в успешный промис
  • throw превращается в отклонённый промис
  • async позволяет использовать внутри await
  • async сам по себе ничего не ждёт, за ожидание отвечает await
  • код до первого await выполняется синхронно
  • async-функцию можно вызывать через await или then()

Коротко

async в JavaScript нужен, чтобы объявить функцию, которая работает через Promise и может использовать await. Главное, что стоит запомнить: async-функция всегда возвращает промис, обычный return внутри неё становится успешным результатом, throw превращается в ошибку промиса, а само ожидание значений делает не async, а await.