Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. Oqimdan qatorlar: createInterface
- 2.1 Bo'lak va qator bir xil emas
- 2.2 Ikki API: callback va promise
- 3. Terminal: stdin, stdout va TTY
- 3.1 Uchta standart oqim
- 3.2 TTY yoki quvur?
- 4. Savol berish: rl.question
- 4.1 Oddiy savol-javob
- 4.2 Biz topgan tuzoq: quvurda javob yo'qoladi
- 4.3 Tuzatish: iterator orqali so'rash
- 5. Interaktiv terminal imkoniyatlari
- 6. Faylni qatorma-qator o'qish
- 7. CLI qadami: valyuta i — interaktiv rejim (holat 23)
- 7.1 Avval mantiqni ajratamiz
- 7.2 src/interaktiv.ts
- 7.3 Testlar
- 7.4 Haqiqiy chiqish
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): savdo kundaligi
- 3-mashq (qiyin): quvurga chidamli test-viktorina
- 4-mashq: CLI qadami — interaktiv rejim
- 10. Real ishda
- Xulosa
- Manbalar
Node.js readline: terminalda savol-javob va faylni qatorma-qator o'qish
Qisqacha:
node:readline— baytlar oqimini qatorlarga bo'ladigan modul. Uning ikki vazifasi bor: foydalanuvchidan terminalda so'rash (rl.question) va istalgan oqimni — faylni,stdinni — qatorma-qator o'qish (for await (const qator of rl)). Dastur ikki xil muhitda ishga tushadi: odam yozayotgan terminal (TTY) va boshqa dasturdan keladigan quvur (pipe). Biz o'lchadik: quvurdarl.questionikkinchi javobni yo'qotadi,for awaitesa hech narsani yo'qotmaydi.
Bu darsda
createInterfacebilan oqimni qatorlarga bo'lasiz vafor awaitbilan o'qiysiz.readline/promisesdagirl.questionbilan savol berasiz va uning quvurdagi tuzog'ini bilasiz.process.stdin.isTTYbilan "odam yozyaptimi yoki quvurmi" ni aniqlaysiz.- Taklif (prompt), tarix va Tab bilan to'ldirish (
completer) qanday sozlanishini ko'rasiz. valyutaga interaktiv rejim qo'shasiz:valyuta i.
Oldin bilishingiz kerak: fs: uch API shakli, process obyekti, Oqimlar, pipe va yo'naltirish, Asinxron iteratsiya.
1. Nega bu kerak?
zlib darsigacha valyuta bir martalik buyruqlar bilan ishladi: valyuta 100 usd — javob — dastur tugadi. Endi tasavvur qiling: Jasur aka bozorda turibdi va ketma-ket beshta summani hisoblamoqchi. Har safar valyuta ni qaytadan ishga tushirsa, har safar CBU'ga so'rov ketadi (yoki kesh o'qiladi) va har safar ~80 ms ishga tushish vaqti sarflanadi.
Qulayrog'i — interaktiv rejim: dastur bir marta ishga tushadi, kurslarni bir marta oladi, keyin valyuta> taklifini chiqarib, qator kutib turadi. Bu Python'ning >>> yoki node ning o'z REPL'iga o'xshaydi.
Ikkinchi vazifa ham qatorlar bilan bog'liq. 40 MB lik CSV faylni readFile bilan butunlay o'qib, split("\n") qilsangiz — butun fayl xotirada turadi. Qatorma-qator o'qisangiz — bir vaqtda faqat bitta qator. Ikkalasini ham readline qiladi.
O'xshatish: readline — kassadagi navbat boshqaruvchisi. Odamlar to'da bo'lib keladi (baytlar bo'lak-bo'lak keladi), u ularni bittalab kassaga o'tkazadi (bitta qator — bitta hodisa).
2. Oqimdan qatorlar: createInterface
2.1 Bo'lak va qator bir xil emas
Oqim (oqim (stream) — brauzerda ko'rgansiz) ma'lumotni bo'laklar bilan beradi. Bo'lak chegarasi qator chegarasiga to'g'ri kelmaydi: "lag'mon" so'zi ikki bo'lakka bo'linib kelishi mumkin. readline bo'laklarni yig'ib, faqat to'liq qatorlarni beradi.
Quyidagi misolda oqimni qo'lda yasaymiz: Readable.from([...]) — massivdagi har element bitta bo'lak. Stream'larni Stream nima darsida to'liq o'rganamiz; hozir u "bo'laklar beradigan manba" deb tushuning.
import { createInterface } from "node:readline/promises";
import { Readable } from "node:stream";
// 2 ta bo'lak: "lag'mon" ikkiga bo'lingan, Windows qator oxiri bor
const input = Readable.from(["osh\nlag'", "mon\r\nmanti"]);
const rl = createInterface({ input, crlfDelay: Infinity });
let n = 0;
for await (const line of rl) {
n++;
console.log(`${n}: ${JSON.stringify(line)}`);
}
console.log("tugadi");Konsolda:
1: "osh"
2: "lag'mon"
3: "manti"
tugadiUchta narsaga qarang:
- "lag'mon" bitta qator bo'lib keldi — bo'lak chegarasi ko'rinmaydi.
\r\n(Windows qator oxiri) qatorga kirmadi:"lag'mon","lag'mon\r"emas.- Oxirgi "manti" dan keyin
\nyo'q, lekin u ham qator sifatida keldi. Oqim tugagach,for awaitsikli o'zi to'xtadi.
crlfDelay: Infinity — \r va \n orasida qancha vaqt o'tsa ham, ularni bitta qator oxiri deb hisoblash. Sukut bo'yicha 100 ms. Fayl o'qiyotganda Node hujjati aynan Infinity ni tavsiya qiladi: katta faylda \r bir bo'lak oxirida, \n keyingisining boshida kelishi mumkin.
2.2 Ikki API: callback va promise
readline ning ikki kirish nuqtasi bor:
| Modul | Uslub | Savol berish |
|---|---|---|
node:readline |
hodisalar va callback (rl.on("line", …)) |
rl.question(matn, callback) |
node:readline/promises |
await |
await rl.question(matn) |
Ikkalasida ham createInterface bir xil, for await ikkalasida ishlaydi. Callback uslubini eski kodda ko'p uchratasiz:
import { createInterface } from "node:readline";
import { Readable } from "node:stream";
const input = Readable.from(["100 usd\n1 eur\n"]);
const rl = createInterface({ input });
rl.on("line", (line) => console.log("qator:", line));
rl.on("close", () => console.log("yopildi"));
console.log("tinglovchilar qo'yildi");Konsolda:
tinglovchilar qo'yildi
qator: 100 usd
qator: 1 eur
yopildiBirinchi bo'lib oxirgi qator chiqdi — "line" hodisalari keyinroq, oqim ma'lumot bergach keladi. rl.on(...) — bu hodisa tinglovchisi: brauzerdagi addEventListener ning Node'dagi o'xshashi. Uni EventEmitter asoslari darsida chuqur ko'ramiz. "close" — kirish oqimi tugaganda yoki rl.close() chaqirilganda.
Tekshirib ko'ring: Kirish
Readable.from(["a\nb", "\nc\n"])bo'lsa,for awaitnechta qator beradi va qaysilar?
Javob
Uchta: "a", "b", "c". "b" birinchi bo'lak oxirida, uning \n i keyingi bo'lak boshida — readline ularni birlashtiradi. Oxirgi \n dan keyin hech narsa yo'q, shuning uchun bo'sh to'rtinchi qator chiqmaydi.
3. Terminal: stdin, stdout va TTY
3.1 Uchta standart oqim
process obyekti darsida process.argv va process.env ni ko'rgansiz. Har jarayonda yana uchta oqim bor (Oqimlar, pipe va yo'naltirish darsidagi stdin, stdout, stderr — Node'da ular obyekt):
| Obyekt | Yo'nalish | Odatda |
|---|---|---|
process.stdin |
dasturga kiradi | klaviatura yoki quvur |
process.stdout |
dasturdan chiqadi | natija |
process.stderr |
dasturdan chiqadi | xato va ogohlantirish |
console.log — aslida process.stdout.write(matn + "\n"). valyuta natijani stdout'ga, "Ogohlantirish: offline rejim" ni stderr'ga yozadi — shuning uchun valyuta kurs > kurs.txt faylga faqat jadval tushadi.
3.2 TTY yoki quvur?
TTY (teletype — terminal qurilmasi) — odam bilan bevosita gaplashayotgan terminal oynasi. U klaviaturadan harf-harf oladi, rang va kursor harakatini tushunadi. Quvur (pipe) — boshqa dastur chiqishi: printf "100 usd\n" | valyuta i. Quvurda odam yo'q, faqat tayyor matn bor.
Node buni isTTY xususiyati bilan aytadi. Biz ikkala holatni ham ishga tushirdik (Node 24.21.0, Windows 11, Git Bash):
echo x | node -p "process.stdin.isTTY"
node -p "process.stdout.isTTY" | catundefined
undefinedBirinchi buyruqda stdin — quvur, ikkinchisida stdout — quvur (| cat). Oddiy terminal oynasida ikkalasi true bo'ladi (Node hujjati bo'yicha). Bizning o'lchov muhitimizda haqiqiy terminal oynasi yo'q edi, shuning uchun true holatini o'zingiz tekshiring: node -p "process.stdin.isTTY".
E'tibor bering: quvurda false emas, undefined. Shuning uchun tekshiruv if (process.stdin.isTTY) yoki === true bilan yoziladi.
Nega bu muhim? Chunki yaxshi CLI ikkala holatda ham to'g'ri ishlaydi:
flowchart TD
S["valyuta i ishga tushdi"] --> T{"chiqish TTY mi?"}
T -->|ha| A["salomlashuv, valyuta> taklifi<br/>Tab, tarix"]
T -->|yo'q| B["taklifsiz: har qator — buyruq"]
A --> L["for await (qator of rl)"]
B --> L
L --> E{"xato qator bo'ldimi?"}
E -->|TTY| K["davom etadi, exit 0"]
E -->|quvur| X["oxirida exit 1"]Diagrammada ikki yo'l bitta siklga qo'shiladi: farq faqat bezakda va exit kodida. Quvurga valyuta> taklifini yozish — natija fayliga axlat qo'shish degani. Quvurda xato bo'lsa exit 1 — skript buni bilsin (Exit code).
Tekshirib ko'ring:
valyuta kurs | grep USDbuyrug'idavalyutauchun qaysi oqim TTY, qaysi biri emas?
Javob
stdout — quvur (grep ga ketadi), shuning uchun process.stdout.isTTY — undefined. stdin va stderr hali ham terminal — ular TTY. Shu sababli ogohlantirishlar (stderr) ekranda ko'rinadi, jadval esa grep ga ketadi.
4. Savol berish: rl.question
4.1 Oddiy savol-javob
readline/promises dagi rl.question(matn) — matnni chiqaradi va bitta qator javobni kutadi:
// savol.mjs
import { createInterface } from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
const rl = createInterface({ input, output });
const name = await rl.question("Ismingiz? ");
const age = await rl.question("Yoshingiz? ");
console.log(`Salom, ${name}! Siz ${age} yoshdasiz.`);
rl.close();Terminalda ishlatsangiz — hammasi kutilgandek: "Ismingiz?" chiqadi, siz yozasiz, Enter, keyingi savol. rl.close() muhim: usiz stdin ochiq qoladi va dastur terminalda tugamaydi (Node hujjati).
4.2 Biz topgan tuzoq: quvurda javob yo'qoladi
Endi xuddi shu faylga javoblarni quvur bilan beramiz — skriptlar va testlar aynan shunday qiladi. Biz ishga tushirdik (Node 24.21.0, Git Bash, Windows 11):
printf "Ali\n25\n" | node savol.mjs
echo $?Ismingiz? Yoshingiz? Warning: Detected unsettled top-level await at file:///C:/Users/…/savol.mjs:6
const age = await rl.question("Yoshingiz? ");
^
13Tarjimasi: "Ogohlantirish: tugallanmagan top-level await aniqlandi". Ikkinchi savolga javob kelmadi, dastur exit kod 13 bilan tugadi. "25" qayerga ketdi?
Sababi — vaqt. Quvur ikkala qatorni bitta bo'lakda birdaniga beradi. readline "Ali" ni birinchi savolga beradi, "25" ni ham darhol "line" hodisasi sifatida chiqaradi. Lekin o'sha paytda ikkinchi savol hali berilmagan: await keyingi qatorga o'tguncha "25" o'tib ketdi. Keyin oqim tugadi, rl yopildi, ikkinchi await hech qachon javob olmaydi. Node bunday osilib qolgan modulni 13-kod bilan to'xtatadi. stdin o'rniga Readable.from bersangiz, ikkinchi question darhol ERR_USE_AFTER_CLOSE xatosini tashlaydi (readline was closed — "readline yopilgan").
Terminalda bu ko'rinmaydi: odam sekin yozadi, har qator alohida keladi. Shuning uchun tuzoq odatda faqat avtomatik test yoki skriptda chiqadi.
4.3 Tuzatish: iterator orqali so'rash
for await qatorlarni yo'qotmaydi, chunki u ichida navbat saqlaydi. Savollar uchun ham shu iteratorni qo'lda ishlatamiz: rl[Symbol.asyncIterator]() (Asinxron iteratsiya) va har savolda next():
import { createInterface } from "node:readline/promises";
import { Readable, PassThrough } from "node:stream";
// test uchun: kirish — "quvur", chiqish — xotiradagi oqim
const input = Readable.from(["Ali\n25\n"]);
const output = new PassThrough();
const rl = createInterface({ input, output });
const lines = rl[Symbol.asyncIterator]();
async function ask(text) {
output.write(text);
const { value, done } = await lines.next();
return done ? "" : value;
}
const name = await ask("Ismingiz? ");
const age = await ask("Yoshingiz? ");
rl.close();
console.log(`Salom, ${name}! Siz ${age} yoshdasiz.`);Konsolda:
Salom, Ali! Siz 25 yoshdasiz.Ikkala javob ham joyida. Bu usul terminalda ham ishlaydi. done — oqim tugagan (Ctrl+D bosilgan yoki quvur tugagan) holat: javob o'rniga bo'sh satr qaytaramiz.
PassThrough — kirganini o'zgarishsiz chiqaradigan oqim. Testda process.stdout o'rniga uni beramiz va yozilganini o'zgaruvchiga yig'amiz. valyuta testlari aynan shunday qiladi (pastda).
Diqqat:
rl.questionnifor await (const qator of rl)bilan bittarlda aralashtirmang. Ikkalasi bir xil qatorlarga da'vo qiladi va qaysi biri olishi vaqtga bog'liq bo'lib qoladi.
5. Interaktiv terminal imkoniyatlari
createInterface ga terminal: true (yoki output TTY bo'lsa — avtomatik) bo'lsa, readline odam uchun qulayliklar qo'shadi:
| Sozlama yoki metod | Nima qiladi |
|---|---|
rl.setPrompt("valyuta> "), rl.prompt() |
taklif matnini chiqaradi |
historySize: 100 |
↑/↓ bilan oldingi buyruqlarni qaytarish |
completer: (qator) => [variantlar, qism] |
Tab bosilganda to'ldirish |
rl.on("SIGINT", …) |
Ctrl+C ni o'zingiz ushlash |
readline.cursorTo, readline.clearLine |
kursorni ko'chirish, qatorni tozalash |
Completer (to'ldiruvchi) — siz yozgan qatorni olib, ikki elementli massiv qaytaradigan funksiya: mos variantlar va to'ldirilayotgan qism. valyuta da u oxirgi so'zni katta harfga o'tkazib, valyuta kodlari ichidan qidiradi: 100 us + Tab → USD.
Ctrl+D — "kirish tugadi" (EOF) belgisi: for await sikli tugaydi. Ctrl+C — readline uni "SIGINT" hodisasiga aylantiradi; tinglovchi bo'lmasa, rl yopiladi. Signallar haqida process obyekti darsida gaplashgansiz.
cursorTo va clearLine "yuklanmoqda 45%" kabi bir joyda yangilanadigan qator uchun kerak. Ularni Interaktiv CLI darsida spinner bilan ishlatamiz.
Diqqat: Bu bo'limdagi TTY imkoniyatlari (taklif, Tab, tarix, Ctrl+C) bizning o'lchov muhitimizda sinalmagan: agentda haqiqiy terminal oynasi yo'q edi. Kod yo'li Node hujjatiga mos yozilgan; o'z terminalingizda albatta tekshiring.
6. Faylni qatorma-qator o'qish
Ikkinchi vazifa — katta fayl. createReadStream faylni bo'laklab o'qiydi, readline qatorlarga bo'ladi:
import { createReadStream } from "node:fs";
import { createInterface } from "node:readline";
const rl = createInterface({
input: createReadStream("kurslar.csv"),
crlfDelay: Infinity,
});
let count = 0;
for await (const line of rl) {
if (line.startsWith("USD,")) count++;
}
console.log(`USD qatorlari: ${count}`);Fayl qanchalik katta bo'lmasin, xotirada bir vaqtda faqat bitta bo'lak (sukut bo'yicha 64 KB) va joriy qator turadi. Kanon o'lchovi: 41 MB, 1 mln qatorli CSV — shu usulda eng ko'p 63 MB xotira, butun faylni o'qib split qilganda — 194 MB. Lekin stream usuli tezroq emas: 796 ms va 567 ms. Batafsil — Katta faylni stream bilan qayta ishlash darsida.
createReadStream ni fs chuqur darsida tilga olgandik. U ham oqim — keyingi darslarda uning ichini ochamiz.
7. CLI qadami: valyuta i — interaktiv rejim (holat 23)
7.1 Avval mantiqni ajratamiz
Interaktiv rejim valyuta 100 usd bilan aynan bir xil buyruqlarni bajarishi kerak. Mantiq cli.ts ichida edi. Uni interaktiv.ts dan chaqirsak, cli.ts → interaktiv.ts → cli.ts aylanma import paydo bo'lardi (CommonJS ichidan darsidagi circular mavzusi). Shuning uchun kanon mantiqni yangi faylga ko'chirdi:
src/buyruq.ts—bajar(sozlar, kontekst):kurs …yoki<miqdor> <KOD> [KOD]→ natija matni. Kirish-chiqish yo'q, faqat hisob.FoydalanishXatosivaKontekstturi ham shu yerda.cli.ts— endi bitta qator:yoz(bajar(positionals, kontekst)).
Bu "funksional yadro" g'oyasi: hisob alohida, terminal alohida. Shunda bitta bajar ni ikki joydan chaqirish mumkin.
7.2 src/interaktiv.ts
Kanon faylining asosiy qismi (kod aynan, ba'zi qatorlar 70 belgidan uzun):
import { createInterface } from "node:readline/promises";
import { FoydalanishXatosi, bajar, type Kontekst } from "./buyruq.ts";
export async function interaktiv(k: Kontekst, kirish: NodeJS.ReadableStream, chiqish: NodeJS.WritableStream & { isTTY?: boolean }): Promise<number> {
const tty = chiqish.isTTY === true;
const kodlar = ["UZS", ...k.kurslar.map((x) => x.kod)];
const rl = createInterface({
input: kirish,
output: chiqish,
terminal: tty,
historySize: 100,
completer: (qator: string): [string[], string] => {
const oxirgi = qator.split(/\s+/).at(-1)?.toUpperCase() ?? "";
const mos = kodlar.filter((kod) => kod.startsWith(oxirgi));
return [mos.length ? mos : kodlar, oxirgi];
},
});
// … yoz(), salomlashuv (faqat tty)
rl.setPrompt("valyuta> ");
if (tty) rl.prompt();
let xatolar = 0;
for await (const qator of rl) {
const sozlar = qator.trim().split(/\s+/).filter(Boolean);
const [birinchi] = sozlar;
if (birinchi === "chiqish" || birinchi === "exit") break;
if (birinchi === "yordam" || birinchi === "?") yoz(QISQA_YORDAM);
else if (birinchi !== undefined) {
try {
yoz(bajar(sozlar, k));
} catch (xato) {
if (!(xato instanceof FoydalanishXatosi)) throw xato;
xatolar++;
yoz(`Xato: ${xato.message}`);
}
}
if (tty) rl.prompt();
}
rl.close();
// quvurda xato qator bo'lsa — skript buni exit kod orqali bilsin
return !tty && xatolar > 0 ? 1 : 0;
}Asosiy qarorlar:
kirishvachiqishparametr. Funksiyaprocess.stdinni o'zi olmaydi — testda boshqa oqim berish uchun.for await,rl.questionemas — «Biz topgan tuzoq» bo'limidagi sabab bilan, quvurdagi har qator saqlanadi.- Xato qator rejimni to'xtatmaydi —
Xato: Noma'lum valyuta: xyzchiqadi va keyingi qator kutiladi. Kutilmagan xato (FoydalanishXatosiemas) esa yuqoriga uchadi — uni yashirmaymiz. cli.tsda:if (birinchi === "interaktiv" || birinchi === "i") return await interaktiv(kontekst, process.stdin, process.stdout);va yordam matnida yangi qator.
7.3 Testlar
test/interaktiv.test.ts da uchta yangi test (jami 30). Ikkitasi funksiyani to'g'ridan chaqiradi — kirish Readable.from, chiqish PassThrough:
async function yurgiz(qatorlar: string[]): Promise<{ kod: number; chiqish: string }> {
const chiqish = new PassThrough();
let matn = "";
chiqish.on("data", (b: Buffer) => (matn += b.toString()));
const kod = await interaktiv(k, Readable.from(qatorlar.map((q) => q + "\n")), chiqish);
return { kod, chiqish: matn };
}
test("quvur rejimi: har qator — buyruq, taklif (prompt) yo'q", async () => {
const r = await yurgiz(["100 usd", "", "1 eur usd", "chiqish", "100 usd"]);
assert.equal(r.kod, 0);
assert.equal(r.chiqish, "100.00 USD = 1 190 315.00 UZS\n1.00 EUR = 1.12 USD\n");
});Bo'sh qator e'tiborsiz qoldi, chiqish dan keyingi "100 usd" bajarilmadi. Uchinchi test valyuta i ni haqiqiy jarayon sifatida ishga tushirib, stdin'ga "10 usd\n" yozadi — execFile bilan. Jarayonni koddan ishga tushirishni ikki darsdan keyin, child_process darsida ko'rasiz.
7.4 Haqiqiy chiqish
Kanon quvur orqali ishga tushirdi (natijalar/23-interaktiv.txt):
printf "100 usd\nkurs gbp jpy\n1 xyz\nyordam\nchiqish\n" | valyuta i --offlineOgohlantirish: offline rejim — saqlangan namuna kurslar (fixtures/) ishlatildi
100.00 USD = 1 190 315.00 UZS
2026-10-23 holatiga (fixture)
GBP 1 15 916.89 -112.11 Angliya funt sterlingi
JPY 1 75.81 0.03 Yaponiya iyenasi
Xato: Noma'lum valyuta: xyz
Buyruqlar: "100 usd", "100 usd eur", "kurs", "kurs gbp jpy", "yordam", "chiqish" (yoki Ctrl+D). Tab — valyuta kodini to'ldiradi.
[exit 1]valyuta> taklifi va salomlashuv yo'q — chiqish TTY emas. 1 xyz xatosidan keyin ham yordam bajarildi. Oxirida exit 1 — chunki bitta xato qator bo'ldi.
Tekshiruv: tip va lint 0, testlar 30/30, paket 44 fayl, 20.2 kB, sovuq start 79 ms. TTY rejimi (taklif, Tab, tarix) — kanon muhitida sinalmagan; o'z terminalingizda valyuta i ni ishga tushirib ko'ring.
Commit: Interaktiv rejim: valyuta i — readline, Tab bilan valyuta kodi, quvurda ham ishlaydi
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Dastur tugamaydi, kursor miltillaydi | rl.close() chaqirilmagan, stdin ochiq |
Ish tugagach rl.close() |
Warning: Detected unsettled top-level await, exit 13 |
Quvurda rl.question javobni o'tkazib yubordi |
for await yoki iteratorning next() |
Error [ERR_USE_AFTER_CLOSE]: readline was closed |
Oqim tugagach yana question |
done ni tekshiring, close dan keyin so'ramang |
Qator oxirida ko'rinmas \r ("usd\r") |
Windows \r\n, crlfDelay qisqa yoki qo'lda split("\n") |
crlfDelay: Infinity; qo'lda bo'lsangiz /\r?\n/ |
Natija faylida valyuta> va ranglar |
Taklif quvurga yozilgan | isTTY === true bo'lgandagina |
| Test osilib qoladi | Testda process.stdin o'qilyapti |
Kirish/chiqishni parametr qiling |
9. Mashqlar
Mashqlar kurs/mashqlar/24/23-readline/ da. Bloklarni o'z terminalingizda node fayl.mjs bilan ishga tushiring.
1-mashq (oson): bo'shliqlarni to'ldiring
- Oqimni qatorlarga bo'ladigan funksiya —
- Windows qator oxirini to'g'ri birlashtirish sozlamasi —
crlfDelay: - Quvurda
process.stdin.isTTYqiymati — awaitbilan savol berish uchun modul —node:readline/- Kirishni tugatadigan tugmalar — Ctrl+
Yechim
- createInterface —
readlinening asosiy funksiyasi. - Infinity —
\rva\nqancha kechiksa ham bitta qator oxiri. - undefined —
falseemas; shuning uchun=== truebilan tekshiramiz. - promises —
node:readline/promises. - D — Ctrl+D EOF yuboradi (Windows
cmdda Ctrl+Z, keyin Enter).
2-mashq (o'rta): savdo kundaligi
Readable.from bilan Jasur akaning kunlik savdosi berilgan: har qator taom,narx. readline va for await bilan har taom bo'yicha jami summani hisoblang. Bo'sh qatorlarni o'tkazib yuboring. Kirish: ["osh,35000\nmanti,30", "000\n\nosh,35000\r\nko'k choy,5000\n"].
Yechim
import { createInterface } from "node:readline/promises";
import { Readable } from "node:stream";
const input = Readable.from([
"osh,35000\nmanti,30",
"000\n\nosh,35000\r\nko'k choy,5000\n",
]);
const rl = createInterface({ input, crlfDelay: Infinity });
const totals = new Map();
for await (const line of rl) {
if (!line.trim()) continue;
const [dish, price] = line.split(",");
totals.set(dish, (totals.get(dish) ?? 0) + Number(price));
}
for (const [dish, sum] of totals) console.log(`${dish}: ${sum}`);Konsolda:
osh: 70000
manti: 30000
ko'k choy: 5000"30" va "000" ikki bo'lakda keldi — readline ularni birlashtirdi. \r\n dan keyingi "osh" toza keldi, shuning uchun Map da ikkita "osh" emas, bitta.
3-mashq (qiyin): quvurga chidamli test-viktorina
Uch savolli viktorina yozing: savollar massivi { q, a }, javob katta-kichik harfga qaramay solishtiriladi. Kirish va chiqish — parametr (quiz(input, output)), natija — to'g'ri javoblar soni. rl.question ishlatmang (nega — «Biz topgan tuzoq» bo'limida). Javoblar quvurdagidek bitta bo'lakda keladi: "Toshkent\n5\nNODE\n". Javob kelmasa (oqim tugasa) — qolgan savollar noto'g'ri hisoblanadi.
Yechim
import { createInterface } from "node:readline/promises";
import { Readable, PassThrough } from "node:stream";
const QUESTIONS = [
{ q: "O'zbekiston poytaxti? ", a: "toshkent" },
{ q: "2 + 3 = ? ", a: "5" },
{ q: "JS'ni serverda bajaradigan runtime? ", a: "node" },
];
async function quiz(input, output) {
const rl = createInterface({ input, output, terminal: false });
const lines = rl[Symbol.asyncIterator]();
let correct = 0;
for (const { q, a } of QUESTIONS) {
output.write(q);
const { value, done } = await lines.next();
if (done) break;
if (value.trim().toLowerCase() === a) correct++;
}
rl.close();
return correct;
}
const out = new PassThrough();
let screen = "";
out.on("data", (b) => (screen += b));
const full = await quiz(Readable.from(["Toshkent\n5\nNODE\n"]), out);
const halfInput = Readable.from(["toshkent\n"]);
const half = await quiz(halfInput, new PassThrough());
console.log(`to'liq: ${full}/3, yarim: ${half}/3`);
console.log(screen.split("? ").length - 1, "ta savol chiqdi");Konsolda:
to'liq: 3/3, yarim: 1/3
3 ta savol chiqditerminal: false — chiqish PassThrough bo'lgani uchun aniq aytdik (TTY emas). Ikkinchi chaqiruvda oqim birinchi javobdan keyin tugadi: done — true, sikl to'xtadi, dastur osilmadi.
4-mashq: CLI qadami — interaktiv rejim
O'z valyuta ingizda «CLI qadami» bo'limidagi qadamni bajaring: buyruq.ts ga mantiqni ko'chiring, interaktiv.ts ni yozing, cli.ts ga i buyrug'ini qo'shing, uchta testni yozing. Keyin tekshiring:
printf "100 usd\n1 xyz\nchiqish\n" | node src/bin.ts i --offline
echo "exit $?"
npm run tip && npm run lint && npm testYechim
Kutilgan: 100.00 USD = 1 190 315.00 UZS, Xato: Noma'lum valyuta: xyz, exit 1; testlar 30/30. Keyin terminalda node src/bin.ts i — valyuta> taklifi chiqishi, 100 us + Tab USD ga to'ldirishi, ↑ oldingi buyruqni qaytarishi kerak. Hammasi o'tsa — yuqoridagi commit xabari bilan commit qiling.
10. Real ishda
- REPL'lar —
node,psql,redis-cli, Prisma Studio'ning terminal qismlari: hammasireadlineyoki uning ustidagi kutubxonalar. - Log tahlili — server loglari gigabaytlab bo'ladi.
createReadStream+readline+for await— Node'da eng oddiy "qatorma-qator" ishlov. - TTY va quvur farqi — professional CLI'lar (git, npm) TTY'da rang va progress chiqaradi, quvurda — toza matn.
isTTYtekshiruvi har yaxshi CLI'da bor. - Interaktiv savollar —
npm init,create-vitesavollari. Ular@clack/promptsyokiinquirerkabi kutubxonalarda; Interaktiv CLI darsida ularni o'lchab taqqoslaymiz. - Intervyuda: "Katta faylni qanday o'qiysiz?" —
readFileemas, stream +readline; "CLI'ni skriptdan qanday test qilasiz?" — kirish/chiqishni parametr qilish.
Xulosa
createInterface({ input })oqimni qatorlarga bo'ladi; bo'lak chegarasi muhim emas,crlfDelay: Infinity—\r\nuchun.for await (const qator of rl)— qatorlarni o'qishning eng ishonchli yo'li; oqim tugasa sikl o'zi tugaydi.rl.questionterminalda qulay, lekin quvurda javobni yo'qotadi (biz o'lchadik: exit 13) — iteratorningnext()bilan yozing.process.stdin.isTTY/process.stdout.isTTY: terminaldatrue, quvurdaundefined.- TTY'da taklif, Tab (
completer), tarix; quvurda — toza matn va xato bo'lsa exit 1.
Keyingi dars: timers va timers/promises chuqur — await qilinadigan kutish, AbortSignal.timeout va valyuta ga qayta urinish.
Manbalar
- Node.js hujjati — Readline (
createInterface,crlfDelay,completer,readline/promises, "Example: Read file stream line-by-Line"): https://nodejs.org/docs/latest-v24.x/api/readline.html - Node.js hujjati — TTY (
isTTY): https://nodejs.org/docs/latest-v24.x/api/tty.html - Node.js hujjati — Process (
process.stdin, exit kod 13 — unsettled top-level await): https://nodejs.org/docs/latest-v24.x/api/process.html#exit-codes - Kanon:
valyutaholat 23 (buyruq.ts,interaktiv.ts,interaktiv.test.ts),natijalar/23-interaktiv.txt,tekshiruv-23.txt; quvurdagirl.questiontuzog'i — biz ishga tushirdik (Node 24.21.0, Windows 11, 2026-10-23).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!