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позволяет использовать внутриawaitasyncсам по себе ничего не ждёт, за ожидание отвечаетawait- код до первого
awaitвыполняется синхронно async-функцию можно вызывать черезawaitилиthen()
Коротко
async в JavaScript нужен, чтобы объявить функцию, которая работает через Promise и может использовать await. Главное, что стоит запомнить: async-функция всегда возвращает промис, обычный return внутри неё становится успешным результатом, throw превращается в ошибку промиса, а само ожидание значений делает не async, а await.