Async/await
async/await โ promise'lar bilan ishlashning eng qulay va zamonaviy usuli. U asinxron kodni sinxronga o'xshab, o'qishga oson ko'rinishda yozishga imkon beradi.
async funksiyalar
async so'zini funksiya oldiga qo'yish mumkin. Bu bitta narsani anglatadi: funksiya doim promise qaytaradi. Agar funksiya oddiy qiymat qaytarsa, JavaScript uni avtomatik ravishda resolved promise'ga o'raydi.
async function f() {
return 1;
}
f().then(console.log); // 1 (1 avtomatik resolved promise'ga o'raldi)
Ya'ni async function f() { return 1; } aslida return Promise.resolve(1) bilan bir xil. Biz albatta promise'ni ham to'g'ridan-to'g'ri qaytarishimiz mumkin โ natija bir xil.
Sodda misolda buni tekshirib ko'ramiz โ async funksiyaning ichidagi console.log sinxron ishlaydi:
console.log'lar ko'rinadi. return "Salom!" promise'ni resolve qiladi, lekin uni .then bilan olmasak, natija konsolda ko'rinmaydi.await
await โ bu haqiqiy sehr. Uning sintaksisi:
// await faqat async funksiya ichida ishlaydi
let value = await promise;
await so'zi JavaScript'ni promise yakunlanguncha kutishga majbur qiladi. Promise resolve bo'lgach, uning natijasini qaytaradi va kod davom etadi.
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('Tayyor!'), 1000);
});
let result = await promise; // promise yakunlanguncha kutadi (*)
console.log(result); // 'Tayyor!'
}
f();
(*) belgisidagi qatorda funksiya bajarilishi to'xtaydi va promise yakunlanishini kutadi. Muhimi โ bu kutish paytida JavaScript dvigateli boshqa ishlarni bajaraveradi (boshqa skriptlar, hodisalar). Ya'ni protsessor "band" bo'lmaydi, bu shunchaki chiroyli kutish usuli.
.then bilan solishtirsak, await ancha o'qishga oson. Quyidagi ikki kod bir xil ishni bajaradi:
// .then bilan
function loadUser() {
return fetch('/user.json')
.then(response => response.json())
.then(user => user.name);
}
// async/await bilan (ancha tozaroq)
async function loadUser() {
let response = await fetch('/user.json');
let user = await response.json();
return user.name;
}
await faqat async ichida
await'ni oddiy (async bo'lmagan) funksiya ichida ishlatib bo'lmaydi โ bu sintaksis xatosi beradi.function f() {
let promise = Promise.resolve(1);
let result = await promise; // Syntax error!
}
Sabab: await faqat async funksiyaning ichida ma'noga ega, chunki funksiyani "to'xtatib turish" mexanizmi shu yerda mavjud.
Top-level await
Zamonaviy JavaScript'da (ES modullarida) await'ni funksiyadan tashqarida, modul darajasida ham ishlatish mumkin โ buni top-level await deyiladi:
// modul darajasida (ES module ichida)
let response = await fetch('/user.json');
let user = await response.json();
console.log(user);
Agar top-level await mavjud bo'lmagan muhitda (yoki eski kodda) ishlatmoqchi bo'lsak, odatda kodni anonim async funksiyaga o'raymiz:
(async () => {
let response = await fetch('/user.json');
let user = await response.json();
console.log(user);
})();
Xatoliklar: try/catch
Agar promise reject bo'lsa, await o'sha joyda xato tashlaydi (throw) โ xuddi throw operatori kabi. Shuning uchun uni oddiy try/catch bilan ushlash mumkin:
async function f() {
try {
let response = await fetch('http://no-such-url');
} catch (err) {
console.log(err); // TypeError: failed to fetch
}
}
f();
Bir try/catch bir nechta await'ni o'rab, ularning har biridagi xatoni ushlashi mumkin:
async function f() {
try {
let response = await fetch('/no-user-here');
let user = await response.json();
} catch (err) {
// fetch va response.json() xatolarini ushlaydi
console.log('Xato ushlandi: ' + err.message);
}
}
.catchga qaraganda tabiiyroq: sinxron kod xatolarini qanday ushlasak, asinxron kod xatolarini ham xuddi shunday try/catch bilan ushlaymiz.Agar try/catch ishlatmasak, async funksiya qaytargan promise reject bo'ladi. Uni tashqarida .catch bilan ushlashimiz mumkin:
async function f() {
let response = await fetch('http://no-such-url');
}
// f() reject qilingan promise qaytaradi
f().catch(console.log); // TypeError: failed to fetch
Promise bilan birga ishlash
async/await promise'lar bilan yaxshi ishlaydi. Masalan, bir nechta ishni parallel kutish uchun await Promise.all(...) ishlatamiz:
async function f() {
// ikkala fetch parallel boshlanadi, ikkalasini kutamiz
let results = await Promise.all([
fetch(url1),
fetch(url2),
fetch(url3)
]);
// results โ barcha javoblar massivi
return results;
}
await qilsangiz, ular navbatma-navbat bajariladi (sekin). Ularni parallel ishga tushirish uchun Promise.all ichiga joylang โ bu ancha tezroq.await nafaqat promise, balki "thenable" obyektlar (ya'ni .then metodiga ega obyektlar) bilan ham ishlaydi โ xuddi promise.then kabi.
Xulosa
asyncfunksiya doim promise qaytaradi; oddiy qiymat avtomatik resolved promise'ga o'raladi;awaitpromise yakunlanguncha kutadi va natijasini qaytaradi;awaitfaqatasyncfunksiya ichida (yoki ES modul darajasida โ top-level await) ishlaydi;- Xatolarni oddiy
try/catchbilan, yoki tashqarida.catchbilan ushlash mumkin; - Parallel ishlar uchun
await Promise.all(...)ishlatiladi; async/awaitโ.then/.catchzanjirlariga qaraganda ancha o'qishga oson va toza usul.