25 август 2025

Async/await

Va’dalar bilan ishlash uchun async/await deb nomlangan maxsus sintaksis mavjud. Buni tushunish va ishlatish hayratlanarli darajada oson.

Async funktsiyalari

Async kalit so’zidan boshlaymiz. Uni quyidagi funktsiyadan oldin qo’yish mumkin:

async function f() {
  return 1;
}

“Async” so’zi funktsiyadan oldin bitta oddiy narsani anglatadi: funktsiya har doim va’da qaytaradi. Agar funktsiya haqiqatan ham va’da bermaydigan qiymatni qaytarsa ham, funktsiya ta’rifini “async” kalit so’zi bilan oldindan belgilash JavaScript-ni ushbu qiymatni hal qilingan va’da bilan avtomatik ravishda o’rashga yo’naltiradi.

Masalan, yuqoridagi kod 1 natijasi bilan yechilgan va’dani qaytaradi, keling uni sinab ko’raylik:

async function f() {
  return 1;
}

f().then(alert); // 1

Biz aniq bir va’dani qaytara olamiz, xuddi shunday bo’lishi mumkin:

async function f() {
  return Promise.resolve(1);
}

f().then(alert); // 1

Shunday qilib, async funktsiya va’dani qaytarishini ta’minlaydi va undagi va’dalarni yopadi. Yetarlicha sodda, to’g’rimi? Ammo bu nafaqat. Faqat async funktsiyalarida ishlaydigan yana bir await kalit so’zi bor va bu juda ajoyib.

Await

Sintaksis:

// faqat async funktsiyalar ichida ishlaydi
let value = await promise;

await kalit so’zi JavaScript-ni ushbu va’da amalga oshguncha kutib turishiga va natijasini qaytarishiga olib keladi.

1 sekund ichida hal qilinadigan va’da bilan misol:

async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("done!"), 1000)
  });

  let result = await promise; // va'da hal bo'lguncha kuting (*)

  alert(result); // "done!"
}

f();

Funktsiyaning bajarilishi (*)satrrida “to’xtaydi” va va’da bajarilgandan so’ng davom etadi, natijada result uning natijasiga aylanadi. Shunday qilib, yuqoridagi kod “done!” ko’rsatadi bir soniyada.

Keling, ta’kidlab o’tamiz: await so’zma-so’z JavaScript-ni va’da tugaguncha kutib turishi va natija bilan davom etishi kerak. Bu hech qanday CPU resurslarini talab qilmaydi, chunki interpretator boshqa ishlarni bajarishi mumkin: boshqa skriptlarni bajarish, hodisalarni boshqarish va hk.

Bu va’da natijasini olish uchun va’daga ko’ra oqlangan sintaksis promise.then , o’qish va yozish osonroq.

Muntazam funktsiyalarda await dan foydalanib bo’lmaydi

Agar mos kelmaydigan funktsiyada await dan foydalanishga harakat qilsak, sintaksis xatosi bo’ladi:

function f() {
  let promise = Promise.resolve(1);
  let result = await promise; // Syntax error
}

Agar funktsiyadan oldin async qo’ymasak, bu xato bo’ladi. Yuqorida aytib o’tilganidek, await faqat async function ichida ishlaydi.

Va'dalar zanjiri bobidagi showAvatar() misolini olamiz va uni async/await yordamida qayta yozamiz:

  1. Biz .then chaqiruvlarini await bilan almashtirishimiz kerak.
  2. Shuningdek, ular ishlashi uchun async funktsiyani bajarishimiz kerak.
async function showAvatar() {

  // bizning JSON-ni o'qish
  let response = await fetch('/article/promise-chaining/user.json');
  let user = await response.json();

  // github foydalanuvchisini o'qish
  let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
  let githubUser = await githubResponse.json();

  // avatarni ko'rsatish
  let img = document.createElement('img');
  img.src = githubUser.avatar_url;
  img.className = "promise-avatar-example";
  document.body.append(img);

  // 3 soniya kuting
  await new Promise((resolve, reject) => setTimeout(resolve, 3000));

  img.remove();

  return githubUser;
}

showAvatar();

Juda toza va o’qish oson, to’g’rimi? Oldingiga qaraganda ancha yaxshi.

await yuqori darajadagi kodda ishlamaydi

