Node.js'da asinxron kod

fs darsida bitta jumla aytilgan edi: Sync metodlari serverni bloklaydi. Endi shuning muqobilini to’liq o’rganamiz. Node.js kodining katta qismi asinxron yoziladi — fayl, tarmoq, baza bilan ishlashning hammasi shu uslubda.

Uch avlod: bir xil vazifa, uch yozuv

Node.js’da asinxron kod yozish usuli vaqt bilan o’zgargan. Uchalasini ham uchratasiz, shuning uchun uchalasini ham tanish kerak.

Uslub Yozuv Holati
Callback fs.readFile(path, cb) Eski, hali ko’p uchraydi
Promise .then().catch() Oraliq bosqich
async/await await fs.readFile(path) Hozirgi standart

Bir xil vazifani — faylni o’qib chiqarishni — uch marta yozamiz.

1. Callback uslubi

callback-demo.js:

const fs = require("fs");

console.log("1. O'qish boshlandi");

fs.readFile("notes.txt", "utf8", (error, data) => {
  if (error) {
    console.log("Xato:", error.code);
    return;
  }
  console.log("2. Fayl o'qildi, uzunligi:", data.length);
});

console.log("3. Bu qator oldin chiqadi");
1. O'qish boshlandi
3. Bu qator oldin chiqadi
2. Fayl o'qildi, uzunligi: 29

Tartibga qarang: 3 2 dan oldin chiqdi. Node.js o’qish buyrug’ini berdi va javobni kutmasdan davom etdi. Fayl tayyor bo’lganda callback chaqirildi.

Callback’lar ichma-ich kirganda

Bitta fayl uchun callback qulay. Uchta faylni ketma-ket o’qish kerak bo’lsa, kod o’ngga siljiy boshlaydi:

const fs = require("fs");

fs.readFile("a.txt", "utf8", (error, first) => {
  if (error) {
    console.log("Xato:", error.code);
    return;
  }
  fs.readFile("b.txt", "utf8", (error, second) => {
    if (error) {
      console.log("Xato:", error.code);
      return;
    }
    fs.readFile("c.txt", "utf8", (error, third) => {
      if (error) {
        console.log("Xato:", error.code);
        return;
      }
      console.log("Hammasi:", first.trim(), second.trim(), third.trim());
    });
  });
});

Uch fayl uchun uch daraja. O’nta amal bo’lsa nima bo’lishini tasavvur qiling.

2. Promise uslubi

Promise — kelajakda tayyor bo’ladigan qiymat va’dasi. fs.promises shu ko’rinishda ishlaydi:

const fs = require("fs").promises;

fs.readFile("notes.txt", "utf8")
  .then((data) => {
    console.log("O'qildi, uzunligi:", data.length);
  })
  .catch((error) => {
    console.log("Xato:", error.code);
  });
O'qildi, uzunligi: 29

Ketma-ket o’qish endi ichma-ich emas, zanjir bo’ladi:

const fs = require("fs").promises;

fs.readFile("a.txt", "utf8")
  .then((first) => {
    console.log("Birinchi:", first.trim());
    return fs.readFile("b.txt", "utf8");
  })
  .then((second) => {
    console.log("Ikkinchi:", second.trim());
  })
  .catch((error) => {
    console.log("Xato:", error.code);
  });
Birinchi: bir
Ikkinchi: ikki

Ikki yutuq: kod o’ngga surilmaydi va bitta catch butun zanjirning xatosini ushlaydi.

3. async/await uslubi

async/await — o’sha Promise, lekin sinxron koddek o’qiladigan yozuv:

const fs = require("fs").promises;

async function readNotes() {
  try {
    const first = await fs.readFile("a.txt", "utf8");
    console.log("Birinchi:", first.trim());

    const second = await fs.readFile("b.txt", "utf8");
    console.log("Ikkinchi:", second.trim());
  } catch (error) {
    console.log("Xato:", error.code);
  }
}

readNotes();
Birinchi: bir
Ikkinchi: ikki

Uchta yozuvni yonma-yon qo’ysak, farq ko’rinadi:

Jihat Callback Promise async/await
Ichma-ichlik Chuqurlashadi Tekis zanjir Tekis
Xato ushlash Har darajada .catch() try/catch
O’qilishi Qiyin O’rtacha Sinxron koddek

Yangi kod async/await bilan yoziladi. Qolgan ikkisi eski loyihalarni o’qish uchun kerak.

util.promisify — eski funksiyani yangilash

Ba’zi kutubxonalar hali ham faqat callback qabul qiladi. Ularni Promise ga aylantirish mumkin:

