Async и await
Внедение стандарта ES2017 в JavaScript привнесло два новых оператора: async
и await
, который призваны упростить работу с промисами.
Асинхронные функции — это такие, которые объявлены с использованием ключевого слова async
// асинхронная функция delay
async function delay() {
// ...
}
// асинхронная функция как часть выражения
const wait = async function() {
// ...
}
// стрелочная запись асинхронной функции sleep
const sleep = async() => {
// ...
}
В качестве результата асинхронная функция всегда возвращает промис. Если в качестве значения вернуть не промис, то она автоматически обернёт его в успешно завершившийся промис:
const hello = async() => {
return 'Hello!';
}
const result = hello();
console.log(result); // [object Promise] { ... }
Результат будет одинаковым, если вместо 'Hello!'
вернуть Promise.resolve('Hello!')
:
const hello = async() => {
return Promise.resolve('Hello!');
}
Так как возвращаемое значение является промисом, то мы можем его обработать с помощью метода then()
:
const hello = async() => {
return 'Hello!';
}
const result = hello();
// "Hello!"
result.then(value => console.log(value));
Или упрошенный вариант:
const hello = async() => {
return 'Hello!';
}
// "Hello!"
hello().then(value => console.log(value));
Когда в асинхронной функции возникает ошибка, то она завершается с отвергнутым обещанием. В этом случае для его обработки можно использовать метод catch()
:
const someFunc = async() => {
throw new Error('Oops');
}
// Error: Oops
someFunc().catch(error => console.error(error));
Внутри асинхронных функций перед промисами можно указывать await
.
Await
await
это ключевое слово, которое используется в асинхронных функциях для того, чтобы указать, что здесь необходимо дождаться завершения промиса. Таким образом await
, указанный перед промисом запрещает интерпретатору перейти к следующей строчке кода, пока он не выполнится. Выполним последовательно (один после завершения другого) три промиса внутри асинхронной функции:
// функция, возвращающая промис
function delay(ms, str) {
return new Promise (resolve => setTimeout(() => {
resolve(str);
}, ms));
}
// асинхронная функция
async function message() {
// ждём выполнение первого промиса delay(1000, 'Игорь') и сохраняем его результат в переменную a
let a = await delay(1000, 'Игорь');
// после завершения первого промиса, переходим к выполнению второго delay(2000, 'Егор')
let b = await delay(2000, 'Егор'); // как только он завершится помещаем его результат в переменную b
// после завершения второго промиса, переходим к выполнению третьего delay(4000, 'Денис')
let c = await delay(4000, 'Денис'); // как только он выполнится сохраняем его результат в переменную c
// выводим значения переменных в консоль
console.log(`${a} ${b} ${c}`); // "Игорь Егор Денис"
}
// вызываем асинхронную функцию
message();
Не смотря на то, что await
заставляет интерпретатор остановиться и дождаться завершения промиса, движок JavaScript в это время может выполнять другие задачи. Это достигается благодаря тому, что асинхронный код выполняется в фоне и не блокирует основной поток. Таким образом, await
не приводит к «зависанию» страницы и не мешает пользователю взаимодействовать с ней.
Обработка ошибок
Обработать потенциальные ошибки в асинхронной функции можно с помощью try..catch
. Для этого этот блок кода (в котором используется await
) необходимо заключить в эту конструкцию:
async function getUser() {
try {
const response = await fetch(url);
const data = await response.json();
} catch(e) {
// если что-то пойдёт не так на каком-то этапе в блоке try, то мы автоматически попадём в метод catch()
console.error(e);
}
}
Если нужно с finally
, то так:
async function getUser() {
try {
const response = await fetch(url);
const data = await response.json();
} catch(e) {
// если что-то пойдёт не так на каком-то этапе в блоке try, то мы автоматически попадём в метод catch()
console.error(e);
} finally {
// выполнится в любом случае, в независимости от того произошла ошибка или нет
}
}
Пример кода
Пример, в котором напишем асинхронный код для получения данных с сервера JSONPlaceholder:
// асинхронная функция для получения данных пользователя в формате JSON
const getUser = async(id) => {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
if (!response.ok) {
throw new Error('Ответ сервера не в диапазоне 200-299');
}
const data = await response.json();
return data;
} catch (e) {
throw new Error('Ошибка при получении данных пользователя');
}
}
// функция для отображения данных пользователя на странице
const renderUsers = (users) => { ... }
// асинхронная функция, в которой сначала вызывается getUser(), а затем renderUsers() для отображения полученных на странице
const clickUser = async(id) => {
try {
const data = await getUser(id);
const users = Array.isArray(data) ? data : [data];
renderUsers(users);
} catch (e) {
console.error(e);
}
}