"Асинхрондук/күтүү" деп аталган убадалар менен иштөө үчүн атайын синтаксис бар. Аны түшүнүү жана колдонуу таң калыштуу оңой.
Асинхрондук функциялар
Ачкыч сөз менен баштайлы async
. Ал функциянын алдына төмөнкүдөй жайгаштырылат:
@A@async function f() {
return 1;
}@A@
Сөздүн async
бир жөнөкөй мааниси бар: бул функция ар дайым убаданы кайтарат. Башка түрдөгү баалуулуктар автоматтык түрдө ийгиликтүү убадага оролгон.
Мисалы, бул функция аткарылган убаданы натыйжа менен кайтарат 1
:
@A@async function f() {
return 1;
}
f().then(alert); // 1@A@
Сиз убаданы ачык кайтара аласыз, натыйжасы бирдей болот:
@A@async function f() {
return Promise.resolve(1);
}
f().then(alert); // 1@A@
async
Ошентип , функциянын алдындагы ачкыч сөз функция баары бир убаданы кайтарып берерин камсыздайт. Макул, жетиштүү жөнөкөй? Бирок бул баары эмес. Дагы бир ачкыч сөз бар await
, аны -функциялардын ичинде гана колдонсо болот async
.
Await
Синтаксис:
@A@// работает только внутри async–функций
let value = await promise;@A@
Ачкыч сөз await
JavaScript котормочусун оң жактагы убада await
аткарылганга чейин күтүшүнө себеп болот. Андан кийин, ал өз натыйжасын кайтарып берет жана коддун аткарылышы улантылат.
Бул мисалда, убада 1 секунданын ичинде ишке ашат:
@A@async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("готово!"), 1000)
});
let result = await promise; // будет ждать, пока промис не выполнится (*)
alert(result); // "готово!"
}
f();@A
Бул мисалда, функциянын аткарылышы (*)
убада аткарылмайынча сапта токтойт. Бул функция башталгандан бир секунддан кийин болот. Андан кийин, result
убаданын аткарылышынын натыйжасы өзгөрмөгө жазылат, ал эми браузер "бүттү!" эскертүү терезесин көрсөтөт.
Ал JavaScriptти убаданын аткарылышын күтүүгө мажбурласа да await
, CPU ресурстарын талап кылбайт. Убада аткарылмайынча, JS кыймылдаткычы башка тапшырмаларды аткара алат: башка скрипттерди аткаруу, окуяларды башкаруу ж.б.
Чынында, бул жөн гана убаданын натыйжасын алуу үчүн "синтаксистик кант" , караганда сүрөттөмө promise.then
.
await
кадимки функцияларда колдонууга болбойтawait
Эгер биз билдирилген функциянын ичинде колдонууга аракет кылсак async
, синтаксис катасын алабыз:
@A@function f() {
let promise = Promise.resolve(1);
let result = await promise; // SyntaxError
}@A
async
Функцияны жарыялоонун алдында ачкыч сөздү көрсөтсөк, ката болбойт . Мурда айтылгандай, await
аны -функциялардын ичинде гана колдонсо болот async
.
Келгиле , Убадаларды чынжырлоодонshowAvatar()
мисалды кайра жазалы :async/await
- Биз чалуулар
.then
менен алмаштыруу керекawait
. async
Жана функция декларациясынын алдында ачкыч сөздү кошуңуз .
@A@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();@A@
Бул абдан жөнөкөй жана окула турган болуп чыкты, туурабы? Мурдагыдан алда канча жакшы.
await
жогорку уя деңгээлинде колдонууга болбойтПрограммисттер, жөнүндө билип await
, көп учурда бул функцияны уянын жогорку деңгээлинде колдонууга аракет кылышат (функциянын корпусунан тышкары). Бирок await
ал -функциялардын ичинде гана иштегендиктен async
, бул иштебейт:
@A@// 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();
...
})();@A@
await
"андан кийин" объектилер менен иштейтсыяктуу promise.then
, await
убадага шайкеш объекттер менен иштөөгө мүмкүндүк берет. Идея, эгерде сиз объектте методду чакыра алсаңыз then
, аны менен колдонуу үчүн жетиштүү болот await
.
Төмөнкү мисалда класс инстанциялары Thenable
менен иштейт await
:
@A@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();@A@
await
Ал убада кылынбаган объектти алганда , .then
JavaScript бул ыкманы автоматтык түрдө иштетип, ага орнотулган функцияларды resolve
жана аргументтерди өткөрүп берет reject
. Андан кийин await
бул функциялардын бири чакырылмайынча коддун андан аркы аткарылышын токтотот (мисалы, бул сап (*)
). resolve
Андан кийин, коддун аткарылышы натыйжа менен же тиешелүү түрдө уланат reject
.
Асинхрондук ыкманы жарыялоо үчүн, жөн гана async
аталыштын алдына жазыңыз:
@A@class Waiter {
async wait() {
return await Promise.resolve(1);
}
}
new Waiter()
.wait()
.then(alert); // 1@A@
Асинхрондук функциялар сыяктуу эле, бул ыкма убаданы кайтарууга кепилдик берет жана сиз колдоно аласыз await
.
Ката иштетүү
Убада ишке ашса, await promise
жыйынтыгын кайтарат. Качан ал ката менен аяктаганда, өзгөчөлүк ташталат. Бул жерде бир сөз бар дегендей throw
.
Мындай код:
@A@async function f() {
await Promise.reject(new Error("Упс!"));
}
Ушул сыяктуу эле нерсени жасайт:
async function f() {
throw new Error("Упс!");
}@A@
Бирок бир айырма бар: иш жүзүндө убада дароо ишке ашпай калышы мүмкүн, бирок бир нече убакыт өткөндөн кийин. Бул учурда кечигүү болот, андан кийин await
өзгөчөлүктү таштайт.
try..catch
Мындай каталар адаттагыдай эле, колдонуу менен кармалышы мүмкүн throw
:
@A@async function f() {
try {
let response = await fetch('http://no-such-url');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();@A@
Ката болгон учурда, аткаруу try
үзгүлтүккө учурайт жана башкаруу блоктун башына өтөт catch
. Блок try
бир нече саптарды ороп алат:
@A@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();@A@
Эгерде бизде жок болсо try..catch
, асинхрондук функция ишке ашпай калган убаданы кайтарат ( абалында rejected
). .catch
Бул учурда, катаны чечүү үчүн убада ыкмасын колдоно алабыз :
@A@async function f() {
let response = await fetch('http://no-such-url');
}
// f() вернёт промис в состоянии rejected
f().catch(alert); // TypeError: failed to fetch // (*)@A@
Эгер сиз кошууну унутуп калсаңыз .catch
, анда "Колдонулбаган убада катасы" катасы пайда болот жана бул тууралуу маалымат консолдо көрсөтүлөт. Мындай каталарды Убадалар: Ката иштетүү бөлүмүндө айтылгандай, глобалдык иштеткич кармап алат .
async/await
Жанаpromise.then/catch
менен иштөөдө async/await
, .then
ал сейрек колдонулат, анткени await
ал автоматтык түрдө убаданын аткарылышын күтөт. Бул учурда, адатта (бирок ар дайым эмес) каталарды колдонуунун try..catch
ордуна колдонуу алда канча ыңгайлуу .catch
.
Бирок уянын жогорку деңгээлинде ( async
-функциялардан тышкары) await
аны колдонууга болбойт, ошондуктан .then/catch
акыркы натыйжаны же каталарды чечүү кеңири таралган практика болуп саналат.
(*)
Бул жогорудагы мисалдагы сапта жасалат .
async/await
менен сонун иштейтPromise.all
Бир эле учурда бир нече убадаларды күтүшүңүз керек болгондо, аларды ороп Promise.all
, анан await
:
@A@// await будет ждать массив с результатами выполнения всех промисов
let results = await Promise.all([
fetch(url1),
fetch(url2),
...
]);@A@
Ката болгон учурда, ал кадимкидей өткөрүлөт: аткарылбай калган убададан Promise.all
. Андан кийин өзгөчөлүк ыргытылат, аны менен туюнтманы ороп кармоого болот try..catch
.
Бардыгы
async
Функцияны жарыялоодон мурун ачкыч сөз :
- Аны дайыма убадасын кайтарууга мажбурлайт.
await
Бул функцияны денеде колдонууга мүмкүндүк берет .
Алдын ала убадаланган ачкыч сөз await
JavaScript анын аягына чейин күтүшүнө себеп болот, андан кийин:
- Убада ишке ашпай калса, бир өзгөчөлүк бар эле, ыргытылат
throw
. - Болбосо убаданын натыйжасы кайтарылат.
Алар биргелешип асинхрондук код жазуу үчүн сонун негизди камсыз кылат. Мындай кодду жазуу жана окуу оңой.
менен иштегенде async/await
ансыз деле кыла алсаңыз да promise.then/catch
, кээде сиз дагы эле бул ыкмаларды колдонушуңуз керек (мисалы, уянын жогорку деңгээлинде). Эгер параллелдүү бир нече тапшырмаларды аткаруу керек болсо, await
айкалышта да сонун иштейт.Promise.all
Tasks
Анын ордуна Убада чынжырчасынын мисалдарынын бирин кайра жазыңыз :async/await
.then/catch
@A@function loadJson(url) {
return fetch(url)
.then(response => {
if (response.status == 200) {
return response.json();
} else {
throw new Error(response.status);
}
})
}
loadJson('no-such-user.json') // (3)
.catch(alert); // Error: 404@A@
Төмөндө Убада чынжыр бөлүгүнөн мисал келтирилген , аны async/await
ордуна колдонуп кайра жазыңыз .then/catch
.
Функцияда demoGithubUser
рекурсияны цикл менен алмаштырыңыз: колдонуп async/await
, муну жасоо оңой болот.
@A@class HttpError extends Error {
constructor(response) {
super(`${response.status} for ${response.url}`);
this.name = 'HttpError';
this.response = response;
}
}
function loadJson(url) {
return fetch(url)
.then(response => {
if (response.status == 200) {
return response.json();
} else {
throw new HttpError(response);
}
})
}
// Запрашивать логин, пока github не вернёт существующего пользователя.
function demoGithubUser() {
let name = prompt("Введите логин?", "iliakan");
return loadJson(`https://api.github.com/users/${name}`)
.then(user => {
alert(`Полное имя: ${user.name}.`);
return user;
})
.catch(err => {
if (err instanceof HttpError && err.response.status == 404) {
alert("Такого пользователя не существует, пожалуйста, повторите ввод.");
return demoGithubUser();
} else {
throw err;
}
});
}
demoGithubUser()@A@;
"Нормалдуу" функция бар. Анын ичиндеги -функцияны аткаруунун натыйжасын кантип алууга болот async
?
@A@async function wait() {
await new Promise(resolve => setTimeout(resolve, 1000));
return 10;
}
function f() {
// ...что здесь написать?
// чтобы вызвать wait() и дождаться результата "10" от async–функции
// не забывайте, здесь нельзя использовать "await"
}@A@
PS Техникалык жактан алганда, тапшырма абдан жөнөкөй, бирок бул суроо көп учурда асинхрондоштуруу / күтүү менен жакында таанышкан иштеп чыгуучулар тарабынан берилет.