Эта документация связана с понятием асинхронности в JavaScript. Зачем нужен асинхронный код и как он работает в деталях описано в обзорной статье «Асинхронность в JavaScript».
Кратко
СкопированоДобавленное перед определением функции ключевое слово 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) // *2})console.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 при этом не блокируется и может выполнять другой код. Как только ответ получен, выполнение кода продолжается.
Вывод на экран будет следующий:
// Вызвали функцию, она начала выполнять// асинхронную операцию и вернула промис (*3)'результат вызова функции' Promise// Получили ответ API, продолжаем// выполнение функции (*1)'ответ получен' Response// Сработал колбэк (*2)'A New Hope'
// Вызвали функцию, она начала выполнять
// асинхронную операцию и вернула промис (*3)
'результат вызова функции' Promise
// Получили ответ API, продолжаем
// выполнение функции (*1)
'ответ получен' Response
// Сработал колбэк (*2)
'A New Hope'
Как понять
СкопированоКлючевые слова async не привносят в JavaScript что-то новое. Они только упрощают работу с промисами.
Вместо кода с цепочкой вызовов:
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)
Определение данных в дочернем модуле с использованием 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, старайтесь применять один подход на проекте: так код легче читать и поддерживать.