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.
Parallel — Promise.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:
- Butun sinxron kod;
process.nextTicknavbati;- Promise navbati (
then,awaitdavomi); - 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:
fs.readFileni callback bilan yozing va callback ichiga hamda tashqarisigaconsole.logqo’yib, chiqish tartibini kuzating;- Mavjud bo’lmagan fayl nomini berib,
if (error)shoxi ishlashini ko’ring; - Xuddi shu vazifani
fs.promisesva.then().catch()bilan qayta yozing; - Uchinchi marta
async/awaitvatry/catchbilan yozing, uch variantni yonma-yon qo’yib solishtiring; - Uchta faylni avval ketma-ket
awaitbilan, keyinPromise.allbilan o’qing; Promise.allro’yxatiga mavjud bo’lmagan fayl qo’shib,catchnima 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 xato —
error-firsttartibi. - Ichma-ich callback kodni o’qib bo’lmas qiladi; Promise zanjiri buni tekislaydi.
- Yangi kod
async/awaitbilan yoziladi, xatotry/catchda ushlanadi. util.promisifyeski callback funksiyani Promise’ga aylantiradi.- Mustaqil amallar
Promise.allbilan 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.