
Chào các bạn Gen Z mê code, tôi là Creyt đây! Hôm nay, chúng ta sẽ "mổ xẻ" một "thám tử" đặc biệt trong thế giới Node.js: fs.readFileSync(). Nghe tên đã thấy "nghiêm túc" rồi đúng không? Nhưng yên tâm, tôi sẽ biến nó thành một câu chuyện dễ hiểu, thậm chí còn hơi "lầy lội" một chút.
fs.readFileSync() là gì mà "gắt" vậy?
Trong Node.js, fs là viết tắt của "File System" – bộ phận chuyên trách mọi thứ liên quan đến file và thư mục. Còn readFileSync? Nó là hàm giúp bạn "đọc file" một cách "đồng bộ" (synchronous).
Tưởng tượng thế này: Bạn đang ở trong một quán trà sữa đông nghịt người. fs.readFileSync() giống như bạn phải đứng xếp hàng, chờ đến lượt mình, và chỉ khi nào bạn nhận được ly trà sữa xong xuôi thì bạn mới có thể làm việc khác (ví dụ: đi tìm chỗ ngồi, chụp ảnh sống ảo). Trong thời gian bạn chờ, cả thế giới của bạn dường như "đóng băng" lại vậy. Không ai được làm gì cho đến khi bạn xong việc.
Đó chính là bản chất của "đồng bộ" trong readFileSync(): Khi bạn gọi nó, Node.js sẽ "đứng yên" và chờ đợi cho đến khi file được đọc xong hoàn toàn, rồi mới chuyển sang thực thi dòng code tiếp theo. Nó là một "blocking I/O" operation – nghĩa là nó "chặn" luồng chính của ứng dụng.
Vậy nó dùng để làm gì? Đơn giản là để đọc nội dung của một file và trả về ngay lập tức. Nội dung có thể là text, JSON, cấu hình, hay bất cứ thứ gì bạn lưu trong file.
Code Ví Dụ: "Thám tử" hành động!
Để fs.readFileSync() hoạt động, bạn cần "triệu hồi" module fs trước. Sau đó, chỉ cần truyền đường dẫn đến file và tùy chọn encoding (mã hóa ký tự, thường là utf8 cho text) là xong.
Bước 1: Chuẩn bị "hiện trường" (tạo file)
Bạn tạo một file data.txt với nội dung sau:
Chào các bạn Gen Z! Node.js là đỉnh của chóp!
Hôm nay học fs.readFileSync() nhé!
Và một file config.json để đọc cấu hình:
{
"appName": "Creyt's Awesome App",
"version": "1.0.0",
"database": {
"host": "localhost",
"port": 27017
},
"secretKey": "super_secure_key_123"
}
Bước 2: "Triệu hồi" và "hỏi cung" (viết code)
Bạn tạo file app.js và viết code như sau:
const fs = require('fs');
console.log('--- Bắt đầu đọc file ---');
try {
// Đọc file data.txt
const dataText = fs.readFileSync('data.txt', 'utf8');
console.log('Nội dung từ data.txt:', dataText);
// Đọc file config.json
const configRaw = fs.readFileSync('config.json', 'utf8');
const config = JSON.parse(configRaw); // Chuyển JSON string thành object
console.log('Tên ứng dụng từ config.json:', config.appName);
console.log('Phiên bản:', config.version);
console.log('Host database:', config.database.host);
// Thử đọc một file không tồn tại để xem lỗi
// const nonExistentFile = fs.readFileSync('nonExistent.txt', 'utf8');
// console.log(nonExistentFile);
} catch (error) {
// Bắt lỗi nếu file không tìm thấy hoặc có vấn đề khi đọc/phân tích
console.error('Ối giời ơi, có lỗi rồi:', error.message);
// console.error('Chi tiết lỗi:', error); // Có thể log chi tiết hơn để debug
}
console.log('--- Kết thúc đọc file ---');
console.log('Ứng dụng tiếp tục chạy các tác vụ khác...');
Khi chạy node app.js, bạn sẽ thấy nội dung file được in ra console một cách tuần tự. Nếu bạn uncomment dòng đọc nonExistent.txt, chương trình sẽ báo lỗi và dừng lại tại catch block.