await dan foydalanishni yangi boshlagan odamlar, biz await dan yuqori darajadagi kodda foydalana olmasligimizni unutishadi. Masalan, bu ishlamaydi:

// yuqori darajadagi kodda sintaksis xatosi
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();

Biz buni noma’lum async funktsiyasiga o’rashimiz mumkin, masalan:

(async () => {
  let response = await fetch('/article/promise-chaining/user.json');
  let user = await response.json();
  ...
})();

P.S. New feature: starting from V8 engine version 8.9+, top-level await works in modules.

await “thenables” ni qabul qiladi

promise.then singari, await ham ishlatilishi mumkin bo’lgan obyektlardan foydalanishga imkon beradi (then usuli bilan chaqirish mumkin). G’oya shundan iboratki, uchinchi tomon obyekti va’da bo’lmasligi mumkin, ammo va’daga mos kelishi mumkin: agar u .then ni qo’llab-quvvatlasa, await bilan ishlatish kifoya.

Demo Thenable klassi, quyida await uning holatlarini qabul qiladi:

class Thenable {
  constructor(num) {
    this.num = num;
  }
  then(resolve, reject) {
    alert(resolve);
    // 1000ms dan keyin this.num*2 bilan hal qilish
    setTimeout(() => resolve(this.num * 2), 1000); // (*)
  }
}

async function f() {
  // 1 soniyani kutadi, keyin natija 2 ga teng bo'ladi
  let result = await new Thenable(1);
  alert(result);
}

f();

Agar await .then bilan va’da qilinmagan obyektga ega bo’lsa, u ushbu funktsiyani mahalliy funktsiyalarni resolve, reject argument sifatida chaqiradi. Keyin await ulardan bittasi chaqirilguncha kutadi (yuqoridagi misolda u (*) satrida bo’ladi) va natijada bilan davom etadi.

Async usullari

Async klass usulini e’lon qilish uchun uni async bilan qo’shib qo’ying:

class Waiter {
  async wait() {
    return await Promise.resolve(1);
  }
}

new Waiter()
  .wait()
  .then(alert); // 1 (this is the same as (result => alert(result)))

Ma’nosi bir xil: qaytarilgan qiymat va’da bo’lishini ta’minlaydi va await ga imkon beradi.

Xato ishlov beruvchi

Agar va’da odatdagidek hal qilinsa, await promise natijani qaytaradi. Ammo rad etilgan taqdirda, u xuddi shu satrda throw iborasi bo’lganidek, xatoga yo’l qo’yadi.

Ushbu kod:

async function f() {
  await Promise.reject(new Error("Whoops!"));
}

Bu xuddi shunday:

async function f() {
  throw new Error("Whoops!");
}

Haqiqiy vaziyatlarda va’da rad etilishidan oldin biroz vaqt talab qilishi mumkin. Shunday qilib, await kutadi va keyin xato bo’ladi.

Ushbu xatoni odatiy throw singari try..catch yordamida topishimiz mumkin:

async function f() {

  try {
    let response = await fetch('http://no-such-url');
  } catch(err) {
    alert(err); // TypeError: failed to fetch
  }
}

f();

Xato bo’lsa, boshqaruv catch blokiga o’tadi. Shuningdek, biz bir nechta satrlarni o’rashimiz mumkin:

async function f() {

  try {
    let response = await fetch('/no-user-here');
    let user = await response.json();
  } catch(err) {
    // fetch va response.json-da xatolarga yo'l qo'yadi
    alert(err);
  }
}

f();

Agar bizda try..catch bo’lmasa, u holda f() async funktsiya chaqiruvi natijasida hosil bo’lgan va’da rad etiladi. Buni boshqarish uchun .catch qo’shib qo’yishimiz mumkin:

async function f() {
  let response = await fetch('http://no-such-url');
}

// f() rad qilingan va'daga aylanadi
f().catch(alert); // TypeError: failed to fetch // (*)

Agar u yerga .catch qo’shishni unutib qo’ysak, unda ishlov berilmagan va’da xatosi paydo bo’ladi (konsolda ko’rish mumkin). Biz Va'dalar bilan ishlashda xato bobida tasvirlangan hodisalarni global ishlov beruvchisi yordamida bunday xatolarga yo’l qo’yamiz.

