9-bo‘lim

Oqimlar va buferlar

Buffer nima, o'qish va yozish oqimlari, pipeline bilan ulash, orqa bosim, Transform oqimi va oqimlarni asinxron takrorlash.

🕑 10 daqiqa o‘qish 📄 528 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Buffer
  2. Oqim bilan o'qish
  3. Qatorlab o'qish
  4. Yozish oqimi
  5. pipeline - eng to'g'ri usul
  6. Transform - oqim ichida o'zgartirish
  7. Orqa bosim
  8. Nima uchun oqim
  9. Xulosa

5-bo'limda readFile butun faylni xotiraga yuklashini aytgan edik. Oqim (stream) esa uni bo'laklab o'qiydi - xotirada har safar faqat bitta bo'lak turadi.

Buffer #

Oqimlar bo'laklarni Buffer sifatida beradi. Bu - xom baytlar to'plami.

JavaScript
const bufer = Buffer.from("Salom", "utf8");

console.log("Baytlar:", bufer.length);
console.log("Birinchi bayt:", bufer[0]);
console.log("Matn:", bufer.toString("utf8"));
console.log("O'n oltilik:", bufer.toString("hex"));
console.log("Buffer mi:", Buffer.isBuffer(bufer));

const emoji = Buffer.from("🐹", "utf8");
console.log("Emoji baytlari:", emoji.length);
Natija
Baytlar: 5
Birinchi bayt: 83
Matn: Salom
O'n oltilik: 53616c6f6d
Buffer mi: true
Emoji baytlari: 4

Buffer - Uint8Array ning kengaytmasi. U matn emas, baytlar bilan ishlaydi, shuning uchun emoji to'rt bayt egallaydi.

Oqim bilan o'qish #

JavaScript
import { createReadStream, createWriteStream } from "node:fs";
import { writeFile, rm } from "node:fs/promises";

await writeFile("katta.txt", "qator\n".repeat(1000), "utf8");

const oqim = createReadStream("katta.txt", { encoding: "utf8" });

let bolaklar = 0;
let belgilar = 0;

for await (const bolak of oqim) {
    bolaklar++;
    belgilar += bolak.length;
}

console.log("Belgilar:", belgilar);
console.log("Bo'laklar bittadan ko'p yoki teng:", bolaklar >= 1);

await rm("katta.txt");
Natija
Belgilar: 6000
Bo'laklar bittadan ko'p yoki teng: true
Bo'laklar soniga tayanmang

Yuqorida bolaklar ning aniq soni chiqarilmadi - faqat taqqoslash.

Sababi: Node faylni necha bo'lakka bo'lishini kafolatlamaydi. U odatiy bufer hajmiga (64 KB), fayl tizimiga va operatsion tizimga bog'liq. Bir kompyuterda bir bo'lak, boshqasida uchta bo'lishi mumkin.

Shu sababdan oqim bilan ishlaganda hech qachon bir bo'lak butun bir qator yoki butun bir yozuvni o'z ichiga oladi deb o'ylamang. Qatorlarga bo'lish kerak bo'lsa, readline modulini ishlating - u buni to'g'ri hal qiladi.

Qatorlab o'qish #

JavaScript
import { createReadStream } from "node:fs";
import { createInterface } from "node:readline";
import { writeFile, rm } from "node:fs/promises";

await writeFile("sonlar.txt", "10\n20\n30\n40\n", "utf8");

const oquvchi = createInterface({
    input: createReadStream("sonlar.txt", { encoding: "utf8" }),
    crlfDelay: Infinity,
});

let jami = 0;
let qatorlar = 0;

for await (const qator of oquvchi) {
    if (qator.trim() === "") continue;
    jami += Number(qator);
    qatorlar++;
}

console.log("Qatorlar:", qatorlar);
console.log("Yig'indi:", jami);

await rm("sonlar.txt");
Natija
Qatorlar: 4
Yig'indi: 100

readline bo'lak chegaralarini o'zi hisobga oladi va har safar to'liq qator beradi.

crlfDelay: Infinity - Windows dagi \r\n ni to'g'ri hal qiladi.

Yozish oqimi #

JavaScript
import { createWriteStream } from "node:fs";
import { readFile, rm } from "node:fs/promises";
import { once } from "node:events";

const yozuvchi = createWriteStream("hisobot.txt", { encoding: "utf8" });

yozuvchi.write("Sarlavha\n");
for (let i = 1; i <= 3; i++) {
    yozuvchi.write(`${i}-qator\n`);
}
yozuvchi.end();

await once(yozuvchi, "finish");

const mazmun = await readFile("hisobot.txt", "utf8");
console.log(mazmun.trimEnd());

await rm("hisobot.txt");
Natija
Sarlavha
1-qator
2-qator
3-qator

end() oqimni yopadi, finish hodisasi esa hamma ma'lumot yozilganini bildiradi.

pipeline - eng to'g'ri usul #

JavaScript
import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";
import { writeFile, readFile, rm } from "node:fs/promises";

await writeFile("manba.txt", "ko'chiriladigan matn", "utf8");

await pipeline(
    createReadStream("manba.txt"),
    createWriteStream("nusxa.txt"),
);

console.log(await readFile("nusxa.txt", "utf8"));
console.log("ko'chirildi");

await rm("manba.txt");
await rm("nusxa.txt");
Natija
ko'chiriladigan matn
ko'chirildi
pipe emas, pipeline ishlating