Mẹo hay từ Creyt: Dùng sao cho "chuẩn chỉnh"?
-
"Chốt đơn" khi nào?:
fs.readFileSync()cực kỳ hữu ích khi bạn cần đọc các file cấu hình (config files), các biến môi trường (.env), hoặc các file dữ liệu nhỏ chỉ một lần duy nhất khi ứng dụng khởi động (bootstrapping). Lúc này, việc chờ đợi một chút để có đủ thông tin ban đầu là hoàn toàn chấp nhận được, thậm chí còn giúp code dễ đọc và dễ quản lý hơn. -
"Né gấp" khi nào?: Tuyệt đối tránh xa
fs.readFileSync()trong các tác vụ xử lý request của người dùng (ví dụ: trong API endpoint của một web server), hoặc bất kỳ tác vụ nào có thể mất nhiều thời gian. Hãy nhớ ví dụ quán trà sữa chứ? Nếu mỗi lần có khách gọi trà sữa mà cả quán phải dừng lại chờ bạn làm xong thì toang! Ứng dụng của bạn sẽ bị "treo" (block) và trải nghiệm người dùng sẽ "tệ hại" (laggy). -
Ghi nhớ "thần chú": "
Synclà Đợi,Asynclà Không Đợi".readFileSynccó chữSyncnên nó "đợi" đó. Khi bạn cần "không đợi" (non-blocking), hãy dùngfs.readFile()(phiên bản bất đồng bộ) hoặc các Promise-based API củafs(ví dụ:fs.promises.readFile). -
"Bảo hiểm"
try-catch:fs.readFileSync()sẽthrowlỗi nếu có vấn đề (ví dụ: file không tồn tại, không có quyền đọc). Luôn luôn bọc nó trongtry-catchđể ứng dụng của bạn không bị "sập nguồn" đột ngột.
Ví dụ thực tế: "Thám tử" hoạt động ở đâu?
- Load cấu hình ứng dụng: Khi bạn khởi động một server Node.js (ví dụ: Express.js), nó cần biết cổng (port) nào để lắng nghe, chuỗi kết nối database là gì, khóa API nào để dùng. Các thông tin này thường được lưu trong
config.json,.envhoặc các file YAML.fs.readFileSync()là lựa chọn lý tưởng để đọc chúng một lần duy nhất khi server vừa "tỉnh giấc". - Đọc chứng chỉ SSL/TLS: Để thiết lập HTTPS cho server, bạn cần đọc các file chứng chỉ (
.key,.crt). Tác vụ này cũng chỉ diễn ra khi server khởi động, nênreadFileSync()là "oke la". - Khởi tạo database: Đôi khi, bạn có các script SQL hoặc dữ liệu ban đầu cần được đọc để khởi tạo database khi ứng dụng deploy lần đầu.
readFileSync()có thể được dùng cho mục đích này.
Thử nghiệm và Nên dùng cho case nào?
Tôi đã từng thấy nhiều bạn sinh viên dùng fs.readFileSync() trong API endpoint để đọc dữ liệu mỗi khi có request. Kết quả? Server "chết đứng" khi có nhiều người truy cập cùng lúc. Đó là một bài học đắt giá về sự khác biệt giữa "đồng bộ" và "bất đồng bộ" trong môi trường Node.js single-threaded.
Nên dùng fs.readFileSync() cho:
- Khởi tạo ứng dụng (Application Initialization): Load cấu hình, biến môi trường, chứng chỉ bảo mật, dữ liệu tĩnh nhỏ cần thiết cho toàn bộ vòng đời của ứng dụng.
- Scripts hoặc Utilities chạy một lần: Các script nhỏ chỉ chạy để thực hiện một tác vụ cụ thể và sau đó thoát (ví dụ: script migrate database, script generate report).
- Khi bạn chắc chắn rằng file rất nhỏ và việc đọc nó sẽ không gây tắc nghẽn đáng kể, và bạn cần giá trị trả về ngay lập tức.
Không nên dùng fs.readFileSync() cho:
- Trong các HTTP request handlers (API endpoints): Dù là đọc file user upload, hay đọc dữ liệu cho mỗi request. Luôn dùng
fs.readFile()(async) hoặc stream để tránh block event loop. - Xử lý các file lớn: Đọc một file dung lượng lớn bằng
readFileSyncsẽ "đóng băng" toàn bộ ứng dụng của bạn trong thời gian rất dài. - Bất kỳ tác vụ I/O nào có khả năng mất thời gian và ảnh hưởng đến trải nghiệm người dùng.
Nhớ nhé, fs.readFileSync() là một công cụ mạnh mẽ nhưng cần được dùng đúng lúc, đúng chỗ. Nó giống như một "vũ khí hạng nặng" – dùng đúng thì "bách phát bách trúng", dùng sai thì "tự bắn vào chân" đó! Hãy là một lập trình viên thông minh, biết khi nào nên "đợi" và khi nào nên "đi tiếp" nhé!
Oke la, bài học hôm nay đến đây là kết thúc. Hẹn gặp lại các bạn trong những "phi vụ" công nghệ 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é!