
Chào các "cú đêm" Gen Z! Creyt đây, hôm nay chúng ta sẽ cùng "mổ xẻ" một "thám tử" cực kỳ "cool ngầu" trong thế giới Node.js: fs.promises.readFile().
fs.promises.readFile() là gì? Để làm gì?
Thử tưởng tượng thế này nhé: Bạn có một "kho báu" thông tin được cất giấu trong một "cuốn sách thần kỳ" (file) trên máy tính. Nhiệm vụ của bạn là đọc toàn bộ nội dung cuốn sách đó. Hồi xưa, các "pháp sư" Node.js thường phải dùng phép thuật "callback" để đọc từng trang một, rồi chờ đợi mòn mỏi, đôi khi còn lạc vào "mê cung callback hell" rối rắm.
Nhưng với fs.promises.readFile(), mọi chuyện trở nên "EZ game" hơn nhiều! Nó giống như một "thám tử siêu năng lực" có khả năng "quét" toàn bộ cuốn sách đó trong tích tắc, gom tất cả thông tin lại thành một "gói" duy nhất, rồi "báo cáo" cho bạn một lần duy nhất khi đã xong xuôi. Và đặc biệt hơn, anh chàng thám tử này làm việc "bất đồng bộ" (asynchronous) - nghĩa là trong lúc anh ta đang "quét sách", bạn vẫn có thể "lướt TikTok" hoặc làm những việc khác mà ứng dụng của bạn không hề "đứng hình" chờ đợi.
Nói tóm lại, fs.promises.readFile() là cách "hiện đại", "sành điệu" để đọc toàn bộ nội dung của một file vào bộ nhớ (RAM) trong Node.js, sử dụng cú pháp Promise "mượt mà" (kết hợp với async/await là "hết nước chấm"). Nó trả về một Promise, một "lời hứa" rằng "tôi sẽ trả về dữ liệu khi tôi đọc xong, hoặc tôi sẽ báo lỗi nếu có gì đó sai".
Code Ví Dụ Minh Họa Rõ Ràng
Để "thực chiến" với fs.promises.readFile(), đầu tiên bạn cần có một file để đọc. Hãy tạo một file tên là du_lieu_quan_trong.txt với nội dung bất kỳ (ví dụ: Chào các Gen Z! Đây là bài học của thầy Creyt về fs.promises.readFile.).
# Trong terminal, tạo file này
echo "Chào các Gen Z! Đây là bài học của thầy Creyt về fs.promises.readFile." > du_lieu_quan_trong.txt
Giờ thì chúng ta sẽ viết code Node.js để đọc file này:
// Bước 1: Import module fs.promises
// Nhớ là .promises nhé, đây là phiên bản trả về Promise!
const fs = require('fs').promises;
async function docFileThanToc() {
try {
console.log("Thám tử Creyt đang bắt đầu đọc file...");
// Bước 2: Gọi fs.readFile() với async/await
// Tham số thứ hai là encoding, 'utf8' là phổ biến nhất cho văn bản tiếng Việt/Anh
const noiDungFile = await fs.readFile('du_lieu_quan_trong.txt', { encoding: 'utf8' });
console.log("Thám tử Creyt đã đọc xong! Đây là nội dung:");
console.log(noiDungFile);
// Bước 3: Thử đọc một file không tồn tại để xem cách xử lý lỗi
console.log("\nThử đọc file không tồn tại để xem lỗi nó như nào nhé...");
const fileMa = await fs.readFile('file_khong_ton_tai.txt', { encoding: 'utf8' });
console.log(fileMa); // Dòng này sẽ không bao giờ chạy nếu file không có
} catch (error) {
// Bước 4: Xử lý lỗi nếu có
console.error("Ối! Thám tử Creyt gặp trục trặc khi đọc file rồi:", error.message);
if (error.code === 'ENOENT') {
console.error("Có vẻ như file bạn muốn đọc không hề tồn tại. Kiểm tra lại đường dẫn nhé!");
}
} finally {
// Bước 5: Luôn chạy sau cùng, dù thành công hay thất bại
console.log("\nCông việc đọc file đã kết thúc, dù thành công hay thất bại!");
}
}
// Chạy hàm đọc file
docFileThanToc();
// --- Cú pháp với .then().catch() cũng rất "ổn áp" nhé! ---
console.log("\n--- Dùng .then().catch() thì sao? ---");
fs.readFile('du_lieu_quan_trong.txt', 'utf8')
.then(data => {
console.log("Đã đọc xong bằng .then().catch():");
console.log(data);
})
.catch(err => {
console.error("Lỗi rồi bạn ơi (từ .then().catch()):", err.message);
});

