--- title: async function slug: Web/JavaScript/Reference/Statements/async_function tags: - JavaScript - async - Функція - приклад translation_of: Web/JavaScript/Reference/Statements/async_function ---
Оголошення async function
визначає асинхронну функцію — функцію, яка є об'єктом {{jsxref("Global_Objects/AsyncFunction","AsyncFunction")}}. Асинхронні функції мають окремий від решти функцій порядок виконання, через цикл подій, вертаючи неявний {{jsxref("Promise","проміс")}} в якості результату. Але синтаксис та структура коду, який використовує асинхронні функції, виглядають, як стандартні синхронні функції.
Ви також можете визначити асинхронну функцію за допомогою {{jsxref("Operators/async_function", "виразу async function", "", 1)}}.
The source for this interactive demo is stored in a GitHub repository. If you'd like to contribute to the interactive demo project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
async function name([param[, param[, ... param]]]) { statements }
name
param
statements
Об'єкт Promise
, який буде вирішений зі значенням, поверненим асинхронною функцією, або відхилений з винятком, не перехопленим всередині асинхронної функції.
Асинхронна функція може містити вираз {{jsxref("Operators/await", "await")}}, який призупиняє виконання функції, щоб дочекатись на вирішення об'єкта Promise
, після чого відновлює виконання асинхронної функції та повертає вирішене значення.
Ключове слово await
працює тільки всередині асинхронних функцій. Якщо ви використаєте його поза межами тіла асинхронної функції, то отримаєте помилку SyntaxError
.
Поки асинхронна функція призупинена, функція, що її викликала, продовжує виконання (отримавши неявний проміс, повернений асинхронною функцією).
Метою async
/await
є спрощення синхронного використання промісів, а також виконання певних дій над групою промісів. Як проміси схожі на структуровані зворотні виклики, так використання async
/await
схоже на поєднання генераторів та промісів.
function resolveAfter2Seconds() { console.log("починається повільний проміс") return new Promise(resolve => { setTimeout(function() { resolve("повільний") console.log("повільний проміс завершено") }, 2000); }); } function resolveAfter1Second() { console.log("починається швидкий проміс") return new Promise(resolve => { setTimeout(function() { resolve("швидкий") console.log("швидкий проміс завершено") }, 1000); }); } async function sequentialStart() { console.log('==ПОСЛІДОВНИЙ СТАРТ==') // 1. Виконання доходить сюди майже миттєво const slow = await resolveAfter2Seconds() console.log(slow) // 2. це виконується 2 секунди після 1. const fast = await resolveAfter1Second() console.log(fast) // 3. це виконується 3 секунди після 1. } async function concurrentStart() { console.log('==КОНКУРЕНТНИЙ СТАРТ з await==') const slow = resolveAfter2Seconds() // запускає таймер негайно const fast = resolveAfter1Second() // запускає таймер негайно // 1. Виконання доходить сюди майже миттєво console.log(await slow) // 2. це виконується 2 секунди після 1. console.log(await fast) // 3. це виконується 2 секунди після 1., одразу після 2., оскільки швидкий вже вирішений } function concurrentPromise() { console.log('==КОНКУРЕНТНИЙ СТАРТ з Promise.all==') return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then((messages) => { console.log(messages[0]) // повільний console.log(messages[1]) // швидкий }); } async function parallel() { console.log('==ПАРАЛЕЛЬНИЙ з await Promise.all==') // Починає 2 "роботи" паралельно та чекає, поки обидві не завершаться await Promise.all([ (async()=>console.log(await resolveAfter2Seconds()))(), (async()=>console.log(await resolveAfter1Second()))() ]) } // Ця функція не обробляє помилки. Дивіться застереження нижче! function parallelPromise() { console.log('==ПАРАЛЕЛЬНИЙ з Promise.then==') resolveAfter2Seconds().then((message)=>console.log(message)) resolveAfter1Second().then((message)=>console.log(message)) } sequentialStart() // через 2 секунди виводить "повільний", далі через ще 1 секунду "швидкий" // чекає, поки попередній завершиться setTimeout(concurrentStart, 4000) // через 2 секунди виводить "повільний", а потім "швидкий" // знову чекає setTimeout(concurrentPromise, 7000) // такий самий, як і concurrentStart // знову чекає setTimeout(parallel, 10000) // справді паралельний: через 1 секунду виводить "швидкий", потім ще через 1 секунду "повільний" // знову чекає setTimeout(parallelPromise, 13000) // такий самий, як і parallel
await
та паралелізмУ sequentialStart
виконання відкладається на 2 секунди для першого await
, а потім ще на секунду для другого await
. Другий таймер не створюється, поки перший не завершиться, отже, код завершує виконання через 3 секунди.
У concurrentStart
обидва таймери створюються і потім очікуються у await
. Таймери виконуються конкурентно, це означає, що код завершує виконання через 2, а не через 3 секунди, тобто, як найповільніший таймер.
Однак, виклики await
все одно запускаються один за одним, це означає, що другий await
чекатиме, поки перший не завершиться. У цьому випадку результат швидшого таймера обробляється після повільнішого.
Якщо ви бажаєте виконувати дві або більше робіт паралельно, ви маєте використовувати await Promise.all([job1(), job2()])
, як це показано у прикладі parallel
.
async
/await
проти Promise#then та обробка помилокБільшість асинхронних функцій також можна написати як звичайні функції, що використовують проміси. Однак, асинхронні функції менш каверзні, коли доходить до обробки помилок.
І concurrentStart
, і concurrentPromise
функціонально еквівалентні:
concurrentStart
, якщо будь-який з викликів з await
-ом зазнає невдачі, виняток буде автоматично перехоплений, виконання асинхронної функції перерветься, а помилка спливе наверх через неявний проміс, що повертається.Promise
, який захопить завершення функції. У concurrentPromise
це означає повернення проміса з Promise.all([]).then()
через return
. До речі, попередня версія цього прикладу забула це зробити!Однак, асинхронні функції все ж можуть ковтати помилки.
Візьміть для прикладу асинхронну функцію parallel
. Якби вона не мала await
(чи return
) для повернення результату виклику Promise.all([])
, будь-яка помилка не спливала б.
В той час, як приклад parallelPromise
виглядає простішим, він взагалі не обробляє помилки! Для цього знадобилося б схожа конструкція return
Promise.all([])
.
API, який вертає {{jsxref("Promise")}}, створить ланцюжок промісів, і це розбиває функцію на багато частин. Розглянемо наступний код:
function getProcessedData(url) { return downloadData(url) // вертає проміс .catch(e => { return downloadFallbackData(url) // вертає проміс }) .then(v => { return processDataInWorker(v) // вертає проміс }) }
його можна переписати єдиною асинхронною функцією наступним чином:
async function getProcessedData(url) { let v try { v = await downloadData(url) } catch(e) { v = await downloadFallbackData(url) } return processDataInWorker(v) }
У наведеному вище прикладі немає оператора await
після ключового слова return
, тому що повернене значення async function
неявно загортається у {{jsxref("Promise.resolve")}}.
return await promiseValue;
проти return promiseValue;
Неявне загортання повернених значень у {{jsxref("Promise.resolve")}} не означає, що return await promiseValue;
є функціонально еквівалентним return promiseValue;
Розглянемо наступну переробку наведеного вище коду, яка вертає null, якщо processDataInWorker
відхиляється з помилкою:
async function getProcessedData(url) { let v; try { v = await downloadData(url) } catch(e) { v = await downloadFallbackData(url) } try { return await processDataInWorker(v) // Зауважте `return await` у порівнянні з `return` } catch (e) { return null } }
Варіант return processDataInWorker(v);
спричинив би відхилення об'єкта {{jsxref("Promise")}}, поверненого функцією, замість вирішення його зі значенням null
, якщо processDataInWorker(v)
відхилено.
Це висвітлює тонку різницю між return foo;
та return await foo;
— return foo;
негайно вертає foo
і ніколи не викидає помилку, навіть якщо foo
є промісом, який відхиляється. return await foo;
чекатиме на вирішення чи відхилення foo
, якщо це проміс, і викидає помилку до повернення, якщо його відхилено.
Специфікація | Статус | Коментар |
---|---|---|
{{SpecName('ESDraft', '#sec-async-function-definitions', 'async function')}} | {{Spec2('ESDraft')}} | Початкове визначення у ES2017. |
{{SpecName('ES8', '#sec-async-function-definitions', 'async function')}} | {{Spec2('ES8')}} |
{{Compat("javascript.statements.async_function")}}