Eski qo'llanmalarda .pipe() metodi ko'p uchraydi:

JavaScript
oqim.pipe(yozuvchi);

Muammo: pipe xatolarni uzatmaydi. O'qish oqimida xato yuz bersa, yozuvchi oqim ochiq qolib ketadi va fayl tavsiflovchisi oqib ketadi.

pipeline esa:

  1. Xatolarni to'g'ri uzatadi;
  2. Xato bo'lganda hamma oqimni yopadi;
  3. node:stream/promises dan olinganda await bilan ishlaydi.

Yangi kodda doim pipeline.

Transform - oqim ichida o'zgartirish #

JavaScript
import { Readable, Transform } from "node:stream";
import { pipeline } from "node:stream/promises";

const kattaHarf = new Transform({
    transform(bolak, kodlash, keyingi) {
        keyingi(null, bolak.toString().toUpperCase());
    },
});

const natijalar = [];

await pipeline(
    Readable.from(["salom ", "dunyo ", "node"]),
    kattaHarf,
    async function* (manba) {
        for await (const bolak of manba) {
            natijalar.push(bolak.toString());
            yield bolak;
        }
    },
    async function (manba) {
        for await (const _ of manba) {
            // oxirgi bosqich - hech narsa qilmaydi
        }
    },
);

console.log(natijalar.join(""));
Natija
SALOM DUNYO NODE

Transform - o'qish va yozishni birlashtirgan oqim: u ma'lumotni qabul qiladi, o'zgartiradi va keyingisiga uzatadi.

Orqa bosim #

Tez manba, sekin qabul qiluvchi Disk tez o'qiydi Bufer to'lib boradi Tarmoq sekin yozadi "sekinroq!" - orqa bosim Bufer to'lganda oqim manbaga to'xtashni aytadi, bo'shaganda esa yana davom ettiradi. pipeline buni o'zi hal qiladi; qo'lda yozsangiz - o'zingiz
JavaScript
import { createWriteStream } from "node:fs";
import { rm } from "node:fs/promises";
import { once } from "node:events";

// highWaterMark - bufer hajmi. Odatda 64 KB, bu yerda esa
// orqa bosimni ko'rsatish uchun ataylab kichik qilingan.
const yozuvchi = createWriteStream("bosim.txt", { highWaterMark: 1024 });

let tosildi = false;

for (let i = 0; i < 2000; i++) {
    const boshJoy = yozuvchi.write(`${i}-qator\n`);
    if (!boshJoy) {
        tosildi = true;
        await once(yozuvchi, "drain");
    }
}

yozuvchi.end();
await once(yozuvchi, "finish");

console.log("Bufer to'lgan payt bo'ldimi:", tosildi);
console.log("Yozib tugatildi");

await rm("bosim.txt");
Natija
Bufer to'lgan payt bo'ldimi: true
Yozib tugatildi

highWaterMark - buferning chegarasi. Odatiy qiymat 64 KB bo'lgani uchun kichik faylda orqa bosim umuman sezilmaydi; yuqoridagi misolda u ataylab 1 KB ga tushirilgan.

write() qaytargan qiymatni e'tiborsiz qoldirmang

yozuvchi.write(...) false qaytarishi mumkin. Bu "buferim to'ldi, biroz kuting" degani.

Uni e'tiborsiz qoldirib yozishda davom etsangiz, ma'lumot xotirada to'plana boradi. Katta fayl yoki tez manba bo'lsa, jarayon xotirasi tugaydi.

Yuqoridagi misolda drain hodisasi kutilmoqda - u bufer bo'shaganini bildiradi.

Amalda buni qo'lda yozish shart emas: pipeline orqa bosimni o'zi boshqaradi. Qo'lda yozish faqat maxsus holatlarda kerak.

Nima uchun oqim #

Yondashuv2 GB fayl uchun
readFile2 GB xotira - jarayon o'ladi
OqimBir necha yuz kilobayt

Oqimlar veb-serverda ham muhim: faylni foydalanuvchiga yuborishda uni butunlay xotiraga yuklash shart emas - diskdan o'qib, darhol tarmoqqa uzatish mumkin.

Buni 10-bo'limda ko'ramiz.

Amaliy topshiriq
  1. Buffer.from bilan matnni baytlarga aylantiring.
  2. Emoji nechta bayt egallashini tekshiring.
  3. createReadStream bilan faylni bo'laklab o'qing.
  4. Bo'laklar sonini chiqarib, bir necha marta ishga tushiring.
  5. readline bilan qatorlab o'qing.
  6. createWriteStream bilan fayl yozing.
  7. pipeline bilan faylni nusxalang.
  8. Transform yozib, matnni katta harfga o'tkazing.
  9. write() qaytargan qiymatni tekshiring.
  10. drain hodisasini kutib ko'ring.

Xulosa #

  • Buffer - xom baytlar; u Uint8Array ning kengaytmasi.
  • Oqim faylni bo'laklab o'qiydi - xotirada faqat bitta bo'lak turadi.
  • Bo'laklar soniga va chegarasiga tayanmang - ular kafolatlanmaydi.
  • Qatorlar kerak bo'lsa - readline.
  • .pipe() xatolarni uzatmaydi; doim pipeline ishlating.
  • Transform - o'qish va yozishni birlashtirgan oqim.
  • write() false qaytarsa, bufer to'lgan: drain ni kuting.
  • Orqa bosimni pipeline o'zi boshqaradi.

Keyingi bo'limda HTTP serverni o'rganamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.