Перейти к основному содержимому

async-await

async и await​

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

async function

async​

return превращает значение в успешный промис. throw отклоняет промис этой функции. Если ничего не вернуть, промис завершится с undefined.

const load = async () => {
return 1;
};

load().then(value => {
console.log(value); // 1
});

await​

Слева от await оказывается уже результат промиса, а не сам промис. Если справа не промис, выполнение просто продолжается с этим значением.

await

const loadPlanet = async () => {
const res = await fetch('https://swapi.dev/api/planets/1');

if (!res.ok) {
throw new Error(String(res.status));
}

return res.json();
};

loadPlanet().then(data => console.log(data));

Отклонённый промис на await выбрасывает ошибку. Её ловит try/catch вокруг await. Если такого блока нет, промис самой async-функции отклоняется.

Где можно писать await​

Внутри async-функции и на верхнем уровне ES-модуля. В обычном скрипте на верхнем уровне будет SyntaxError. Ниже два отдельных вызова одной и той же функции, а не один общий скрипт.

const loadPlanet = async () => {
const res = await fetch('https://swapi.dev/api/planets/1');

if (!res.ok) {
throw new Error(String(res.status));
}

return res.json();
};

// обычный скрипт
loadPlanet().then(res => console.log('res', res));
// модульный скрипт
const res = await loadPlanet();

console.log('res', res);

В браузере вторая запись работает в <script type="module">. В среде, где запускается фрагмент кода, для неё включают top-level await в advanced settings.

Несколько запросов​

Запросы через несколько await подряд идут по очереди: второй начнётся после первого.

const load = async () => {
const planet = await loadPlanet();
const posts = await loadPosts();

return { planet, posts };
};

Promise.all запускает их одновременно и ждёт все. Если один отклонится, весь Promise.all отклонится.

Promise.all()

const load = async () => {
const [planet, posts] = await Promise.all([loadPlanet(), loadPosts()]);

return { planet, posts };
};

Promise.allSettled дожидается всех, даже если часть завершилась ошибкой. У каждого элемента будет status: fulfilled или rejected.

Promise.allSettled()

Цикл​

for...of останавливается на await. forEach так не умеет: он сразу запускает все колбэки, а строка после него выполняется, не дожидаясь ответов.

const ids = [1, 2, 3];

const loadPlanet = async id => {
const res = await fetch(`https://swapi.dev/api/planets/${id}`);

return res.json();
};

ids.forEach(async id => {
const planet = await loadPlanet(id);
console.log(planet);
});

console.log('готово'); // раньше планет

Последовательная загрузка идёт через for...of. Параллельная собирает массив промисов через map и ждёт его в Promise.all.

const loadInOrder = async () => {
for (const id of ids) {
const planet = await loadPlanet(id);
console.log(planet);
}
};

const loadTogether = async () => {
const planets = await Promise.all(ids.map(id => loadPlanet(id)));

console.log(planets);
};