1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
|
---
title: async function
slug: Web/JavaScript/Reference/Statements/async_function
tags:
- JavaScript
- async
- Функція
- приклад
translation_of: Web/JavaScript/Reference/Statements/async_function
---
<div>
<div>{{jsSidebar("Statements")}}</div>
<p><span class="seoSummary">Оголошення <code><strong>async function</strong></code> визначає <strong>асинхронну функцію</strong> — функцію, яка є об'єктом {{jsxref("Global_Objects/AsyncFunction","AsyncFunction")}}.</span> Асинхронні функції мають окремий від решти функцій порядок виконання, через <a href="/uk/docs/Web/JavaScript/EventLoop">цикл подій</a>, вертаючи неявний {{jsxref("Promise","проміс")}} в якості результату. Але синтаксис та структура коду, який використовує асинхронні функції, виглядають, як стандартні синхронні функції.</p>
<div class="noinclude">
<p>Ви також можете визначити асинхронну функцію за допомогою {{jsxref("Operators/async_function", "виразу async function", "", 1)}}.</p>
</div>
</div>
<div>{{EmbedInteractiveExample("pages/js/statement-async.html", "taller")}}</div>
<p class="hidden">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 <a href="https://github.com/mdn/interactive-examples">https://github.com/mdn/interactive-examples</a> and send us a pull request.</p>
<h2 id="Синтаксис">Синтаксис</h2>
<pre class="syntaxbox">async function <em>name</em>([<em>param</em>[, <em>param</em>[, ... <em>param</em>]]]) {
<em>statements</em>
}
</pre>
<h3 id="Параметри">Параметри</h3>
<dl>
<dt><code>name</code></dt>
<dd>Ім'я функції.</dd>
</dl>
<dl>
<dt><code>param</code></dt>
<dd>Ім'я аргумента, що передається у функцію.</dd>
</dl>
<dl>
<dt><code>statements</code></dt>
<dd>Інструкції, що складають тіло функції.</dd>
</dl>
<h3 id="Значення_що_повертається">Значення, що повертається</h3>
<p>Об'єкт <code><a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a></code>, який буде вирішений зі значенням, поверненим асинхронною функцією, або відхилений з винятком, не перехопленим всередині асинхронної функції.</p>
<h2 id="Опис">Опис</h2>
<p>Асинхронна функція може містити вираз {{jsxref("Operators/await", "await")}}, який призупиняє виконання функції, щоб дочекатись на вирішення об'єкта <code>Promise</code>, після чого відновлює виконання асинхронної функції та повертає вирішене значення.<br>
<br>
<strong>Ключове слово <code>await</code> працює тільки всередині асинхронних функцій.</strong> Якщо ви використаєте його поза межами тіла асинхронної функції, то отримаєте помилку <code><a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError">SyntaxError</a></code>.</p>
<p>Поки асинхронна функція призупинена, функція, що її викликала, продовжує виконання (отримавши неявний проміс, повернений асинхронною функцією).</p>
<div class="note">
<p>Метою <code>async</code>/<code>await</code> є спрощення синхронного використання промісів, а також виконання певних дій над групою промісів. Як проміси схожі на структуровані зворотні виклики, так використання <code>async</code>/<code>await</code> схоже на поєднання <a href="/uk/docs/Web/JavaScript/Guide/Iterators_and_Generators">генераторів</a> та промісів.</p>
</div>
<h2 id="Приклади">Приклади</h2>
<h3 id="Асинхронні_функції_та_порядок_виконання">Асинхронні функції та порядок виконання</h3>
<pre class="brush: js">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
</pre>
<h4 id="await_та_паралелізм"><code>await</code> та паралелізм</h4>
<p>У <code>sequentialStart</code> виконання відкладається на 2 секунди для першого <code>await</code>, а потім ще на секунду для другого <code>await</code>. Другий таймер не створюється, поки перший не завершиться, отже, код завершує виконання через 3 секунди.</p>
<p>У <code>concurrentStart</code> обидва таймери створюються і потім очікуються у <code>await</code>. Таймери виконуються конкурентно, це означає, що код завершує виконання через 2, а не через 3 секунди, тобто, як найповільніший таймер.<br>
Однак, виклики <code>await</code> все одно запускаються один за одним, це означає, що другий <code>await</code> чекатиме, поки перший не завершиться. У цьому випадку результат швидшого таймера обробляється після повільнішого.</p>
<p>Якщо ви бажаєте виконувати дві або більше робіт паралельно, ви маєте використовувати <code>await Promise.all([job1(), job2()])</code>, як це показано у прикладі <code>parallel</code>.</p>
<div class="warning">
<h4 id="asyncawait_проти_Promisethen_та_обробка_помилок"><code>async</code>/<code>await</code> проти Promise#then та обробка помилок</h4>
<p>Більшість асинхронних функцій також можна написати як звичайні функції, що використовують проміси. Однак, асинхронні функції менш каверзні, коли доходить до обробки помилок.</p>
<p>І <code>concurrentStart</code>, і <code>concurrentPromise</code> функціонально еквівалентні:</p>
<ul>
<li>У <code>concurrentStart</code>, якщо будь-який з викликів з <code>await</code>-ом зазнає невдачі, виняток буде автоматично перехоплений, виконання асинхронної функції перерветься, а помилка спливе наверх через неявний проміс, що повертається.</li>
<li>Щоб те саме сталось у випадку з Promise, функція має потурбуватись щодо повернення об'єкту <code>Promise</code>, який захопить завершення функції. У <code>concurrentPromise</code> це означає повернення проміса з <code>Promise.all([]).then()</code> через <code>return</code>. До речі, попередня версія цього прикладу забула це зробити!</li>
</ul>
<p>Однак, асинхронні функції все ж можуть ковтати помилки.<br>
Візьміть для прикладу асинхронну функцію <code>parallel</code>. Якби вона не мала <code>await</code> (чи <code>return</code>) для повернення результату виклику <code>Promise.all([])</code>, будь-яка помилка не спливала б.<br>
В той час, як приклад <code>parallelPromise</code> виглядає простішим, він взагалі не обробляє помилки! Для цього знадобилося б схожа конструкція <code>return </code><code>Promise.all([])</code>.</p>
</div>
<h3 id="Заміна_ланцюжка_промісів_на_асинхронну_функцію">Заміна ланцюжка промісів на асинхронну функцію</h3>
<p>API, який вертає {{jsxref("Promise")}}, створить ланцюжок промісів, і це розбиває функцію на багато частин. Розглянемо наступний код:</p>
<pre class="brush: js">function getProcessedData(url) {
return downloadData(url) // вертає проміс
.catch(e => {
return downloadFallbackData(url) // вертає проміс
})
.then(v => {
return processDataInWorker(v) // вертає проміс
})
}
</pre>
<p>його можна переписати єдиною асинхронною функцією наступним чином:</p>
<pre class="brush: js">async function getProcessedData(url) {
let v
try {
v = await downloadData(url)
} catch(e) {
v = await downloadFallbackData(url)
}
return processDataInWorker(v)
}
</pre>
<p>У наведеному вище прикладі немає оператора <code>await</code> після ключового слова <code>return</code>, тому що повернене значення <code>async function</code> неявно загортається у {{jsxref("Promise.resolve")}}.</p>
<div class="blockIndicator note">
<h4 id="return_await_promiseValue_проти_return_promiseValue"><code>return await promiseValue;</code> проти <code>return promiseValue;</code></h4>
<p>Неявне загортання повернених значень у {{jsxref("Promise.resolve")}} не означає, що <code>return await promiseValue;</code> є функціонально еквівалентним <code>return promiseValue;</code></p>
<p>Розглянемо наступну переробку наведеного вище коду, яка вертає null, якщо <code>processDataInWorker</code> відхиляється з помилкою:</p>
<pre class="brush: js">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
}
}
</pre>
<p>Варіант <code>return processDataInWorker(v);</code> спричинив би відхилення об'єкта {{jsxref("Promise")}}, поверненого функцією, замість вирішення його зі значенням <code>null</code>, якщо <code>processDataInWorker(v)</code> відхилено.</p>
<p>Це висвітлює тонку різницю між <code>return foo;</code> та <code>return await foo;</code> — <code>return foo;</code> негайно вертає <code>foo</code> і ніколи не викидає помилку, навіть якщо <code>foo</code> є промісом, який відхиляється. <code>return await foo;</code> <em>чекатиме</em> на вирішення чи відхилення <code>foo</code>, якщо це проміс, і викидає помилку <strong>до повернення</strong>, якщо його відхилено.</p>
</div>
<h2 id="Специфікації">Специфікації</h2>
<table class="standard-table">
<thead>
<tr>
<th scope="col">Специфікація</th>
<th scope="col">Статус</th>
<th scope="col">Коментар</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{SpecName('ESDraft', '#sec-async-function-definitions', 'async function')}}</td>
<td>{{Spec2('ESDraft')}}</td>
<td>Початкове визначення у ES2017.</td>
</tr>
<tr>
<td>{{SpecName('ES8', '#sec-async-function-definitions', 'async function')}}</td>
<td>{{Spec2('ES8')}}</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="Сумісність_з_веб-переглядачами">Сумісність з веб-переглядачами</h2>
<div>
<p>{{Compat("javascript.statements.async_function")}}</p>
</div>
<h2 id="Див._також">Див. також</h2>
<ul>
<li>{{jsxref("Operators/async_function", "вираз асинхронної функції")}}</li>
<li>Об'єкт {{jsxref("AsyncFunction")}}</li>
<li>{{jsxref("Operators/await", "await")}}</li>
<li><a href="http://innolitics.com/10x/javascript-decorators-for-promise-returning-functions/">"Decorating Async Javascript Functions" на "innolitics.com"</a></li>
</ul>
|