Mẹo (Best Practices) Để Ghi Nhớ và Dùng Thực Tế
-
Luôn Luôn Bắt Lỗi (Error Handling): Trong thế giới lập trình, không phải lúc nào mọi thứ cũng "thuận buồm xuôi gió". File có thể không tồn tại, bạn không có quyền đọc, hoặc file bị hỏng. Hãy luôn dùng
try...catchvớiasync/awaithoặc.catch()vớiPromiseđể "bọc" code của bạn. Đây là "tấm khiên" bảo vệ ứng dụng khỏi "sập nguồn" bất ngờ. -
Chỉ Định Encoding "Chuẩn Chỉ": File văn bản đâu phải lúc nào cũng là tiếng Anh. Để tránh "dấu hỏi" hay "ô vuông" khó hiểu xuất hiện trong nội dung đọc được, hãy luôn nói rõ bạn muốn đọc file với encoding nào (ví dụ:
'utf8'là "quốc dân" cho văn bản đa ngôn ngữ). Nếu không chỉ định,readFilesẽ trả về mộtBuffer(dạng chuỗi byte), lúc đó bạn lại phải tự decode, hơi "rối não" đấy.
-
Cẩn Thận Với File "Khổng Lồ":
fs.promises.readFile()sẽ "ôm" toàn bộ nội dung file vào bộ nhớ (RAM) của ứng dụng. Hãy tưởng tượng bạn đang cố gắng nhét cả một "thư viện quốc gia" vào một cái "balo học sinh" vậy. Nếu bạn đọc một file vài GB, RAM của bạn sẽ "kêu gào thảm thiết" và ứng dụng có thể "sập". Lúc đó, hãy nghĩ đếnfs.createReadStream()- nó giống như việc "đọc từng trang một" thay vì "ôm cả cuốn sách", tiết kiệm bộ nhớ hơn rất nhiều. -
Ưu Tiên
fs.promises: Nếu bạn đang code một ứng dụng Node.js mới toanh hoặc đang "tân trang" lại code cũ, hãy luôn dùng phiên bảnpromisescủafsthay vì phiên bản callback truyền thống. Code của bạn sẽ "sáng sủa", "dễ thở" và dễ bảo trì hơn rất nhiều.
Ví Dụ Thực Tế Các Ứng Dụng/Website Đã Ứng Dụng
fs.promises.readFile() là một "công cụ" cơ bản nhưng cực kỳ quyền năng, được sử dụng trong vô số tình huống thực tế:
- Đọc file cấu hình (
.env,config.json): Hầu hết các ứng dụng cần đọc các cài đặt ban đầu (như kết nối database, API keys) từ các file cấu hình nhỏ.readFilelà lựa chọn lý tưởng cho các file này. - Phục vụ nội dung tĩnh đơn giản: Nếu website của bạn có các trang HTML, CSS, JS nhỏ được lưu trữ dưới dạng file, bạn có thể dùng
readFileđể đọc chúng và gửi về trình duyệt. - Tải template (mẫu): Khi bạn dùng các template engine (như EJS, Handlebars, Pug), chúng thường dùng
readFileđể đọc các file template.ejshay.hbstrước khi render ra HTML cuối cùng. - Đọc dữ liệu từ JSON/CSV nhỏ: Các file dữ liệu nhỏ dùng để lưu trữ thông tin không cần database phức tạp (ví dụ: danh sách các quốc gia, dữ liệu mock cho phát triển) cũng là một case lý tưởng để dùng
readFile.
Thử Nghiệm Đã Từng và Hướng Dẫn Nên Dùng Cho Case Nào
Creyt đã từng "kinh qua" rất nhiều dự án, và đây là kinh nghiệm "xương máu" khi dùng fs.promises.readFile():
-
Nên dùng khi:
- Kích thước file nhỏ đến trung bình: (vài KB đến vài MB). Đây là "sân chơi" của
readFile. Đọc các file cấu hình, file markdown, file log nhỏ, file ảnh thumbnail là những case hoàn hảo. - Bạn cần toàn bộ nội dung file để xử lý một lần: Ví dụ, bạn cần parse một file JSON thành object, biên dịch một template HTML, hoặc nén/giải nén một file zip nhỏ.
readFilesẽ "gom" hết dữ liệu rồi mới giao cho bạn xử lý. - Bạn muốn code bất đồng bộ gọn gàng: Với
async/await, code của bạn sẽ trông như đang đọc file một cách đồng bộ mà không hề block ứng dụng. "Đẹp trai" và "thông minh"!
- Kích thước file nhỏ đến trung bình: (vài KB đến vài MB). Đây là "sân chơi" của
-
Không nên dùng khi:
- File có kích thước rất lớn: (vài chục MB trở lên, đến vài GB). Như đã nói ở trên, nó sẽ "ngốn" RAM và có thể làm crash ứng dụng của bạn. Lúc đó, hãy chuyển sang dùng
fs.createReadStream()để xử lý file theo từng đoạn (chunk) một, giống như bạn đọc một cuốn sách dày theo từng chương vậy, không cần nhét cả cuốn vào đầu một lúc. - Bạn chỉ cần đọc một phần của file: Nếu bạn chỉ muốn đọc vài dòng đầu hoặc một đoạn cụ thể trong file,
readFilekhông phải là lựa chọn tối ưu vì nó vẫn đọc toàn bộ file. Trong trường hợp này,fs.createReadStream()hoặc các thư viện chuyên biệt có thể hiệu quả hơn.
- File có kích thước rất lớn: (vài chục MB trở lên, đến vài GB). Như đã nói ở trên, nó sẽ "ngốn" RAM và có thể làm crash ứng dụng của bạn. Lúc đó, hãy chuyển sang dùng
Hy vọng với bài học này, các bạn Gen Z đã "bỏ túi" được thêm một "siêu năng lực" nữa trong hành trình chinh phục Node.js. Nhớ thực hành và "vọc vạch" thật nhiều nhé! Hẹn gặp lại trong những bài học "chất như nước cất" tiếp theo!
Thuộc Series: Nodejs
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!