const fs = require("fs");
const util = require("util");

// Callback qabul qiladigan funksiyani Promise qaytaradigan qilamiz
const readFileAsync = util.promisify(fs.readFile);

async function run() {
  const data = await readFileAsync("notes.txt", "utf8");
  console.log("O'qildi:", data.length, "belgi");
}

run();
O'qildi: 29 belgi

Ketma-ket va parallel

Bu farq amalda tezlikka ta’sir qiladi.

Ketma-ket — har await oldingisini kutadi:

const fs = require("fs").promises;

async function readSequential() {
  const first = await fs.readFile("a.txt", "utf8");
  const second = await fs.readFile("b.txt", "utf8");
  const third = await fs.readFile("c.txt", "utf8");

  console.log("Ketma-ket:", [first.trim(), second.trim(), third.trim()]);
}

readSequential();

Uchala fayl bir-biriga bog’liq emas — kutishning hojati yo’q.

ParallelPromise.all hammasini birdan boshlaydi:

const fs = require("fs").promises;

async function readParallel() {
  const [first, second, third] = await Promise.all([
    fs.readFile("a.txt", "utf8"),
    fs.readFile("b.txt", "utf8"),
    fs.readFile("c.txt", "utf8"),
  ]);

  console.log("Parallel:", [first.trim(), second.trim(), third.trim()]);
}

readParallel();
Parallel: [ 'bir', 'ikki', 'uch' ]

Natijalar massivi berilgan tartibda qaytadi, qaysi fayl tezroq o’qilganidan qat’i nazar.

Usul Qachon
Ketma-ket await Keyingi amal oldingisining natijasiga bog’liq
Promise.all Amallar bir-biridan mustaqil

Event loop — tartib qanday belgilanadi

Node.js nima darsida aytilgan edi: Node.js bitta oqimda ishlaydi, lekin kutish paytida bo’sh turmaydi. Buni boshqaradigan tizim — event loop.

Tartibni ko’rish uchun:

console.log("1 sinxron");

setTimeout(() => console.log("4 setTimeout"), 0);

Promise.resolve().then(() => console.log("3 promise"));

process.nextTick(() => console.log("2 nextTick"));

console.log("1.5 sinxron oxiri");
1 sinxron
1.5 sinxron oxiri
2 nextTick
3 promise
4 setTimeout

setTimeout da kutish vaqti 0 bo’lsa ham oxirida bajarildi. Tartib shunday:

  1. Butun sinxron kod;
  2. process.nextTick navbati;
  3. Promise navbati (then, await davomi);
  4. Taymerlar (setTimeout, setInterval).

Demak setTimeout(fn, 0) “hoziroq bajar” degani emas — “sinxron kod va Promise’lar tugagach bajar” degani.

Event loop darsida bu mexanizm batafsil ko’rilgan.

Amaliy mashq

node-start papkasida a.txt, b.txt, c.txt fayllarini yaratib bajaring:

  1. fs.readFile ni callback bilan yozing va callback ichiga hamda tashqarisiga console.log qo’yib, chiqish tartibini kuzating;
  2. Mavjud bo’lmagan fayl nomini berib, if (error) shoxi ishlashini ko’ring;
  3. Xuddi shu vazifani fs.promises va .then().catch() bilan qayta yozing;
  4. Uchinchi marta async/await va try/catch bilan yozing, uch variantni yonma-yon qo’yib solishtiring;
  5. Uchta faylni avval ketma-ket await bilan, keyin Promise.all bilan o’qing;
  6. Promise.all ro’yxatiga mavjud bo’lmagan fayl qo’shib, catch nima ushlashini ko’ring.

4-qadam eng muhimi: bir xil mantiqni uch uslubda ko’rgandan keyin eski kodni o’qish qiyin bo’lmaydi.

Qisqacha xulosa

  • Node.js’da fayl va tarmoq amallari asinxron: natija keyinroq keladi.
  • Callback’ning birinchi parametri har doim xatoerror-first tartibi.
  • Ichma-ich callback kodni o’qib bo’lmas qiladi; Promise zanjiri buni tekislaydi.
  • Yangi kod async/await bilan yoziladi, xato try/catch da ushlanadi.
  • util.promisify eski callback funksiyani Promise’ga aylantiradi.
  • Mustaqil amallar Promise.all bilan parallel bajariladi va tartibli massiv qaytaradi.
  • Event loop avval sinxron kodni, keyin Promise’larni, oxirida taymerlarni bajaradi.

Keyingi darsda EventEmitter — hodisaga javob beradigan kod yozish.

Bu dars foydali bo'ldimi?

marta ko'rildi