Node.js oqimlar (stream) asoslari
fs darsida bitta ogohlantirish qolgan
edi: readFile faylni butunlay xotiraga yuklaydi. Fayl 30 KB bo’lsa
muammo yo’q. 2 GB bo’lsa — dastur xotira yetmasligidan yiqiladi. Yechim:
faylni bir zumda emas, bo’lak-bo’lak o’qish.
Muammoni ko’rsatamiz
Katta fayl yaratamiz. make-big.js:
const fs = require("fs");
let text = "";
for (let i = 1; i <= 200000; i++) {
text += "Qator " + i + "\n";
}
fs.writeFileSync("big.txt", text, "utf8");
console.log("Hajmi:", fs.statSync("big.txt").size, "bayt");
node make-big.js
Hajmi: 2488895 bayt
Ikki yarim megabaytlik fayl tayyor.
createReadStream — o’qish oqimi
read-stream.js:
const fs = require("fs");
const readStream = fs.createReadStream("big.txt", "utf8");
let chunkCount = 0;
let totalLength = 0;
readStream.on("data", (chunk) => {
chunkCount += 1;
totalLength += chunk.length;
});
readStream.on("end", () => {
console.log("Bo'laklar soni:", chunkCount);
console.log("Jami belgilar:", totalLength);
});
readStream.on("error", (error) => {
console.log("Xato:", error.code);
});
node read-stream.js
Bo'laklar soni: 38
Jami belgilar: 2488895
Fayl 38 ta bo’lakda o’qildi. Har bo’lak kelganda data hodisasi
e’lon qilindi, hammasi tugagach — end.
Tanish ko’rinyaptimi? Bu EventEmitter darsidagi
on va hodisalarning o’zi. Oqimlar EventEmitter’dan meros olgan.
Uch asosiy hodisa
| Hodisa | Qachon | Nima keladi |
|---|---|---|
data |
Navbatdagi bo’lak tayyor | Bo’lakning o’zi |
end |
Fayl tugadi | Hech nima |
error |
Xato yuz berdi | Error obyekti |
Bo’lak hajmi
Oqim bo’lak hajmini o’zi tanlaydi — standart qiymat 64 KB. Uni o’zgartirish mumkin:
const fs = require("fs");
const readStream = fs.createReadStream("big.txt", {
encoding: "utf8",
highWaterMark: 16 * 1024,
});
let chunkCount = 0;
readStream.on("data", () => {
chunkCount += 1;
});
readStream.on("end", () => {
console.log("16 KB bo'lak bilan:", chunkCount);
});
16 KB bo'lak bilan: 152
Bo’lak kichraydi — soni ko’paydi. highWaterMark bir vaqtda xotirada
turadigan hajmni belgilaydi.
Asosiy yutuq: xotira cheklangan qoladi
Farqni to’g’ri tushunish muhim.
readFileSync bilan o’qiganda xotira sarfi fayl hajmiga qarab
o’sadi: 2 MB fayl 2 MB, 2 GB fayl 2 GB talab qiladi. Kichik faylda
buni sezmaysiz:
const fs = require("fs");
const before = process.memoryUsage().heapUsed;
const content = fs.readFileSync("big.txt", "utf8");
const after = process.memoryUsage().heapUsed;
console.log("Qatorlar:", content.split("\n").length - 1);
console.log("Xotira o'sishi (MB):", ((after - before) / 1024 / 1024).toFixed(1));
Qatorlar: 200000
Xotira o'sishi (MB): 2.4
Oqimda esa xotirada bir vaqtda faqat bitta bo’lak turadi — fayl qanchalik katta bo’lmasin:
const fs = require("fs");
const readStream = fs.createReadStream("big.txt", "utf8");
let lineCount = 0;
let rest = "";
readStream.on("data", (chunk) => {
// Bo'lak qator o'rtasida uzilishi mumkin — oxirgi bo'lagini saqlaymiz
const parts = (rest + chunk).split("\n");
rest = parts.pop();
lineCount += parts.length;
});
readStream.on("end", () => {
if (rest.length > 0) {
lineCount += 1;
}
console.log("Qatorlar:", lineCount);
});
readStream.on("error", (error) => {
console.log("Xato:", error.code);
});
Qatorlar: 200000
createWriteStream — yozish oqimi
Yozishda ham xuddi shunday mantiq:
const fs = require("fs");
const writeStream = fs.createWriteStream("output.txt");
for (let i = 1; i <= 5; i++) {
writeStream.write(`Qator ${i}\n`);
}
// Yozish tugaganini bildiramiz
writeStream.end();
writeStream.on("finish", () => {
console.log("Yozib bo'lindi");
});
Yozib bo'lindi
output.txt ichida besh qator turadi.
pipe — ikki oqimni ulash
Faylni nusxalash uchun o’qib, keyin yozish kerak. pipe buni bitta
qatorga jamlaydi:
const fs = require("fs");
const readStream = fs.createReadStream("big.txt");
const writeStream = fs.createWriteStream("copy.txt");
readStream.pipe(writeStream);
writeStream.on("finish", () => {
console.log("Nusxa tayyor, hajmi:", fs.statSync("copy.txt").size);
});
readStream.on("error", (error) => {
console.log("O'qishda xato:", error.code);
});
Nusxa tayyor, hajmi: 2488895
Bu yerda encoding berilmadi — nusxalashda matnga aylantirish keraksiz.
Baytlar qanday kelsa, shunday yoziladi.
Backpressure — to’lib ketishdan himoya
O’qish tezligi yozish tezligidan yuqori bo’lishi mumkin: disk sekin, tarmoq band. Unda bo’laklar qayerga to’planadi?
pipe buni o’zi boshqaradi — shuning uchun nusxalashda pipe
tavsiya etiladi.
Qo’lda yozganda esa write() qaytargan qiymatga qarash kerak:
const fs = require("fs");
const readStream = fs.createReadStream("big.txt");
const writeStream = fs.createWriteStream("manual-copy.txt");
readStream.on("data", (chunk) => {
const canContinue = writeStream.write(chunk);
if (!canContinue) {
// Bufer to'ldi — o'qishni to'xtatamiz
readStream.pause();
}
});
// Bufer bo'shadi — davom etamiz
writeStream.on("drain", () => {
readStream.resume();
});
readStream.on("end", () => {
writeStream.end();
});
writeStream.on("finish", () => {
console.log("Qo'lda nusxalash tugadi");
});
readStream.on("error", (error) => {
console.log("Xato:", error.code);
});
Qo'lda nusxalash tugadi
write() false qaytarsa, bufer to’lgan. pause() o’qishni
to’xtatadi, drain hodisasidan keyin resume() davom ettiradi.
Oqimlar yana qayerda uchraydi
Oqim faqat fayl uchun emas. Node.js’da ko’p narsa oqim:
| Oqim | Turi |
|---|---|
process.stdout |
Yozish — terminalga chiqarish |
process.stdin |
O’qish — terminaldan kiritish |
| HTTP so’rov va javob | O’qish va yozish |
console.log aslida process.stdout ga yozadi:
process.stdout.write("Yangi qatorsiz matn");
process.stdout.write(" — davomi shu yerda\n");
Yangi qatorsiz matn — davomi shu yerda
Farqi: console.log oxiriga o’zi yangi qator qo’shadi, write esa
qo’shmaydi.
Keyingi darsdagi HTTP javobi ham yozish oqimi — response.end()
chaqirig’i aynan shundan.
Amaliy mashq
node-start papkasida bajaring:
make-big.jsbilan katta fayl yaratib, hajministatSyncorqali chiqaring;createReadStreambilan o’qib, bo’laklar sonini va jami belgilar sonini sanang;highWaterMarkni 16 KB qilib qayta o’qing va bo’laklar soni o’zgarganini ko’ring;- Mavjud bo’lmagan fayl uchun oqim ochib
errorhodisasini ushlang, so’ng tinglovchini o’chirib dastur yiqilishini kuzating; createWriteStreambilan besh qator yozing vafinishhodisasida xabar chiqaring;pipebilan faylni nusxalang va nusxa hajmi asl fayl hajmiga tengligini tekshiring.
4-qadam eng muhimi: oqimlarda error tinglovchisining yo’qligi eng ko’p
uchraydigan va eng kutilmagan yiqilish sababi.
Qisqacha xulosa
- Oqim ma’lumotni bo’lak-bo’lak uzatadi: butun fayl xotiraga yuklanmaydi.
- Oqim EventEmitter’dan meros olgan —
data,endvaerrorhodisalari bilan ishlaydi. - Har oqimga
errortinglovchisi qo’yiladi, aks holda dastur yiqiladi. highWaterMarkbir bo’lak hajmini belgilaydi, standarti 64 KB.- Oqimning yutug’i — xotira sarfi fayl hajmiga qarab o’smaydi.
pipeikki oqimni ulaydi va backpressure ni o’zi boshqaradi.process.stdout,process.stdinva HTTP javobi ham oqim.
Keyingi darsda http moduli — tashqi paketsiz server yozish.