fs/promises — async/await bilan fayl tizimi
Oldingi darsda nima o'rgandik?
fs modulining callback uslubini ko'rdik. U ishlaydi, lekin muammo bor.
Callback ning muammosi
Tasavvur qiling: siz faylni o'qib, uni qayta ishlaysiz, natijani boshqa faylga yozasiz. Callback bilan:
fs.readFile('users.json', 'utf8', (err, data) => {
if (err) return console.error(err);
const users = JSON.parse(data);
const filtered = users.filter(u => u.active);
fs.writeFile('active-users.json', JSON.stringify(filtered), 'utf8', (err) => {
if (err) return console.error(err);
fs.readFile('active-users.json', 'utf8', (err, result) => {
if (err) return console.error(err);
console.log('Tayyor:', result);
});
});
});
Bu — Callback Hell. Har bir asinxron operatsiya ichida yana bir callback. Kod o'ngga qarab "uchib ketadi". O'qish qiyin, xatolarni boshqarish qiyin.
fs/promises bu muammoni hal qiladi.
fs/promises nima?
fs/promises — fs modulining zamonaviy versiyasi. Har bir funksiya callback o'rniga Promise qaytaradi. Bu async/await bilan ishlash imkonini beradi.
const fs = require('fs/promises');
// yoki ES Modules da:
import fs from 'fs/promises';
Yuqoridagi Callback Hell ni fs/promises bilan yozamiz:
const fs = require('fs/promises');
async function processUsers() {
const data = await fs.readFile('users.json', 'utf8');
const users = JSON.parse(data);
const filtered = users.filter(u => u.active);
await fs.writeFile('active-users.json', JSON.stringify(filtered, null, 2), 'utf8');
const result = await fs.readFile('active-users.json', 'utf8');
console.log('Tayyor:', result);
}
processUsers();
Bir xil natija, lekin kod yuqoridan pastga o'qiladi — xuddi sinxron kod kabi. Ichma-ich callback yo'q.
Xatolarni boshqarish
Callback da har bir qadamda if (err) return yozish kerak edi. async/await da bir try/catch yetarli:
async function readConfig() {
try {
const data = await fs.readFile('config.json', 'utf8');
return JSON.parse(data);
} catch (err) {
if (err.code === 'ENOENT') {
console.log("Config fayl yo'q, standart sozlamalar ishlatiladi");
return { port: 3000, debug: false };
}
throw err; // boshqa xatolarni yuqoriga uzatamiz
}
}
err.code — xato turi. ENOENT fayl topilmadi degani. Shu kodni tekshirib, har xil xatolarga har xil munosabat ko'rsatish mumkin.
readFile va writeFile
Oldingi darsda ko'rilgan funksiyalar — xuddi shu, faqat callback o'rniga Promise:
const fs = require('fs/promises');
const path = require('path');
async function example() {
// O'qish
const text = await fs.readFile(path.join(__dirname, 'hello.txt'), 'utf8');
console.log(text);
// Yozish
await fs.writeFile(path.join(__dirname, 'output.txt'), 'Yangi kontent', 'utf8');
// Qo'shib yozish
await fs.appendFile(path.join(__dirname, 'log.txt'), 'Yangi qator\n', 'utf8');
}
mkdir, readdir, stat, unlink
Barchasi bir xil — faqat await qo'shiladi:
async function fileOperations() {
// Papka yaratish
await fs.mkdir('uploads', { recursive: true });
// Papka ichini o'qish
const items = await fs.readdir('.');
console.log(items);
// Fayl haqida ma'lumot
const stat = await fs.stat('app.js');
console.log(stat.size, 'bayt');
console.log(stat.isFile());
// Fayl o'chirish
await fs.unlink('temp.txt');
}
Parallel operatsiyalar — Promise.all
async/await ning katta afzalligi — bir nechta mustaqil operatsiyani parallel bajarish mumkin.
Tasavvur qiling: uchta faylni o'qish kerak. Ketma-ket o'qisak:
// Ketma-ket — sekin:
const a = await fs.readFile('a.txt', 'utf8'); // 100ms kutiladi
const b = await fs.readFile('b.txt', 'utf8'); // yana 100ms
const c = await fs.readFile('c.txt', 'utf8'); // yana 100ms
// Jami: ~300ms
Parallel o'qisak:
// Parallel — tez:
const [a, b, c] = await Promise.all([
fs.readFile('a.txt', 'utf8'),
fs.readFile('b.txt', 'utf8'),
fs.readFile('c.txt', 'utf8'),
]);
// Jami: ~100ms ← uchtalasi bir vaqtda o'qildi
Promise.all massiv ichidagi barcha Promise larni bir vaqtda ishga tushiradi va hammasi tugagach natijalarni massiv sifatida qaytaradi. Agar bittasi xato bersa — catch ga tushadi.
Papka ichidagi fayllarni o'qish
Amaliy misol — papka ichidagi barcha .json fayllarni o'qib, birlashtirish:
const fs = require('fs/promises');
const path = require('path');
async function readAllJson(dir) {
const items = await fs.readdir(dir);
// Faqat .json fayllarni olish
const jsonFiles = items.filter(item => path.extname(item) === '.json');
// Hammasini parallel o'qish
const contents = await Promise.all(
jsonFiles.map(file =>
fs.readFile(path.join(dir, file), 'utf8').then(JSON.parse)
)
);
return contents;
}
readAllJson('./data').then(results => {
console.log(`${results.length} ta fayl o'qildi`);
});
Fayl mavjudligini tekshirish
fs/promises da access funksiyasi bor. Fayl mavjud bo'lsa — hech narsa qaytarmaydi. Mavjud bo'lmasa — xato chiqaradi.
async function fileExists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
const exists = await fileExists('config.json');
if (!exists) {
await fs.writeFile('config.json', JSON.stringify({ port: 3000 }, null, 2));
console.log('Config fayl yaratildi');
}
Amaliy misol — JSON "ma'lumotlar bazasi"
Kichik loyihalarda ba'zan to'liq ma'lumotlar bazasi o'rniga JSON fayl ishlatiladi. Mana oddiy CRUD:
const fs = require('fs/promises');
const path = require('path');
const DB_FILE = path.join(__dirname, 'db.json');
async function readDB() {
try {
const data = await fs.readFile(DB_FILE, 'utf8');
return JSON.parse(data);
} catch {
return []; // fayl yo'q bo'lsa — bo'sh massiv
}
}
async function writeDB(data) {
await fs.writeFile(DB_FILE, JSON.stringify(data, null, 2), 'utf8');
}
async function addUser(user) {
const users = await readDB();
users.push({ id: Date.now(), ...user });
await writeDB(users);
return users;
}
async function getUsers() {
return readDB();
}
async function deleteUser(id) {
const users = await readDB();
const filtered = users.filter(u => u.id !== id);
await writeDB(filtered);
return filtered;
}
// Ishlatish:
await addUser({ name: 'Ali', email: 'ali@example.com' });
await addUser({ name: 'Vali', email: 'vali@example.com' });
const users = await getUsers();
console.log(users);
Tez-tez uchraydigan xatolar
await ni async funksiyasiz ishlatish:
// Xato:
const data = await fs.readFile('file.txt', 'utf8');
// SyntaxError: await is only valid in async functions
// To'g'ri:
async function main() {
const data = await fs.readFile('file.txt', 'utf8');
}
await faqat async funksiya ichida ishlaydi. Top-level await faqat ES Modules da ("type": "module") ishlaydi.
try/catch unutish:
// Xato — xato ushlanmaydi:
async function read() {
const data = await fs.readFile('yoq.txt', 'utf8');
// Fayl yo'q bo'lsa — UnhandledPromiseRejection xatosi
}
// To'g'ri:
async function read() {
try {
const data = await fs.readFile('yoq.txt', 'utf8');
} catch (err) {
console.error(err.message);
}
}
Promise.all da bitta xato hammani to'xtatadi:
// Agar bitta fayl yo'q bo'lsa — hammasi xato beradi:
const [a, b, c] = await Promise.all([
fs.readFile('a.txt', 'utf8'),
fs.readFile('yoq.txt', 'utf8'), // bu xato beradi
fs.readFile('c.txt', 'utf8'),
]);
// Catch ga tushadi, a va c ham olinmaydi
// Har birini alohida xato boshqarish uchun — Promise.allSettled:
const results = await Promise.allSettled([
fs.readFile('a.txt', 'utf8'),
fs.readFile('yoq.txt', 'utf8'),
fs.readFile('c.txt', 'utf8'),
]);
results.forEach(r => {
if (r.status === 'fulfilled') console.log(r.value);
else console.error(r.reason.message);
});
Callback vs fs/promises — qachon qaysi?
| Holat | Tavsiya |
|---|---|
| Yangi loyiha | fs/promises |
| Eski kod bilan ishlash | Callback (moslik uchun) |
| Server so'rovlarida | fs/promises — hech qachon Sync |
| Skript, bir martalik ishlar | Sync ham yaxshi |
| Parallel operatsiyalar | fs/promises + Promise.all |
Xulosa
fs/promises — fs modulining zamonaviy versiyasi. Callback o'rniga Promise qaytaradi, async/await bilan ishlaydi. Kod o'qilishi oson, xatolarni boshqarish qulay, parallel operatsiyalar Promise.all bilan samarali.
Yangi loyihalarda har doim fs/promises ishlatiladi. Callback uslubi — eski kod bilan ishlashda yoki asosni tushunish uchun.
Keyingi dars
14-dars: fs Stream — createReadStream, createWriteStream, katta fayllar