Mundarija (31)
- Bu darsda
- 1. Nega bu kerak?
- 2. Uch shakl yonma-yon
- 2.1 Bitta fayl, uch xil o'qish
- 2.2 Ular ichida nima bo'ladi
- 2.3 Qaysi birini qachon
- 3. O'qish: encoding va Buffer
- 3.1 Encoding bermasangiz
- 3.2 Encoding nima va qaysilari bor
- 3.3 JSON faylni o'qish
- 4. Yozish: writeFile va appendFile
- 4.1 writeFile faylni almashtiradi
- 4.2 appendFile oxiriga qo'shadi
- 4.3 Yozish opsiyalari
- 5. Xatolar: ENOENT va boshqalar
- 5.1 Xato obyektida nima bor
- 5.2 Eng ko'p uchraydigan kodlar
- 5.3 Ushlanmagan xato
- 5.4 Naqsh: "yo'q bo'lsa — sukut, boshqa xato — yuqoriga"
- 6. Bir nechta faylni birdan
- 7. Sozlama faylini tekshirish (jonli)
- 8. CLI qadami: sozlama.ts (holat 18, 1-qism)
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): qaysi shakl?
- 2-mashq (o'rta): so'rovlar hisoblagichi
- 3-mashq (qiyin): CSV'dan kurslar
- 4-mashq: CLI qadami
- 11. Real ishda
- Xulosa
- Manbalar
Node.js fs moduli: uch API shakli — sync, callback va promise
Qisqacha:
node:fsdagi deyarli har amal uch shaklda bor: sync (readFileSync— natija kelguncha butun dastur kutadi), callback (readFile(fayl, (xato, matn) => …)— eski uslub) va promise (node:fs/promisesdanawait readFile(…)). Yangi kodda — promise shakli. Encoding bermasangizBuffer(baytlar) qaytadi,"utf8"bersangiz — matn. Fayl yo'q bo'lsa xatocode—"ENOENT": unitry/catchda ushlab, "yo'q bo'lsa sukut qiymat" qilish — eng ko'p uchraydigan naqsh.
Bu darsda
- Bitta amalni uch shaklda yozib, ular qachon to'g'ri ekanini tushuntira olasiz.
readFile,writeFile,appendFilebilan fayl o'qiysiz va yozasiz; encoding vaBufferfarqini bilasiz.- Xato obyektidagi
code,syscall,errnoni o'qib,ENOENTni boshqa xatolardan ajratasiz. valyutauchun ixtiyoriyconfig.jsonni xavfsiz o'qiydigan kodni tushunasiz (holat 18 ning birinchi qismi).
Oldin bilishingiz kerak: Blocking va non-blocking I/O, libuv thread pool, ES Modules Node'da, async/await, try/catch.
1. Nega bu kerak?
Hozircha valyuta har chaqirilganda CBU'dan kursni qaytadan so'raydi va hech narsani eslab qolmaydi. Foydalanuvchi esa ikki narsani xohlaydi. Birinchisi — kurs tez chiqsin, ya'ni oxirgi javob diskda saqlansin (kesh). Ikkinchisi — valyuta kurs deb yozganda har safar USD EUR RUB ni terib o'tirmasin, sevimli valyutalar sozlama faylida tursin.
Ikkalasi ham bitta ko'nikmaga tayanadi: diskdagi faylni o'qish va yozish. Brauzerda buni deyarli qila olmasdingiz — sayt foydalanuvchi diskiga tegolmaydi. Node'da esa bu oddiy ish, uni fs moduli (file system — fayl tizimi) bajaradi.
Lekin fs ni birinchi ochganingizda chalkashib qolasiz: readFile, readFileSync, fs.promises.readFile — uchtasi ham bor. Qaysi biri to'g'ri? Bu darsning javobi: uchalasi ham bitta ishni qiladi, farqi — kutish usulida.
O'xshatish: choyxonada choy buyurtma qilish. Sync — choyxonachi choy damlanguncha sizning stolingiz yonida turadi, boshqa mijozga qaramaydi. Callback — "tayyor bo'lsa, chaqiraman" deydi va keyin chaqiradi. Promise — sizga raqamli chek beradi: chek bilan keyin choyni olasiz, xohlasangiz bir nechta chekni birdan kutasiz.
2. Uch shakl yonma-yon
2.1 Bitta fayl, uch xil o'qish
Avval faylni yaratamiz, keyin uni uch usulda o'qiymiz. Bu blok to'liq Node'da ishlaydi — saytdagi natija oynasi brauzer, unda fs yo'q. O'z kompyuteringizda .mjs fayl qilib ishga tushiring.
import fs from "node:fs";
import { readFile } from "node:fs/promises";
fs.writeFileSync("config.json", '{"valyutalar": ["USD", "EUR"]}');
// 1) sync: natija shu qatorning o'zida
const a = fs.readFileSync("config.json", "utf8");
console.log("sync:", a.length);
// 2) promise: await bilan
const c = await readFile("config.json", "utf8");
console.log("promise:", c.length);Konsolda:
sync: 30
promise: 30Ikkalasi ham 30 belgilik matn qaytardi. Farq ko'rinmaydi, chunki fayl kichik va boshqa ish yo'q. Farq kutish paytida nima bo'lishida.
Callback shaklini alohida ko'ramiz — u natijani darhol emas, keyinroq beradi:
import fs from "node:fs";
fs.writeFileSync("config.json", '{"valyutalar": ["USD", "EUR"]}');
fs.readFile("config.json", "utf8", (xato, matn) => {
if (xato) {
console.log("xato:", xato.code);
return;
}
console.log("callback:", matn.length);
});
console.log("readFile chaqirildi, davom etamiz");Konsolda:
readFile chaqirildi, davom etamiz
callback: 30Qatorlar tartibiga qarang. readFile faylni o'qishni boshlaydi va darhol qaytadi. Kod keyingi qatorga o'tadi. Fayl o'qilgach, Node callback'ni event loop orqali chaqiradi (event loop fazalari).
2.2 Ular ichida nima bo'ladi
Qaysi thread ishlayotganiga qarang. Sync shaklda JS thread'ining o'zi diskni kutadi. Callback va promise shakllarida esa kutishni libuv thread pool'i oladi (thread pool darsi):
sequenceDiagram
participant JS as JS thread
participant P as libuv thread pool
participant D as Disk
JS->>D: readFileSync (JS kutib turadi)
D-->>JS: baytlar
JS->>P: readFile / await readFile
Note over JS: boshqa ishlarni bajaradi
P->>D: o'qish
D-->>P: baytlar
P-->>JS: callback yoki Promise bajarildiBirinchi juftlikda JS thread hech narsa qilmaydi — faqat kutadi. Ikkinchisida u bo'sh, boshqa so'rovlarga xizmat qila oladi.
2.3 Qaysi birini qachon
| Shakl | Import | Qachon to'g'ri |
|---|---|---|
| promise | import { readFile } from "node:fs/promises" |
Yangi kodning deyarli hammasi |
| sync | import { readFileSync } from "node:fs" |
Dastur boshida bir marta, kichik skript, CLI ishga tushishi |
| callback | import { readFile } from "node:fs" |
Eski kod; eng tez (Promise obyekti yo'q), lekin noqulay |
Sync qachon zararli? Server ko'p foydalanuvchiga xizmat qilganda. Bitta so'rovda readFileSync 50 ms kutsa, shu 50 ms davomida hamma foydalanuvchi kutadi (event loop'ni bloklash). CLI'da esa foydalanuvchi bitta, u baribir natijani kutadi. Shuning uchun ba'zi CLI'lar ishga tushishda sync ishlatadi — bu xato emas, ongli tanlov. valyuta esa promise shaklini ishlatadi: kod bir xil uslubda bo'ladi va keyinchalik serverda ham qayta ishlatish mumkin.
node:fs/promises va fs.promises — bitta narsa, ikki xil yo'l:
import fs from "node:fs";
import * as fsp from "node:fs/promises";
console.log(fs.promises.readFile === fsp.readFile); // trueTekshirib ko'ring: CLI dastur ishga tushganda bitta kichik sozlama faylini
readFileSyncbilan o'qiydi. Bu event loop'ni bloklash xatosimi?
Javob
Odatda yo'q. Event loop'ni bloklash zararli, chunki shu paytda boshqa ishlar (so'rovlar, taymerlar) kutib qoladi. CLI ishga tushayotganda boshqa ish yo'q — foydalanuvchi baribir natijani kutadi. Serverda esa har so'rov ichidagi sync o'qish hamma mijozni to'xtatadi.
3. O'qish: encoding va Buffer
3.1 Encoding bermasangiz
readFile ga encoding bermasangiz, u matn emas, Buffer qaytaradi:
import { readFile, writeFile } from "node:fs/promises";
await writeFile("narx.txt", "so'm");
const xom = await readFile("narx.txt");
console.log(xom);
console.log(xom.length);
console.log(xom.toString("utf8"));Konsolda:
<Buffer 73 6f 27 6d>
4
so'mBuffer — xotiradagi xom baytlar ketma-ketligi. 73 — s harfi, 6f — o, 27 — apostrof, 6d — m. Har bayt o'n oltilik (hex) sanoq tizimida ko'rsatilgan. Buffer'ni chuqur Buffer darsida o'rganamiz. Hozir bitta qoidani eslang: matn kerak bo'lsa — encoding bering.
const matn = await readFile("config.json", "utf8");
// yoki obyekt shaklida:
const matn2 = await readFile("config.json", { encoding: "utf8" });3.2 Encoding nima va qaysilari bor
Encoding (kodlash) — baytlarni harflarga aylantirish qoidasi. O'xshatish: Morze alifbosi. Bir xil "nuqta-tire" ketma-ketligini to'g'ri jadval bilan o'qisangiz — so'z, noto'g'risi bilan — bema'ni harflar chiqadi.
| Encoding | Nima uchun |
|---|---|
"utf8" |
Deyarli hamma matn (JSON, CSV, o'zbek, rus harflari) |
"base64" |
Binar ma'lumotni matn sifatida uzatish (rasm, kalit) |
"hex" |
Baytlarni ko'rish, hash'lar |
"latin1" |
Eski fayllar; har belgi — bitta bayt |
Ko'p baytli harfda noto'g'ri encoding qanday buziladi — Kirill harfi bilan ko'ramiz:
import { readFile, writeFile } from "node:fs/promises";
await writeFile("rubl.txt", "рубль");
const togri = await readFile("rubl.txt", "utf8");
const notogri = await readFile("rubl.txt", "latin1");
console.log(togri, togri.length);
console.log(notogri.length, notogri === togri);
console.log((await readFile("rubl.txt")).length);Konsolda:
рубль 5
10 false
105 harf — 10 bayt: UTF-8 da har Kirill harfi 2 bayt. latin1 har baytni alohida harf deb o'qidi: 10 ta "harf" chiqdi, ekranda ular bema'ni belgilar ("krakozyabra") bo'lib ko'rinadi. CBU javobidagi CcyNm_RU maydonini noto'g'ri encoding bilan o'qisangiz, aynan shunday ko'rinadi.
3.3 JSON faylni o'qish
JSON fayl — matn. O'qish ikki qadam: matnni olish, keyin JSON.parse:
import { readFile, writeFile } from "node:fs/promises";
await writeFile("config.json", '{"valyutalar": ["usd", "eur"]}');
const sozlama = JSON.parse(await readFile("config.json", "utf8"));
console.log(sozlama.valyutalar.map((v) => v.toUpperCase()));Konsolda:
[ 'USD', 'EUR' ]ES Modules darsida yana bir yo'lni ko'rgansiz: import paket from "./package.json" with { type: "json" }. Farqi: import faylni dastur ishga tushganda bir marta o'qiydi va natijani keshlaydi. Fayl yo'q bo'lsa — dastur umuman ishga tushmaydi. Foydalanuvchi o'zgartiradigan, bo'lishi ham, bo'lmasligi ham mumkin bo'lgan fayl uchun readFile kerak.
Tekshirib ko'ring:
"so'm"4 bayt edi."o'zbek"necha bayt bo'ladi?"Oʻzbek"(maxsusʻbelgisi bilan) chi?
Javob
"o'zbek" — 6 bayt: hamma belgi lotin va oddiy apostrof, har biri 1 bayt. "Oʻzbek" — 7 bayt: ʻ (U+02BB) belgisi UTF-8 da 2 bayt. Shuning uchun kursda oddiy apostrof ishlatiladi — u hamma joyda bir xil va bitta bayt.
4. Yozish: writeFile va appendFile
4.1 writeFile faylni almashtiradi
writeFile — fayl yo'q bo'lsa yaratadi, bor bo'lsa butunlay almashtiradi:
import { readFile, writeFile } from "node:fs/promises";
await writeFile("kurs.txt", "USD 11903.15\nEUR 13329.15\n");
await writeFile("kurs.txt", "RUB 146.75\n");
console.log(await readFile("kurs.txt", "utf8"));Konsolda:
RUB 146.75Birinchi yozuv yo'qoldi. Bu tuzoq emas, aynan shunday ishlaydi — lekin "nega USD yo'qoldi?" degan savol ko'p beriladi.
4.2 appendFile oxiriga qo'shadi
Log yoki tarix fayli uchun — appendFile:
import { appendFile, readFile, writeFile } from "node:fs/promises";
await writeFile("tarix.log", ""); // har safar toza boshlaymiz
await appendFile("tarix.log", "10:00 kurs USD\n");
await appendFile("tarix.log", "10:05 100 USD\n");
console.log(await readFile("tarix.log", "utf8"));Konsolda:
10:00 kurs USD
10:05 100 USDBirinchi qatordagi writeFile ga e'tibor bering. Usiz dasturni ikkinchi marta ishga tushirsangiz, fayl oldingi qatorlarni ham saqlab qoladi va to'rt qator chiqadi. appendFile — "qo'sh", u hech qachon tozalamaydi.
4.3 Yozish opsiyalari
writeFile uchinchi parametr sifatida obyekt oladi:
await writeFile("kesh.json", matn, {
encoding: "utf8", // sukut ham shu
mode: 0o600, // Linux/macOS: faqat egasi o'qiy va yoza oladi
flag: "w", // sukut: yarat yoki almashtir
});| Flag | Ma'nosi |
|---|---|
"w" |
yozish (sukut): yo'q bo'lsa yaratadi, bor bo'lsa tozalaydi |
"a" |
qo'shish — appendFile ning o'zi |
"wx" |
faqat yangi fayl: bor bo'lsa EEXIST xatosi |
mode: 0o600 — Linux va macOS uchun ruxsat: "faqat fayl egasi". valyuta kesh faylini shu bilan yozadi. Windows bu raqamni deyarli e'tiborsiz qoldiradi — u yerda ruxsatlar boshqa tizimda. "wx" va ruxsatlarni fs chuqur darsida davom ettiramiz.
Tekshirib ko'ring: Har kurs so'rovini
kurslar.logga yozib boramiz. Qaysi biri kerak —writeFileyokiappendFile?
Javob
appendFile — har yangi qator oxiriga qo'shiladi. writeFile bilan har so'rov oldingilarni o'chirib, faqat oxirgisi qolardi.
5. Xatolar: ENOENT va boshqalar
5.1 Xato obyektida nima bor
Yo'q faylni o'qiymiz:
import { readFile } from "node:fs/promises";
try {
await readFile("yoq.json", "utf8");
} catch (xato) {
console.log(xato.code);
console.log(xato.syscall);
console.log(xato instanceof Error);
}Konsolda:
ENOENT
open
truefs xatolarida oddiy message dan tashqari maxsus maydonlar bor:
| Maydon | Qiymat | Ma'nosi |
|---|---|---|
code |
"ENOENT" |
Xato turi — dasturda shunga qarang |
syscall |
"open" |
Qaysi tizim chaqiruvi yiqildi |
path |
to'liq yo'l | Qaysi fayl |
errno |
-4058 (Windows) |
OS raqami — platformaga bog'liq |
Biz Windows'da ishga tushirdik. message va errno shunday chiqdi:
console.log(xato.errno);
console.log(xato.message);-4058
ENOENT: no such file or directory, open 'C:\Users\…\yoq.json'Tarjimasi: "bunday fayl yoki papka yo'q, ochish (open) paytida". Biz nisbiy "yoq.json" bergan edik, xabarda esa to'liq yo'l. errno Linux'da boshqa raqam (hujjatga ko'ra -2; biz Linux'da sinamadik). Shuning uchun qoida: code ni tekshiring — u hamma OS'da bir xil. message va errno ga tayanmang.
ENOENT — "Error NO ENTry", ya'ni "bunday yozuv yo'q". Nomi g'alati, lekin 1970-yillardagi Unix'dan qolgan va hamma joyda shunday.
5.2 Eng ko'p uchraydigan kodlar
code |
Qachon | Odatiy javob |
|---|---|---|
ENOENT |
Fayl yoki papka yo'q | Sukut qiymat yoki "yarating" deb xabar |
EACCES / EPERM |
Ruxsat yo'q | Foydalanuvchiga aniq xabar |
EISDIR |
Faylning o'rniga papka berildi | Yo'lni tekshirish |
EEXIST |
Fayl allaqachon bor ("wx" bilan) |
Boshqa nom |
ENOTDIR |
Yo'l o'rtasidagi qism papka emas | Yo'lni tekshirish |
EMFILE |
Ochiq fayllar juda ko'p | Xavfsiz fayl operatsiyalari darsida |
EISDIR ni ko'ramiz — papkani fayl deb o'qish:
import { readFile } from "node:fs/promises";
try {
await readFile(".", "utf8");
} catch (xato) {
console.log(xato.code, "—", xato.message);
}Konsolda:
EISDIR — EISDIR: illegal operation on a directory, read"Papka ustida noto'g'ri amal, o'qish paytida". Bu safar message da yo'l yo'q — xato open da emas, read da chiqdi.
5.3 Ushlanmagan xato
try/catch siz await readFile yo'q faylda butun dasturni to'xtatadi. Biz ishga tushirdik (izning bir qismi):
import { readFile } from "node:fs/promises";
const matn = await readFile("yoq.json", "utf8");
console.log(matn);Error: ENOENT: no such file or directory, open 'C:\Users\…\yoq.json'
at async open (node:internal/fs/promises:1345:25)
at async readFile (node:internal/fs/promises:1996:14) {
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\…\\yoq.json'
}Node xato obyektining maydonlarini ham { … } ichida chiqardi. console.log(matn) ga navbat kelmadi, jarayon exit kod 1 bilan tugadi (process darsi).
5.4 Naqsh: "yo'q bo'lsa — sukut, boshqa xato — yuqoriga"
Hamma xatoni bir xil ushlash xavfli. Sozlama fayli yo'q — bu normal holat, sukut qiymat bilan davom etamiz. Lekin ruxsat yo'q (EACCES) — bu muammo, uni yashirsak, foydalanuvchi nega sozlamasi ishlamayotganini hech qachon bilmaydi.
import { readFile, writeFile } from "node:fs/promises";
const SUKUT = { valyutalar: ["USD", "EUR", "RUB"] };
async function sozlamaniOqi(fayl) {
let matn;
try {
matn = await readFile(fayl, "utf8");
} catch (xato) {
if (xato.code === "ENOENT") return SUKUT; // yo'q — normal
throw xato; // boshqa xato — yashirmaymiz
}
return JSON.parse(matn);
}
await writeFile("bor.json", '{"valyutalar": ["KZT"]}');
console.log(await sozlamaniOqi("yoq.json"));
console.log(await sozlamaniOqi("bor.json"));Konsolda:
{ valyutalar: [ 'USD', 'EUR', 'RUB' ] }
{ valyutalar: [ 'KZT' ] }catch ichidagi throw xato — qayta tashlash. Biz faqat tanigan xatoni "yutamiz", qolganini yuqoriga uzatamiz.
Diqqat: "Avval tekshir, keyin o'qi" (
existsSynckeyinreadFile) — ko'p uchraydigan, lekin noto'g'ri naqsh. Tekshiruv va o'qish orasida fayl o'chirilishi mumkin — xato baribir chiqadi va kod ikki marta diskka murojaat qiladi. To'g'ri yo'l — darhol o'qish vaENOENTni ushlash. Bu xatoning nomi — TOCTOU (time-of-check to time-of-use: tekshirish va ishlatish vaqti farqi). Uni xavfsiz fayl operatsiyalari darsida chuqurroq ko'ramiz.
Tekshirib ko'ring:
catch (xato) { return SUKUT; }— hamma xatoda sukut qaytaradi. Qaysi holatda bu foydalanuvchini aldaydi?
Javob
Fayl bor, lekin uni o'qishga ruxsat yo'q (EACCES) yoki yo'l papkaga ishora qiladi (EISDIR). Foydalanuvchi sozlama yozgan, lekin dastur indamay sukutni ishlatadi — "sozlamam ishlamayapti" degan sirli muammo paydo bo'ladi. Xato ko'rinsa, sababini darhol topadi.
6. Bir nechta faylni birdan
Promise shaklining katta afzalligi — bir nechta amalni parallel boshlash. Promise kombinatorlari shu yerda ham ishlaydi:
import { readFile, stat, writeFile } from "node:fs/promises";
await writeFile("kurslar.json", '[{"Ccy": "USD"}]');
const [s, matn] = await Promise.all([
stat("kurslar.json"),
readFile("kurslar.json", "utf8"),
]);
console.log(s.size, JSON.parse(matn)[0].Ccy);Konsolda:
16 USDstat — fayl haqida ma'lumot (hajm, o'zgargan vaqt) beradi; u fs chuqur darsida. Ikkala amal bir vaqtda boshlandi va ikkalasi tugaganda await qaytdi. valyuta kesh faylini aynan shunday o'qiydi.
Uzoq o'qishni to'xtatish ham mumkin: readFile signal opsiyasini qabul qiladi (AbortSignal.timeout(1000) — AbortController). Kichik fayllar uchun bu kam kerak bo'ladi.
7. Sozlama faylini tekshirish (jonli)
JSON.parse faqat sintaksisni tekshiradi. {"valyutalar": "USD"} to'g'ri JSON, lekin valyutalar massiv emas. Fayldan kelgan narsaga ishonmaymiz — tarmoqdan kelgan javobga ishonmaganimiz kabi. Tekshiruvchi funksiya — toza JS, unda fs yo'q. Shuning uchun bu blok saytda ham «Ishga tushir» bilan ishlaydi:
const SUKUT = { valyutalar: ["USD", "EUR", "RUB"] };
class SozlamaXatosi extends Error {}
function sozlamaniTekshir(x, fayl) {
if (typeof x !== "object" || x === null || Array.isArray(x)) {
throw new SozlamaXatosi(`${fayl}: obyekt kutilgan`);
}
const { valyutalar = SUKUT.valyutalar } = x;
const togri = Array.isArray(valyutalar) && valyutalar.every(
(v) => typeof v === "string" && /^[A-Za-z]{3}$/.test(v),
);
if (!togri) {
throw new SozlamaXatosi(
`${fayl}: "valyutalar" 3 harfli kodlar massivi bo'lishi kerak`,
);
}
return { valyutalar: valyutalar.map((v) => v.toUpperCase()) };
}
const misollar = [
{},
{ valyutalar: ["usd", "kzt"] },
{ valyutalar: "USD" },
[],
];
for (const x of misollar) {
try {
console.log(sozlamaniTekshir(x, "config.json"));
} catch (xato) {
console.log(xato.message);
}
}Konsolda:
{ valyutalar: [ 'USD', 'EUR', 'RUB' ] }
{ valyutalar: [ 'USD', 'KZT' ] }
config.json: "valyutalar" 3 harfli kodlar massivi bo'lishi kerak
config.json: obyekt kutilganBo'sh obyekt {} — sukut valyutalar (destrukturlashdagi sukut qiymat). Kichik harflar katta harfga aylandi. Qolgan ikkitasi — aniq xabar bilan xato. Saytdagi konsol obyektni biroz boshqacha ko'rsatishi mumkin ({valyutalar: Array(3)}) — ma'nosi bir xil.
8. CLI qadami: sozlama.ts (holat 18, 1-qism)
Holat 18 to'rt darsga bo'lingan: bu dars — fayl o'qish, fs chuqur — stat va glob, xavfsiz fayl operatsiyalari — atomik yozish, path va os — papkalar joyi. Hali commit qilmang — commit to'rtinchi darsda.
Bugun src/sozlama.ts ni yozasiz. Kanondagi o'qish funksiyasi:
export async function sozlamaniOqi(papka: string): Promise<Sozlama> {
const fayl = path.join(papka, "config.json");
let matn: string;
try {
matn = await readFile(fayl, "utf8");
} catch (xato) {
if (xato instanceof Error && "code" in xato && xato.code === "ENOENT") return SUKUT;
throw xato;
}
try {
return sozlamaniTekshir(JSON.parse(matn), fayl);
} catch (xato) {
if (xato instanceof SyntaxError) throw new SozlamaXatosi(`${fayl}: JSON xato — ${xato.message}`);
throw xato;
}
}Biz JS'da yozgan naqshdan ikki farq bor:
- TypeScript'da
catch (xato)—unknown.xato.codeni to'g'ridan o'qib bo'lmaydi, avvalinstanceof Error && "code" in xatobilan toraytiriladi (tip toraytirish). - Ikkita alohida
try. Birinchisi — faylni o'qish xatolari, ikkinchisi — JSON xatosi. Buzilgan JSON (SyntaxError) foydalanuvchiga tushunarliSozlamaXatosiga aylanadi: qaysi fayl, nima xato.cli.tsuni ushlaydi va exit kod 2 bilan chiqadi — xato jim yutilmaydi.
Kanon testlari sozlamaning to'rt holatini tekshiradi: fayl yo'q → sukut, to'g'ri fayl, buzilgan JSON va noto'g'ri tur → xato. Fayl qayerda yotishi (papka) — path va os darsida.
cli.ts da valyuta kurs kodsiz chaqirilsa, sozlamadagi valyutalar olinadi. Holat 18 tugagach kanon natijasi (fixture bilan, config.json yo'q — sukut USD EUR RUB):
$ valyuta kurs
Ogohlantirish: offline rejim — saqlangan namuna kurslar (fixtures/) ishlatildi
2026-10-23 holatiga (fixture)
USD 1 11 903.15 -62.36 AQSH dollari
EUR 1 13 329.15 -108.12 EVRO
RUB 1 146.75 0.79 Rossiya rubli9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
<Buffer 7b 22 …> chiqdi |
Encoding berilmagan | readFile(f, "utf8") |
[object Promise] yoki Promise { <pending> } |
await unutilgan |
await readFile(…) |
Cannot read properties of undefined callback'da |
(matn, xato) — tartib teskari |
Callback'da birinchi — xato |
| Fayl topilmaydi, lekin u bor | Nisbiy yo'l cwd'ga nisbatan | path va os darsidagi import.meta.dirname |
| Oldingi ma'lumot yo'qoldi | writeFile almashtiradi |
Qo'shish — appendFile |
| Hamma xato "sozlama yo'q" deb chiqadi | catch hamma xatoni yutadi |
code === "ENOENT" ni tekshirib, qolganini throw |
require is not defined |
ESM faylda CJS sintaksis | import … from "node:fs" |
Nisbiy yo'l tuzog'i alohida e'tiborga loyiq. readFile("config.json") faylni joriy papka (cwd — terminal turgan papka)dan qidiradi, skript yotgan papkadan emas. valyuta ni boshqa papkadan chaqirsangiz, u boshqa config.json ni qidiradi. Shuning uchun valyuta hech qachon nisbiy yo'l ishlatmaydi — har doim to'liq yo'l quradi.
10. Mashqlar
1-mashq (oson): qaysi shakl?
- Natijani shu qatorning o'zida qaytaradigan shakl:
readFile - Yangi kod uchun tavsiya qilingan modul:
node:fs/ - Encodingsiz
readFileqaytaradigan tur: - Fayl yo'qligini bildiruvchi
code: - Faylni almashtirmay, oxiriga yozadigan funksiya:
Yechim
- Sync —
readFileSync: JS thread kutadi. - promises —
awaitbilan ishlaydi. - Buffer — xom baytlar; matn uchun
"utf8". - ENOENT — "Error NO ENTry".
- appendFile — log va tarix uchun.
2-mashq (o'rta): so'rovlar hisoblagichi
hisobla(fayl) funksiyasini yozing: fayldan sonni o'qiydi, 1 ga oshiradi, qaytadan yozadi va yangi sonni qaytaradi. Fayl yo'q bo'lsa — 0 dan boshlaydi. Boshqa xatolar — yuqoriga. Uch marta chaqiring. Natija 1, 2, 3 bo'lsin (dastur boshida faylni o'chiring: rm(fayl, { force: true }) — force fayl yo'q bo'lsa xato bermaydi).
Yechim
import { readFile, rm, writeFile } from "node:fs/promises";
async function hisobla(fayl) {
let son = 0;
try {
son = Number(await readFile(fayl, "utf8"));
} catch (xato) {
if (xato.code !== "ENOENT") throw xato;
}
son += 1;
await writeFile(fayl, String(son));
return son;
}
await rm("hisob.txt", { force: true });
console.log(await hisobla("hisob.txt"));
console.log(await hisobla("hisob.txt"));
console.log(await hisobla("hisob.txt"));Konsolda:
1
2
3rm bo'lmasa, dasturni ikkinchi marta ishga tushirganda 4 5 6 chiqadi — fayl diskda qoladi. Diskdagi holat dastur tugagach ham yashaydi, bu o'zgaruvchidan asosiy farqi. Yana bir tuzoq: ikki jarayon bir vaqtda hisobla qilsa, ikkalasi ham 3 ni o'qib, 4 yozishi mumkin — bitta hisob yo'qoladi. Bunday "poyga"lar haqida xavfsiz fayl operatsiyalari darsida.
3-mashq (qiyin): CSV'dan kurslar
kurslar.csv faylini yarating (writeFile bilan) va uni o'qib, har valyuta uchun "USD: 11 903.15" ko'rinishidagi qatorni chiqaring. Fayl:
kod,kurs
USD,11903.15
EUR,13329.15
RUB,146.75Talablar: promise shakli; sarlavha qatorini tashlab yuboring; bo'sh qatorlarni e'tiborsiz qoldiring; sonni ming bo'yicha bo'sh joy bilan yozing (toLocaleString emas — satr metodlari va regex bilan). Fayl yo'q bo'lsa — "kurslar.csv topilmadi" deb chiqaring.
Yechim
import { readFile, writeFile } from "node:fs/promises";
const sonniYoz = (n) => {
const [butun, kasr] = n.toFixed(2).split(".");
return `${butun.replace(/\B(?=(\d{3})+(?!\d))/g, " ")}.${kasr}`;
};
async function kurslarniChiqar(fayl) {
let matn;
try {
matn = await readFile(fayl, "utf8");
} catch (xato) {
if (xato.code === "ENOENT") {
console.log(`${fayl} topilmadi`);
return;
}
throw xato;
}
const qatorlar = matn.split("\n").slice(1).filter((q) => q.trim());
for (const q of qatorlar) {
const [kod, kurs] = q.split(",");
console.log(`${kod}: ${sonniYoz(Number(kurs))}`);
}
}
const csv = "kod,kurs\nUSD,11903.15\nEUR,13329.15\nRUB,146.75\n\n";
await writeFile("kurslar.csv", csv);
await kurslarniChiqar("kurslar.csv");
await kurslarniChiqar("boshqa.csv");Konsolda:
USD: 11 903.15
EUR: 13 329.15
RUB: 146.75
boshqa.csv topilmadiFayl oxiridagi ikkita \n bo'sh qatorlar beradi — filter ularni olib tashladi. Windows'da yaratilgan CSV'da qatorlar \r\n bilan tugashi mumkin, unda kurs oxirida \r qoladi. Number("146.75\r") baribir son beradi, lekin matn sifatida ishlatsangiz muammo bo'ladi. Bu CRLF tuzog'i — path va os darsida.
4-mashq: CLI qadami
src/sozlama.ts ni yozing: SUKUT, SozlamaXatosi, sozlamaniTekshir va sozlamaniOqi («CLI qadami» bo'limidagi kod). Hozircha papka sifatida vaqtinchalik process.cwd() bering. Uch holatni qo'lda sinang: fayl yo'q, to'g'ri config.json, buzilgan JSON ({"valyutalar": [). Commit qilmang.
Yechim
Buzilgan JSON uchun kutilgan xabar boshlanishi: config.json: JSON xato — … va JSON.parse ning o'z xabari (masalan Unexpected end of JSON input). npm run tip va npm run lint toza bo'lsin. papka ni to'g'ri joyga — OS sozlama papkasiga — path va os darsida ulaymiz.
11. Real ishda
- Konfiguratsiya fayllari. ESLint, Prettier, TypeScript, Vite — hammasi ishga tushganda
fsbilan sozlama faylini qidiradi va o'qiydi. "Yo'q bo'lsa — sukut" naqshi aynan shu asboblarda. - Server. Express yoki NestJS serverida fayl o'qish — faqat promise shakli. Sync shakl faqat ishga tushishda (sertifikat, sozlama) ishlatiladi. Serverni keyingi qismda yozamiz.
- Intervyu savoli: "
readFileSyncvareadFilefarqi nima va qachon qaysi biri?" — javob event loop orqali. - Xato kodlari. Log yoki Sentry'da
ENOENT,EACCES,EMFILEni ko'rganda ma'nosini darhol bilish — tajribali backend dasturchining belgisi.
Xulosa
fsdagi har amal uch shaklda: sync (kutadi), callback (eski), promise (node:fs/promises— yangi kod uchun).- Encoding bermasangiz —
Buffer; matn uchun"utf8". Noto'g'ri encoding — "krakozyabra". writeFilealmashtiradi,appendFileqo'shadi;flag,mode— uchinchi parametrda.- Xatoda
codega qarang (ENOENT,EACCES,EISDIR): u har OS'da bir xil,messagevaerrnoesa yo'q. - "Yo'q bo'lsa — sukut, boshqa xato —
throw" va "tekshirmay o'qi, xatoni ushla" — fayl kodining asosiy naqshlari.
Keyingi dars: fs chuqur: kataloglar, stat, glob va watch — papka yaratish, aylanib chiqish, fayl hajmi va yoshi, valyuta kesh buyrug'i.
Manbalar
- Node.js 24 hujjati — File system (Promises API, Callback API, Synchronous API, File system flags)
- Node.js 24 hujjati — Errors: Common system errors
- Node.js 24 hujjati — Buffers and character encodings
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!