async/await va promise.then/catch

Biz async/await dan foydalanganda kamdan-kam hollarda .then kerak bo’ladi, chunki await bizga kutishni boshqaradi. Va .catch o’rniga odatdagi try..catch dan foydalanishimiz mumkin. Bu odatda (har doim ham) qulayroq emas.

Ammo kodning yuqori darajasida, har qanday async funktsiyasidan tashqarida bo’lganimizda, sintaktik ravishda await dan foydalana olmaymiz, shuning uchun yakuniy natijani boshqarish uchun .then/catch yoki xatolarni qo’shish odatiy holdir.

Yuqoridagi misolning (*) satridagi kabi.

async/await Promise.all bilan yaxshi ishlaydi

Bir nechta va’dalarni kutish kerak bo’lganda, ularni Promise.all ga o’rab, keyin await orqali kutish mumkin:

// natijalar massivini kutish
let results = await Promise.all([
  fetch(url1),
  fetch(url2),
  ...
]);

Xato bo’lsa, u odatdagidek tarqaladi: bajarilmagan va’dadan Promise.all gacha va keyin biz chaqiruv atrofida try..catch yordamida ushlab qolishimiz mumkin bo’lgan istisnoga aylanadi.

Mikrovazifa navbat

Mikrovazifalar va hodisalar tsikli bobida ko’rganimizdek, va’da beruvchilar asinxron tarzda bajariladi. Har bir .then/catch/finally ishlov beruvchisi avval “mikrovazifa navbatiga” kiradi va joriy kod tugagandan so’ng bajariladi.

Async/await va’dalarga asoslanadi, shuning uchun u bir xil mikrovazifalarda navbatni ichki sifatida ishlatadi va makrovazifalarga nisbatan bir xil ustuvorlikka ega.

Masalan, bizda:

  • setTimeout(handler, 0), nol kechikish bilan handler ni ishga tushirishi kerak.
  • let x = await f(), function f() bu async, lekin darhol qaytadi.

Kodda await ning ostida setTimeout bo’lsa, qaysi biri birinchi ishlaydi?

async function f() {
  return 1;
}

(async () => {
    setTimeout(() => alert('timeout'), 0);

    await f();

    alert('await');
})();

Bu yerda noaniqlik yo’q: await har doim birinchi bo’lib tugaydi, chunki (mikrovazifa) u setTimeout ishlov berishdan yuqori ustuvorlikka ega.

Xulosa

Funktsiyadan oldin async kalit so’zi ikkita ta’sirga ega:

  1. Har doim va’dasini qaytaradi.
  2. Unda await dan foydalanishga ruxsat beradi.

Va’da oldidan await kalit so’zi JavaScript-ni ushbu va’da bajarilishini kutib turishiga majbur qiladi va keyin:

  1. Agar bu xato bo’lsa, istisno yaratiladi, xuddi o’sha joyda throw error chaqirilgandek.
  2. Aks holda, natijani qaytaradi, shuning uchun biz uni qiymatga belgilashimiz mumkin.

Ular birgalikda asinxron kod yozish uchun ajoyib asos yaratadilar, bu ham o’qilishi, ham yozilishi oson.

Async/await bilan biz kamdan-kam hollarda promise.then/catch deb yozishimiz kerak, ammo biz hali ham ularning va’dalarga asoslanganligini unutmasligimiz kerak, chunki ba’zida (masalan, tashqi tomondan) biz ushbu usullardan foydalanishimiz kerak. Shuningdek, Promise.all bir vaqtning o’zida ko’plab vazifalarni kutish uchun foydali narsa.

Vazifalar

Va'dalar zanjiri bobidagi misollardan birini .then/catch o’rniga async/await yordamida qayta yozing:

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").catch(alert); // Error: 404

Izohlar kod ostida:

async function loadJson(url) {
  // (1)
  let response = await fetch(url); // (2)

  if (response.status == 200) {
    let json = await response.json(); // (3)
    return json;
  }

  throw new Error(response.status);
}

loadJson("no-such-user.json").catch(alert); // Error: 404 (4)

