async и await
В цепочке промисов каждый шаг живёт в своём колбэке, и код читается не сверху вниз. Слова async и await позволяют писать ожидание как обычные строки.
Снова представьте повара. В рецепте хлеба есть шаг «дождаться, пока подойдёт тесто». Повар не стоит над миской час: он кладёт закладку в рецепт и готовит другие заказы. Когда тесто подошло, он открывает рецепт на закладке и продолжает со следующего шага. Давайте посмотрим, как это выглядит в коде:
function wait(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
async function main() {
const name = await wait(100, "Ann");
console.log(name);
}
main();
console.log("after main");
// after main
// AnnРазберём пример по частям:
- Функция
waitвозвращает промис, который черезmsмиллисекунд выполнится со значениемvalue. - Внутри
mainстрока сawaitждёт этого и кладёт"Ann"в константуname. - Следующая строка
console.log(name)выполняется только потом и печатаетAnn.
Поэтому код внутри main читается сверху вниз, как без ожидания. В одной функции await можно писать много раз: каждая такая строка ждёт свой промис по очереди.
Функция с async
Слово async перед function делает функцию асинхронной. Такая функция всегда возвращает промис, а значение после return становится значением этого промиса:
async function one() {
return 1;
}
const p = one();
console.log(typeof p); // object
console.log(p instanceof Promise); // true
p.then((v) => console.log(v)); // 1Что здесь происходит:
one()возвращает промис, а не число1;- для промиса, как для массива,
typeofдаёт"object"; - отличить промис от других объектов помогает
instanceof Promise; - само число
1достают черезthenили черезawait.
На собеседовании любят спрашивать, что вернёт вызов async-функции. Ответ: промис, даже если внутри написано return 1.
Что делает await
await ставит на паузу только свою async-функцию, пока промис справа от него не перестанет быть pending. Остальная программа в это время работает дальше. Это и есть закладка в рецепте: повар отложил один рецепт, а не всю кухню.
Поэтому в первом примере after main печатается раньше Ann. Когда main дошла до await, вызов main() вернул промис, и выполнилась следующая строка.
Даже если промис уже выполнен, продолжение функции ждёт, как колбэк then, пока закончится текущий код.
Здесь аналогия с поваром перестаёт работать. Повар может забыть, на чём остановился, а функция продолжает ровно со строки после await, и все её переменные на месте.
Отказ и try…catch
Если промис выполнен, await даёт его значение. Если промис отклонён, await бросает его причину, как throw. Такую ошибку ловят обычным try…catch:
async function load() {
try {
await Promise.reject(new Error("no network"));
console.log("loaded");
} catch (e) {
console.log(e.message);
}
}
load();
// no networkPromise.reject(reason) создаёт уже отклонённый промис. Строка loaded пропущена: после броска выполнение переходит в catch.
Если ошибку внутри async-функции не поймать, функция не падает на месте. Промис, который она вернула, становится rejected с этой ошибкой, и поймать её можно снаружи: load().catch((e) => …). Внутри функции try…catch обычно удобнее: обработка стоит рядом со строкой, которая ждёт.
Поэтому try…catch вокруг вызова без await такую ошибку не поймает. Сам вызов ничего не бросает: он сразу возвращает промис, а отказ этого промиса видят только catch и await.
Частая ошибка
await написали в функции без async:
function show() {
const name = await wait(100, "Ann"); // ✗
console.log(name);
}SyntaxError: await is only valid in async functions and the top level bodies of modulesЭто SyntaxError, поэтому файл не запускается совсем, даже если show нигде не вызвана. Конец сообщения говорит о модулях — файлах с import и export. В курсе их нет, а в обычном файле await работает только внутри async-функции. Добавьте async перед function.
Главное
async-функция всегда возвращает промис, а значение послеreturnстановится его значением.awaitставит на паузу только свою функцию, пока промис не перестанет бытьpending; остальная программа идёт дальше.- Если промис отклонён,
awaitбросает его причину, и её ловятtry…catch. awaitработает только внутриasync-функции.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.