Эта документация связана с понятием асинхронности в JavaScript. Зачем нужен асинхронный код и как он работает в деталях описано в обзорной статье Асинхронность в JS.
Кратко
СкопированоДобавленное перед определением функции ключевое слово async
делает функцию асинхронной. Возвращаемое значение такой функции автоматически оборачивается в Promise:
async function getStarWarsMovies() { return 1}console.log(getStarWarsMovies()) // Promise { <state>: "fulfilled", <value>: 1 }
async function getStarWarsMovies() { return 1 } console.log(getStarWarsMovies()) // Promise { <state>: "fulfilled", <value>: 1 }
Асинхронные функции нужны для выполнения асинхронных операций: работой с API, базами данных, чтения файлов и т.д.
Асинхронные операции выполняются не сразу: код отправил запрос к API и ждёт, пока сервер пришлёт ответ. Ключевое слово await
используется, чтобы дождаться выполнения асинхронной операции:
async function getStarWarsMovie(id) { const response = await fetch(`https://swapi.dev/api/films/${id}/`) console.log("ответ получен", response) // *1 return response.json()}const movies = getStarWarsMovie(1).then((movie) => { console.log(movie.title)}) // *2console.log("результат вызова функции", movies) // *3
async function getStarWarsMovie(id) { const response = await fetch(`https://swapi.dev/api/films/${id}/`) console.log("ответ получен", response) // *1 return response.json() } const movies = getStarWarsMovie(1).then((movie) => { console.log(movie.title) }) // *2 console.log("результат вызова функции", movies) // *3
Движок JavaScript при этом не блокируется и может выполнять другой код. Как только ответ получен, выполнение кода продолжается.
Вывод на экран будет следующий:
"результат вызова функции" Promise // вызвали функцию, она начала выполнять асинхронную операцию и вернула Promise (*3)"ответ получен" Response // получили ответ API, продолжаем выполнение функции (*1)"A New Hope" // сработал callback (*2)
"результат вызова функции" Promise // вызвали функцию, она начала выполнять асинхронную операцию и вернула Promise (*3) "ответ получен" Response // получили ответ API, продолжаем выполнение функции (*1) "A New Hope" // сработал callback (*2)
Как понять
СкопированоКлючевые слова async
не привносят в JavaScript что-то новое. Они только упрощают работу с Promise.
Вместо кода с цепочкой вызовов:
function getMainActorProfileFromMovie(id) { return fetch(`https://swapi.dev/api/films/${id}/`) .then((movieResponse) => { return movieResponse.json() }) .then((movie) => { const characterUrl = movie.characters[0].split("//")[1] return fetch(`https://${characterUrl}`) }) .then((characterResponse) => { return characterResponse.json() }) .catch((err) => { console.error("Произошла ошибка!", err) })}getMainActorProfileFromMovie(1).then((profile) => { console.log(profile)})
function getMainActorProfileFromMovie(id) { return fetch(`https://swapi.dev/api/films/${id}/`) .then((movieResponse) => { return movieResponse.json() }) .then((movie) => { const characterUrl = movie.characters[0].split("//")[1] return fetch(`https://${characterUrl}`) }) .then((characterResponse) => { return characterResponse.json() }) .catch((err) => { console.error("Произошла ошибка!", err) }) } getMainActorProfileFromMovie(1).then((profile) => { console.log(profile) })
Можно записать с async
:
async function getMainActorProfileFromMovie(id) { try { const movieResponse = await fetch(`https://swapi.dev/api/films/${id}/`); const movie = await movieResponse.json(); const characterUrl = movie.characters[0].split('//')[1]; const characterResponse = await fetch(`https://${characterUrl}`); return characterResponse.json(); } catch (err) { console.error('Произошла ошибка!', err); }}getMainActorProfileFromMovie(1).then((profile) => {console.log(profile)});
async function getMainActorProfileFromMovie(id) { try { const movieResponse = await fetch(`https://swapi.dev/api/films/${id}/`); const movie = await movieResponse.json(); const characterUrl = movie.characters[0].split('//')[1]; const characterResponse = await fetch(`https://${characterUrl}`); return characterResponse.json(); } catch (err) { console.error('Произошла ошибка!', err); } } getMainActorProfileFromMovie(1).then((profile) => {console.log(profile)});
Такой код проще понимать:
- он плоский;
- выглядит, как синхронный;
- использует стандартный try...catch блок для обработки ошибок.
☝️ Ключевое слово await
может использоваться не только внутри асинхронных функций, но и в модулях.
Подробнее об использовании `await` в модулях (Top level await)
Допустим у нас есть модуль Parent
импортирующий данные из модулей Child
:
// Parent.mjsimport {data} from './Child.mjs'console.log('Parent:', data)
// Parent.mjs import {data} from './Child.mjs' console.log('Parent:', data)
Модуль Child
экспортирует данные, полученные асинхронно:
// Child.mjs// пример асинхронной функции возвращающий Promiseconst promise = fetch('https://dummyjson.com/products/1').then(res => res.json());export const data = await promise
// Child.mjs // пример асинхронной функции возвращающий Promise const promise = fetch('https://dummyjson.com/products/1').then(res => res.json()); export const data = await promise
При запуске Parent
будет ожидать завершения асинхронной операции.
💡 Возможность использовать await
вне асинхронной функции в модулях появилась в стандарте ES2022.
Попытка использовать await
вне модуля и не в асинхронной функции приведёт к синтаксической ошибке:
SyntaxError
function getMainActorProfileFromMovie(id) { // код примера выше}await getMainActorProfileFromMovie(1)
function getMainActorProfileFromMovie(id) { // код примера выше } await getMainActorProfileFromMovie(1)
На практике
Скопированосоветует Скопировано
🛠 Всегда используйте async
вместо цепочек then
и колбэков.
Этот подход проще читается, легче отлаживается, пользуется стандартными способами обработки ошибок.
🛠 await
нельзя использовать вне асинхронных функций. Если нужно выполнить асинхронную операцию в глобальной области видимости, придётся воспользоваться then
.
советует Скопировано
🛠 Используйте await
для параллельного выполнения нескольких независимых асинхронных функций.
Используя async
, мы делаем наш код последовательным: ожидаем выполнения одной асинхронной функции и лишь после запускаем другую. В примере ниже новости будут запрошены только после получения пользователя:
async function getUser(){ // Возвращает информацию о пользователе}async function getNews(){ // Возвращает список новостей}const user = await getUser()const news = await getNews()
async function getUser(){ // Возвращает информацию о пользователе } async function getNews(){ // Возвращает список новостей } const user = await getUser() const news = await getNews()
Но, запустив get
параллельно c get
, мы в большинстве случаев получим результат быстрее. Promise
позволяет запустить запросы параллельно, при этом дожидаться результата мы можем как и раньше при помощи await
:
const [user, news] = await Promise.all([ getUser(), getNews()])
const [user, news] = await Promise.all([ getUser(), getNews() ])
🛠 Не смешивайте синтаксис async
и Promise
, старайтесь применять один подход на проекте: так код легче читать и поддерживать.