fs/promises — async/await bilan fayl tizimi

~15 daq · Node.js · Core Modullar

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