Izohlar:

  1. loadUrl funktsiyasi async bo’ladi.

  2. Hammasi .then ichi await bilan almashtiriladi.

  3. Buni kutish o’rniga response.json() ni qaytarishimiz mumkin, masalan:

    if (response.status == 200) {
      return response.json(); // (3)
    }

    Shunda tashqi kod ushbu va’dani hal qilishini kutishi kerak edi. Bizning holatda bu muhim emas.

  4. loadJson dan chiqarilgan xato .catch tomonidan hal qilinadi. Biz u yerda await loadJson(…) dan foydalana olmaymiz, chunki biz async funktsiyasida emasmiz.

Quyida Va'dalar zanjiri bobidagi “rethrow” misolini topishingiz mumkin. Uni .then/catch o’rniga async/await yordamida qayta yozing.

Va demoGithubUser dagi tsikl foydasiga rekursiyadan xalos bo’ling: async/await yordamida bajarish oson bo’ladi.

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 haqiqiy foydalanuvchini ismini qaytarguncha foydalanuvchi ismini so'rang
function demoGithubUser() {
  let name = prompt("Ism kiriting", "iliakan");

  return loadJson(`https://api.github.com/users/${name}`)
    .then((user) => {
      alert(`To'liq ism: ${user.name}.`);
      return user;
    })
    .catch((err) => {
      if (err instanceof HttpError && err.response.status == 404) {
        alert("Bunday foydalanuvchi yo'q, iltimos qayta kiring.");
        return demoGithubUser();
      } else {
        throw err;
      }
    });
}

demoGithubUser();

Bu yerda hech qanday fokuslar yo’q. demoGithubUser ichida .catch ni try...catch bilan almashtiring va kerak bo’lganda async/await ni qo’shing:

class HttpError extends Error {
  constructor(response) {
    super(`${response.status} for ${response.url}`);
    this.name = "HttpError";
    this.response = response;
  }
}

async function loadJson(url) {
  let response = await fetch(url);
  if (response.status == 200) {
    return response.json();
  } else {
    throw new HttpError(response);
  }
}

// Github haqiqiy foydalanuvchini qaytarguncha foydalanuvchi nomini so'rang
async function demoGithubUser() {
  let user;
  while (true) {
    let name = prompt("Ism kiriting", "iliakan");

    try {
      user = await loadJson(`https://api.github.com/users/${name}`);
      break; // xato yo'q, tsiklni tark etish
    } catch (err) {
      if (err instanceof HttpError && err.response.status == 404) {
        // alert-dan keyin tsikl davom etadi
        alert("Bunday foydalanuvchi yo'q, iltimos qayta kiring.");
      } else {
        // noma'lum xato, qaytarmoq
        throw err;
      }
    }
  }

  alert(`To'liq ism: ${user.name}.`);
  return user;
}

demoGithubUser();

Bizda “muntazam” funktsiya mavjud. Undan async ga qanday chaqiruv bajarish va uning natijasidan foydalanish kerak?

async function wait() {
  await new Promise((resolve) => setTimeout(resolve, 1000));

  return 10;
}

function f() {
  // ...bu yerda nima yozish kerak?
  // async wait() ni chaqirishimiz va 10 ni kutishimiz kerak
  // esda tuting, biz "await" dan foydalana olmaymiz
}

P.S. Vazifa texnik jihatdan juda sodda, ammo async/await uchun yangi dasturchilar uchun savol juda keng tarqalgan.

Ichkarida qanday ishlashini bilish foydalidir.

Faqat async chaqiruvini va’da sifatida ko’rib chiqing va unga .then qo’shib qo’ying.

async function wait() {
  await new Promise(resolve => setTimeout(resolve, 1000));

  return 10;
}

function f() {
  // 1 soniyadan keyin 10ni ko'rsatadi
  wait().then(result => alert(result));
}

f();
O'quv qo'llanma xaritasi

Izohlar

izoh berishdan oldin buni o'qing…
  • Agar sizda nimani yaxshilash kerakligi haqida takliflaringiz bo'lsa - iltimos, GitHub muammosini yuboring yoki izoh berish o'rniga so'rov yuboring.
  • Agar siz maqolada biror narsani tushunolmasangiz - iltimos, batafsilroq ma'lumot bering.
  • Bir nechta so'z so'zlarini kiritish uchun <code> yorlig'ini ishlating, bir nechta satrlar uchun - ularni <pre> yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepen…)