СÑÑеÑÑвÑÐµÑ ÑпеÑиалÑнÑй ÑинÑакÑÐ¸Ñ Ð´Ð»Ñ ÑабоÑÑ Ñ Ð¿ÑомиÑами, коÑоÑÑй назÑваеÑÑÑ Â«async/await». Ðн ÑдивиÑелÑно пÑоÑÑ Ð´Ð»Ñ Ð¿Ð¾Ð½Ð¸Ð¼Ð°Ð½Ð¸Ñ Ð¸ иÑполÑзованиÑ.
ÐÑÐ¸Ð½Ñ ÑоннÑе ÑÑнкÑии
ÐаÑнÑм Ñ ÐºÐ»ÑÑевого Ñлова async. Ðно ÑÑавиÑÑÑ Ð¿ÐµÑед ÑÑнкÑией, Ð²Ð¾Ñ Ñак:
async function f() {
return 1;
}
У Ñлова async один пÑоÑÑой ÑмÑÑл: ÑÑа ÑÑнкÑÐ¸Ñ Ð²Ñегда возвÑаÑÐ°ÐµÑ Ð¿ÑомиÑ. ÐнаÑÐµÐ½Ð¸Ñ Ð´ÑÑгиÑ
Ñипов обоÑаÑиваÑÑÑÑ Ð² завеÑÑивÑийÑÑ ÑÑпеÑно пÑÐ¾Ð¼Ð¸Ñ Ð°Ð²ÑомаÑиÑеÑки.
ÐапÑимеÑ, ÑÑа ÑÑнкÑÐ¸Ñ Ð²Ð¾Ð·Ð²ÑаÑÐ¸Ñ Ð²ÑполненнÑй пÑÐ¾Ð¼Ð¸Ñ Ñ ÑезÑлÑÑаÑом 1:
async function f() {
return 1;
}
f().then(alert); // 1
Ðожно и Ñвно веÑнÑÑÑ Ð¿ÑомиÑ, ÑезÑлÑÑÐ°Ñ Ð±ÑÐ´ÐµÑ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñм:
async function f() {
return Promise.resolve(1);
}
f().then(alert); // 1
Так ÑÑо клÑÑевое Ñлово async пеÑед ÑÑнкÑией гаÑанÑиÑÑеÑ, ÑÑо ÑÑа ÑÑнкÑÐ¸Ñ Ð² лÑбом ÑлÑÑае веÑнÑÑ Ð¿ÑомиÑ. СоглаÑиÑеÑÑ, доÑÑаÑоÑно пÑоÑÑо? Ðо ÑÑо еÑÑ Ð½Ðµ вÑÑ. ÐÑÑÑ Ð´ÑÑгое клÑÑевое Ñлово â await, коÑоÑое можно иÑполÑзоваÑÑ ÑолÑко внÑÑÑи async-ÑÑнкÑий.
Await
СинÑакÑиÑ:
// ÑабоÑÐ°ÐµÑ ÑолÑко внÑÑÑи asyncâÑÑнкÑий
let value = await promise;
ÐлÑÑевое Ñлово await заÑÑÐ°Ð²Ð¸Ñ Ð¸Ð½ÑеÑпÑеÑаÑÐ¾Ñ JavaScript ждаÑÑ Ð´Ð¾ ÑеÑ
поÑ, пока пÑÐ¾Ð¼Ð¸Ñ ÑпÑава Ð¾Ñ await не вÑполниÑÑÑ. ÐоÑле Ñего оно веÑнÑÑ ÐµÐ³Ð¾ ÑезÑлÑÑаÑ, и вÑполнение кода пÑодолжиÑÑÑ.
Ð ÑÑом пÑимеÑе пÑÐ¾Ð¼Ð¸Ñ ÑÑпеÑно вÑполниÑÑÑ ÑеÑез 1 ÑекÑндÑ:
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("гоÑово!"), 1000)
});
let result = await promise; // бÑÐ´ÐµÑ Ð¶Ð´Ð°ÑÑ, пока пÑÐ¾Ð¼Ð¸Ñ Ð½Ðµ вÑполниÑÑÑ (*)
alert(result); // "гоÑово!"
}
f();
Рданном пÑимеÑе вÑполнение ÑÑнкÑии оÑÑановиÑÑÑ Ð½Ð° ÑÑÑоке (*) до ÑеÑ
поÑ, пока пÑÐ¾Ð¼Ð¸Ñ Ð½Ðµ вÑполниÑÑÑ. ÐÑо пÑоизойдÑÑ ÑеÑез ÑекÑÐ½Ð´Ñ Ð¿Ð¾Ñле запÑÑка ÑÑнкÑии. ÐоÑле Ñего в пеÑеменнÑÑ result бÑÐ´ÐµÑ Ð·Ð°Ð¿Ð¸Ñан ÑезÑлÑÑÐ°Ñ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¿ÑомиÑа, и бÑаÑÐ·ÐµÑ Ð¾ÑобÑÐ°Ð·Ð¸Ñ alert-окно «гоÑово!».
ÐбÑаÑиÑе внимание, Ñ
оÑÑ await и заÑÑавлÑÐµÑ JavaScript дожидаÑÑÑÑ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¿ÑомиÑа, ÑÑо не оÑÐ½Ð¸Ð¼Ð°ÐµÑ ÑеÑÑÑÑов пÑоÑеÑÑоÑа. Ðока пÑÐ¾Ð¼Ð¸Ñ Ð½Ðµ вÑполниÑÑÑ, JS-движок Ð¼Ð¾Ð¶ÐµÑ Ð·Ð°Ð½Ð¸Ð¼Ð°ÑÑÑÑ Ð´ÑÑгими задаÑами: вÑполнÑÑÑ Ð¿ÑоÑие ÑкÑипÑÑ, обÑабаÑÑваÑÑ ÑобÑÑÐ¸Ñ Ð¸ Ñ.п.
Ðо ÑÑÑи, ÑÑо пÑоÑÑо «ÑинÑакÑиÑеÑкий ÑаÑ
аÑ» Ð´Ð»Ñ Ð¿Ð¾Ð»ÑÑÐµÐ½Ð¸Ñ ÑезÑлÑÑаÑа пÑомиÑа, более наглÑднÑй, Ñем promise.then.
await нелÑÐ·Ñ Ð¸ÑполÑзоваÑÑ Ð² обÑÑнÑÑ
ÑÑнкÑиÑÑ
ÐÑли Ð¼Ñ Ð¿Ð¾Ð¿ÑобÑем иÑполÑзоваÑÑ await внÑÑÑи ÑÑнкÑии, обÑÑвленной без async, полÑÑим ÑинÑакÑиÑеÑкÑÑ Ð¾ÑибкÑ:
function f() {
let promise = Promise.resolve(1);
let result = await promise; // SyntaxError
}
ÐÑибки не бÑдеÑ, еÑли Ð¼Ñ Ñкажем клÑÑевое Ñлово async пеÑед обÑÑвлением ÑÑнкÑии. Ðак бÑло Ñказано ÑанÑÑе, await можно иÑполÑзоваÑÑ ÑолÑко внÑÑÑи asyncâÑÑнкÑий.
ÐавайÑе пеÑепиÑем пÑÐ¸Ð¼ÐµÑ showAvatar() из Ñаздела ЦепоÑка пÑомиÑов Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ async/await:
- Ðам нÑжно замениÑÑ Ð²ÑзовÑ
.thenнаawait. - РдобавиÑÑ ÐºÐ»ÑÑевое Ñлово
asyncпеÑед обÑÑвлением ÑÑнкÑии.
async function showAvatar() {
// запÑаÑиваем JSON Ñ Ð´Ð°Ð½Ð½Ñми полÑзоваÑелÑ
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
// запÑаÑиваем инÑоÑмаÑÐ¸Ñ Ð¾Ð± ÑÑом полÑзоваÑеле из github
let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
let githubUser = await githubResponse.json();
// оÑобÑажаем аваÑÐ°Ñ Ð¿Ð¾Ð»ÑзоваÑелÑ
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
// ждÑм 3 ÑекÑÐ½Ð´Ñ Ð¸ заÑем ÑкÑÑваем аваÑаÑ
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
return githubUser;
}
showAvatar();
ÐолÑÑилоÑÑ Ð¾ÑÐµÐ½Ñ Ð¿ÑоÑÑо и ÑиÑаемо, пÑавда? ÐоÑаздо лÑÑÑе, Ñем ÑанÑÑе.
await нелÑÐ·Ñ Ð¸ÑполÑзоваÑÑ Ð½Ð° веÑÑ
нем ÑÑовне вложенноÑÑиÐÑогÑаммиÑÑÑ, Ñзнав об await, ÑаÑÑо пÑÑаÑÑÑÑ Ð¸ÑполÑзоваÑÑ ÑÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð½Ð° веÑÑ
нем ÑÑовне вложенноÑÑи (вне Ñела ÑÑнкÑии). Ðо из-за Ñого, ÑÑо await ÑабоÑÐ°ÐµÑ ÑолÑко внÑÑÑи asyncâÑÑнкÑий, Ñак ÑделаÑÑ Ð½Ðµ полÑÑиÑÑÑ:
// SyntaxError на веÑÑ
нем ÑÑовне вложенноÑÑи
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
Ðожно обеÑнÑÑÑ ÑÑÐ¾Ñ ÐºÐ¾Ð´ в анонимнÑÑ asyncâÑÑнкÑиÑ, Ñогда вÑÑ Ð·Ð°ÑабоÑаеÑ:
(async () => {
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
...
})();
await ÑабоÑÐ°ÐµÑ Ñ Â«thenable»âобÑекÑамиÐак и promise.then, await позволÑÐµÑ ÑабоÑаÑÑ Ñ Ð¿ÑомиÑâÑовмеÑÑимÑми обÑекÑами. ÐÐ´ÐµÑ Ð² Ñом, ÑÑо еÑли Ñ Ð¾Ð±ÑекÑа можно вÑзваÑÑ Ð¼ÐµÑод then, ÑÑого доÑÑаÑоÑно, ÑÑÐ¾Ð±Ñ Ð¸ÑполÑзоваÑÑ ÐµÐ³Ð¾ Ñ await.
РпÑимеÑе ниже, ÑкземплÑÑÑ ÐºÐ»Ð°ÑÑа Thenable бÑдÑÑ ÑабоÑаÑÑ Ð²Ð¼ÐµÑÑе Ñ await:
class Thenable {
constructor(num) {
this.num = num;
}
then(resolve, reject) {
alert(resolve);
// вÑполниÑÑ resolve Ñо знаÑением this.num * 2 ÑеÑез 1000мÑ
setTimeout(() => resolve(this.num * 2), 1000); // (*)
}
};
async function f() {
// код бÑÐ´ÐµÑ Ð¶Ð´Ð°ÑÑ 1 ÑекÑндÑ,
// поÑле Ñего знаÑение result ÑÑÐ°Ð½ÐµÑ ÑавнÑм 2
let result = await new Thenable(1);
alert(result);
}
f();
Ðогда await полÑÑÐ°ÐµÑ Ð¾Ð±ÑÐµÐºÑ Ñ .then, не ÑвлÑÑÑийÑÑ Ð¿ÑомиÑом, JavaScript авÑомаÑиÑеÑки запÑÑÐºÐ°ÐµÑ ÑÑÐ¾Ñ Ð¼ÐµÑод, пеÑÐµÐ´Ð°Ð²Ð°Ñ ÐµÐ¼Ñ Ð°ÑгÑменÑÑ â вÑÑÑоеннÑе ÑÑнкÑии resolve и reject. ÐаÑем await пÑиоÑÑÐ°Ð½Ð¾Ð²Ð¸Ñ Ð´Ð°Ð»ÑнейÑее вÑполнение кода, пока лÑÐ±Ð°Ñ Ð¸Ð· ÑÑиÑ
ÑÑнкÑий не бÑÐ´ÐµÑ Ð²Ñзвана (в пÑимеÑе ÑÑо ÑÑÑока (*)). ÐоÑле Ñего вÑполнение кода пÑодолжиÑÑÑ Ñ ÑезÑлÑÑаÑом resolve или reject ÑооÑвеÑÑÑвенно.
ÐÐ»Ñ Ð¾Ð±ÑÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð°ÑинÑ
Ñонного меÑода доÑÑаÑоÑно напиÑаÑÑ async пеÑед именем:
class Waiter {
async wait() {
return await Promise.resolve(1);
}
}
new Waiter()
.wait()
.then(alert); // 1
Ðак и в ÑлÑÑае Ñ Ð°ÑинÑ
ÑоннÑми ÑÑнкÑиÑми, Ñакой меÑод гаÑанÑиÑованно возвÑаÑÐ°ÐµÑ Ð¿ÑомиÑ, и в его Ñеле можно иÑполÑзоваÑÑ await.
ÐбÑабоÑка оÑибок
Ðогда пÑÐ¾Ð¼Ð¸Ñ Ð·Ð°Ð²ÐµÑÑаеÑÑÑ ÑÑпеÑно, await promise возвÑаÑÐ°ÐµÑ ÑезÑлÑÑаÑ. Ðогда завеÑÑаеÑÑÑ Ñ Ð¾Ñибкой â бÑÐ´ÐµÑ Ð²ÑбÑоÑено иÑклÑÑение. Ðак еÑли Ð±Ñ Ð½Ð° ÑÑом меÑÑе наÑ
одилоÑÑ Ð²ÑÑажение throw.
Такой код:
async function f() {
await Promise.reject(new Error("УпÑ!"));
}
ÐÐµÐ»Ð°ÐµÑ Ñо же Ñамое, ÑÑо и Ñакой:
async function f() {
throw new Error("УпÑ!");
}
Ðо еÑÑÑ Ð¾ÑлиÑие: на пÑакÑике пÑÐ¾Ð¼Ð¸Ñ Ð¼Ð¾Ð¶ÐµÑ Ð·Ð°Ð²ÐµÑÑиÑÑÑÑ Ñ Ð¾Ñибкой не ÑÑазÑ, а ÑеÑез некоÑоÑое вÑемÑ. Ð ÑÑом ÑлÑÑае бÑÐ´ÐµÑ Ð·Ð°Ð´ÐµÑжка, а заÑем await вÑбÑоÑÐ¸Ñ Ð¸ÑклÑÑение.
Такие оÑибки можно ловиÑÑ, иÑполÑзÑÑ try..catch, как Ñ Ð¾Ð±ÑÑнÑм throw:
async function f() {
try {
let response = await fetch('http://no-such-url');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
Ð ÑлÑÑае оÑибки вÑполнение try пÑеÑÑваеÑÑÑ Ð¸ ÑпÑавление пÑÑÐ³Ð°ÐµÑ Ð² наÑало блока catch. Ðлоком try можно обеÑнÑÑÑ Ð½ÐµÑколÑко ÑÑÑок:
async function f() {
try {
let response = await fetch('/no-user-here');
let user = await response.json();
} catch(err) {
// пеÑеÑ
ваÑÐ¸Ñ Ð»ÑбÑÑ Ð¾ÑÐ¸Ð±ÐºÑ Ð² блоке try: и в fetch, и в response.json
alert(err);
}
}
f();
ÐÑли Ñ Ð½Ð°Ñ Ð½ÐµÑ try..catch, аÑинÑ
ÑÐ¾Ð½Ð½Ð°Ñ ÑÑнкÑÐ¸Ñ Ð±ÑÐ´ÐµÑ Ð²Ð¾Ð·Ð²ÑаÑаÑÑ Ð·Ð°Ð²ÐµÑÑивÑийÑÑ Ñ Ð¾Ñибкой пÑÐ¾Ð¼Ð¸Ñ (в ÑоÑÑоÑнии rejected). Ð ÑÑом ÑлÑÑае Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ Ð¼ÐµÑод .catch пÑомиÑа, ÑÑÐ¾Ð±Ñ Ð¾Ð±ÑабоÑаÑÑ Ð¾ÑибкÑ:
async function f() {
let response = await fetch('http://no-such-url');
}
// f() веÑнÑÑ Ð¿ÑÐ¾Ð¼Ð¸Ñ Ð² ÑоÑÑоÑнии rejected
f().catch(alert); // TypeError: failed to fetch // (*)
ÐÑли забÑÑÑ Ð´Ð¾Ð±Ð°Ð²Ð¸ÑÑ .catch, Ñо бÑÐ´ÐµÑ ÑгенеÑиÑована оÑибка «Uncaught promise error» и инÑоÑмаÑÐ¸Ñ Ð¾Ð± ÑÑом бÑÐ´ÐµÑ Ð²Ñведена в конÑолÑ. Такие оÑибки можно поймаÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑнÑм обÑабоÑÑиком, о ÑÑм подÑобно напиÑано в Ñазделе ÐÑомиÑÑ: обÑабоÑка оÑибок.
async/await и promise.then/catchÐÑи ÑабоÑе Ñ async/await, .then иÑполÑзÑеÑÑÑ Ð½ÐµÑаÑÑо, Ñак как await авÑомаÑиÑеÑки Ð¾Ð¶Ð¸Ð´Ð°ÐµÑ Ð·Ð°Ð²ÐµÑÑÐµÐ½Ð¸Ñ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¿ÑомиÑа. Ð ÑÑом ÑлÑÑае обÑÑно (но не вÑегда) гоÑаздо Ñдобнее пеÑеÑ
ваÑÑваÑÑ Ð¾Ñибки, иÑполÑзÑÑ try..catch, нежели Ñем .catch.
Ðо на веÑÑ
нем ÑÑовне вложенноÑÑи (вне asyncâÑÑнкÑий) await иÑполÑзоваÑÑ Ð½ÐµÐ»ÑзÑ, поÑÑÐ¾Ð¼Ñ .then/catch Ð´Ð»Ñ Ð¾Ð±ÑабоÑки ÑиналÑного ÑезÑлÑÑаÑа или оÑибок â обÑÑÐ½Ð°Ñ Ð¿ÑакÑика.
Так Ñделано в ÑÑÑоке (*) в пÑимеÑе вÑÑе.
async/await оÑлиÑно ÑабоÑÐ°ÐµÑ Ñ Promise.allÐогда необÑ
одимо подождаÑÑ Ð½ÐµÑколÑко пÑомиÑов одновÑеменно, можно обеÑнÑÑÑ Ð¸Ñ
в Promise.all, и заÑем await:
// await бÑÐ´ÐµÑ Ð¶Ð´Ð°ÑÑ Ð¼Ð°ÑÑив Ñ ÑезÑлÑÑаÑами вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð²ÑеÑ
пÑомиÑов
let results = await Promise.all([
fetch(url1),
fetch(url2),
...
]);
Ð ÑлÑÑае оÑибки она бÑÐ´ÐµÑ Ð¿ÐµÑедаваÑÑÑÑ ÐºÐ°Ðº обÑÑно: Ð¾Ñ Ð·Ð°Ð²ÐµÑÑивÑегоÑÑ Ñ Ð¾Ñибкой пÑомиÑа к Promise.all. РпоÑле бÑÐ´ÐµÑ ÑгенеÑиÑовано иÑклÑÑение, коÑоÑое можно оÑловиÑÑ, обеÑнÑв вÑÑажение в try..catch.
ÐÑого
ÐлÑÑевое Ñлово async пеÑед обÑÑвлением ÑÑнкÑии:
- ÐбÑзÑÐ²Ð°ÐµÑ ÐµÑ Ð²Ñегда возвÑаÑаÑÑ Ð¿ÑомиÑ.
- ÐозволÑÐµÑ Ð¸ÑполÑзоваÑÑ
awaitв Ñеле ÑÑой ÑÑнкÑии.
ÐлÑÑевое Ñлово await пеÑед пÑомиÑом заÑÑÐ°Ð²Ð¸Ñ JavaScript дождаÑÑÑÑ ÐµÐ³Ð¾ вÑполнениÑ, поÑле Ñего:
- ÐÑли пÑÐ¾Ð¼Ð¸Ñ Ð·Ð°Ð²ÐµÑÑаеÑÑÑ Ñ Ð¾Ñибкой, бÑÐ´ÐµÑ ÑгенеÑиÑовано иÑклÑÑение, как еÑли Ð±Ñ Ð½Ð° ÑÑом меÑÑе наÑ
одилоÑÑ
throw. - ÐнаÑе веÑнÑÑÑÑ ÑезÑлÑÑÐ°Ñ Ð¿ÑомиÑа.
ÐмеÑÑе они пÑедоÑÑавлÑÑÑ Ð¾ÑлиÑнÑй каÑÐºÐ°Ñ Ð´Ð»Ñ Ð½Ð°Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð°ÑÐ¸Ð½Ñ Ñонного кода. Такой код легко и пиÑаÑÑ, и ÑиÑаÑÑ.
ХоÑÑ Ð¿Ñи ÑабоÑе Ñ async/await можно обÑ
одиÑÑÑÑ Ð±ÐµÐ· promise.then/catch, иногда вÑÑ-Ñаки пÑиÑ
одиÑÑÑ Ð¸ÑполÑзоваÑÑ ÑÑи меÑÐ¾Ð´Ñ (на веÑÑ
нем ÑÑовне вложенноÑÑи, напÑимеÑ). Также await оÑлиÑно ÑабоÑÐ°ÐµÑ Ð² ÑоÑеÑании Ñ Promise.all, еÑли необÑ
одимо вÑполниÑÑ Ð½ÐµÑколÑко Ð·Ð°Ð´Ð°Ñ Ð¿Ð°ÑаллелÑно.

ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)