10 câu hỏi có thể gặp khi phỏng vấn
Chọn theo kỹ năng và cấp của tin (Node.js, ReactJS, JavaScript & TypeScript). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.
01Event loop của Node gồm những phase nào?
process.nextTick, Promise,setImmediatevàsetTimeoutchạy theo thứ tự nào?Node.jsGợi ý trả lời
Ôn thêm câu hỏi Node.js →Event loop của Node (libuv) đi qua các phase chính: timers (
setTimeout,setInterval), poll (chờ và xử lý I/O), check (setImmediate), close callbacks.Giữa các callback, Node chạy hết hàng đợi
process.nextTicktrước, rồi đến microtask của Promise.setTimeout(() => console.log("timeout")); setImmediate(() => console.log("immediate")); Promise.resolve().then(() => console.log("promise")); process.nextTick(() => console.log("nextTick")); // nextTick, promise, rồi timeout/immediate- Thứ tự giữa
setTimeout(0)vàsetImmediateở module chính là không chắc chắn; nhưng nếu cả hai được đặt trong một callback I/O thìsetImmediateluôn chạy trước.
Hay sai: gọi
process.nextTickđệ quy, khiến event loop không bao giờ sang được phase I/O.- Thứ tự giữa
02Stream trong Node là gì? Backpressure là gì? Làm sao xử lý file vài GB mà không hết RAM?Node.js
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →Stream xử lý dữ liệu theo từng chunk thay vì đọc hết vào bộ nhớ. Có bốn loại: Readable, Writable, Duplex, Transform.
Backpressure: khi bên ghi chậm hơn bên đọc, dữ liệu dồn lại trong bộ đệm.
write()trả vềfalseđể báo nên dừng đọc cho đến khi có sự kiệndrain.Dùng
pipelineđể tự xử lý backpressure, lỗi và dọn dẹp:import { pipeline } from "node:stream/promises"; import { createGzip } from "node:zlib"; await pipeline( fs.createReadStream("big.csv"), createGzip(), fs.createWriteStream("big.csv.gz"), );Ứng dụng: upload/download file lớn, xuất CSV từ DB theo con trỏ, proxy dữ liệu.
Hay sai: dùng
fs.readFilecho file lớn, hoặc.pipe()nối nhiều bước mà không bắt lỗi ở từng stream, khiến file descriptor bị rò.03Một endpoint làm tác vụ nặng CPU (xử lý ảnh, tạo báo cáo) làm cả server chậm theo. Bạn xử lý thế nào?Node.js
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →Tác vụ CPU chạy trên main thread sẽ chặn event loop, nên mọi request khác phải chờ. Có vài hướng:
worker_threads: chạy tính toán ở thread khác trong cùng process. Nên dùng một pool worker (ví dụ thư viện Piscina) thay vì tạo worker mới cho mỗi request.- Hàng đợi công việc: API chỉ nhận yêu cầu, đẩy job vào queue (BullMQ trên Redis, RabbitMQ), trả về
202kèm mã job; worker riêng xử lý, client hỏi lại trạng thái hoặc nhận thông báo. - Tách service: nếu việc nặng là trọng tâm, viết bằng ngôn ngữ phù hợp hơn hoặc dùng dịch vụ chuyên dụng.
clusterhay PM2 chạy nhiều process giúp tận dụng nhiều core, nhưng không giải quyết việc một request nặng chặn process của nó.Người phỏng vấn muốn nghe: bạn đo được độ trễ event loop (
perf_hooks.monitorEventLoopDelay) để phát hiện vấn đề này.04Custom hook là gì? Các quy tắc của hooks là gì và vì sao có chúng?ReactJS
Gợi ý trả lời
Ôn thêm câu hỏi ReactJS →Custom hook là hàm bắt đầu bằng
use, dùng các hook khác bên trong, để tái sử dụng logic có state giữa nhiều component (không chia sẻ state, mỗi component có bản riêng).function useDebounce(value, ms) { const [debounced, setDebounced] = useState(value); useEffect(() => { const id = setTimeout(() => setDebounced(value), ms); return () => clearTimeout(id); }, [value, ms]); return debounced; }Quy tắc: chỉ gọi hook ở cấp cao nhất của component hoặc custom hook; không gọi trong
if, vòng lặp hay saureturnsớm. Lý do: React nhận diện từng hook theo thứ tự gọi, đổi thứ tự là state bị ghép nhầm.Ngoại lệ: hook
use()của React 19 được gọi trong điều kiện.Người phỏng vấn muốn nghe: bạn bật ESLint plugin
react-hooksđể bắt các lỗi này tự động.05Bạn chọn cách quản lý state thế nào: Context, Redux Toolkit, Zustand hay TanStack Query?ReactJS
Gợi ý trả lời
Ôn thêm câu hỏi ReactJS →Trước hết phân loại state:
- Server state (dữ liệu từ API): dùng TanStack Query hoặc SWR. Chúng lo cache, loading, refetch, đồng bộ. Phần lớn "global state" trong app thực ra thuộc loại này.
- UI state cục bộ:
useState/useReducerngay trong component. - State trên URL (bộ lọc, trang): để trên query string, chia sẻ link được.
- Client state toàn cục thật sự (giỏ hàng, theme, user đang đăng nhập): Context nếu ít thay đổi; Zustand hoặc Redux Toolkit nếu thay đổi thường xuyên hoặc logic phức tạp.
Redux Toolkit hợp khi team lớn cần quy ước chặt và DevTools mạnh. Zustand gọn nhẹ hơn.
Hay sai: gom state hay thay đổi vào một Context lớn. Mọi component dùng Context đó render lại mỗi lần bất kỳ giá trị nào đổi. Hãy tách Context, hoặc dùng thư viện có selector.
06Trong TypeScript,
interfacevàtypekhác nhau thế nào? Phân biệtany,unknownvànever.JavaScript & TypeScriptGợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Cả hai đều mô tả hình dạng object và phần lớn trường hợp dùng thay nhau được.
interfacecó thể gộp khai báo (declaration merging) vàextends; hợp để mô tả object, API công khai.typemô tả được union, tuple, kiểu ánh xạ, kiểu điều kiện:type Status = "idle" | "loading".
Ba kiểu đặc biệt:
any: tắt kiểm tra kiểu, lỗi lọt qua compiler.unknown: nhận mọi giá trị nhưng bắt buộc thu hẹp kiểu trước khi dùng. Hợp cho dữ liệu từJSON.parse, API,catch (e).never: giá trị không bao giờ xảy ra; dùng để bắt thiếu case trongswitch.
function assertNever(x: never): never { throw new Error("Unhandled: " + x); }Hay sai: dùng
as SomeTypeđể ép dữ liệu từ API. Đó chỉ là lời hứa với compiler; muốn chắc chắn phải validate lúc runtime (ví dụ bằng Zod).07Trong NestJS, module, provider và dependency injection hoạt động ra sao? Guard, interceptor, pipe khác nhau thế nào?Node.js
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →NestJS tổ chức app thành module. Mỗi module khai báo controller, provider (service, repository) và
exportsnhững gì module khác được dùng. IoC container của Nest tự tạo provider và inject qua constructor.@Injectable() export class OrdersService { constructor(private readonly repo: OrdersRepository) {} }Thứ tự một request đi qua: middleware, guard, interceptor (trước), pipe, handler, interceptor (sau); exception filter bắt lỗi ở bất kỳ bước nào.
- Guard: quyết định request có được đi tiếp không (xác thực, phân quyền).
- Pipe: biến đổi và validate input (
ValidationPipevới DTO). - Interceptor: bọc quanh handler: log, đo thời gian, đổi định dạng response, cache.
Hay sai: hai provider phụ thuộc vòng tròn rồi xử lý bằng
forwardRefkhắp nơi. Thường đó là dấu hiệu nên tách lại trách nhiệm giữa các module.08Process Node trên production tăng RAM liên tục rồi bị kill. Bạn tìm memory leak thế nào?Node.js · Senior
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →Trước hết xác nhận có leak thật: theo dõi heap qua thời gian (
process.memoryUsage(), metrics của APM). Heap tăng rồi giảm sau GC là bình thường; chỉ tăng không giảm mới là leak.Nguyên nhân hay gặp:
- Cache trong bộ nhớ (
Map, object) không giới hạn kích thước hoặc thời hạn. - Event listener đăng ký mỗi request mà không gỡ (thường kèm cảnh báo
MaxListenersExceededWarning). - Closure giữ object lớn; timer không
clearInterval. - Mảng toàn cục lưu dữ liệu request để debug rồi quên xoá.
Cách điều tra:
- Chạy với
--inspect, mở Chrome DevTools, chụp heap snapshot trước và sau khi bắn tải, so sánh object tăng thêm và chuỗi retainers. - Dùng
--heapsnapshot-near-heap-limitđể tự chụp snapshot khi sắp hết bộ nhớ.
Người phỏng vấn muốn nghe: cache thì dùng LRU có giới hạn hoặc Redis, và biết rằng tăng
--max-old-space-sizechỉ làm lỗi đến chậm hơn.- Cache trong bộ nhớ (
09Have you worked with foreign clients or teams in different time zones? How did it go?Phỏng vấn tiếng Anh
Gợi ý trả lời
Ôn thêm câu hỏi Phỏng vấn tiếng Anh →"Yes, for the last two years I've worked with a client in Europe, so we only had about three overlapping hours a day. To make the most of that time, we kept a short daily call and I prepared questions in advance. For everything else, I wrote clear updates in Jira and Slack so they could reply when they started their day. This helped us avoid waiting a whole day for answers."
Mẹo: nhấn vào cách bạn giao tiếp chủ động và viết rõ ràng. Dùng "overlapping hours", "asynchronous communication", "I kept them updated". Tránh nói "I have worked with them since 2 years", đúng là "for two years".
10Bạn phản ứng thế nào khi nhận góp ý tiêu cực về công việc của mình?Câu hỏi hành vi & văn hoá
Gợi ý trả lời
Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →Cho thấy bạn tách góp ý khỏi cảm xúc cá nhân và biến nó thành hành động.
- Lắng nghe hết, hỏi lại để hiểu ví dụ cụ thể và kỳ vọng.
- Cảm ơn, kể cả khi chưa đồng ý hoàn toàn; nếu có góc nhìn khác, trao đổi bằng thông tin, không phòng thủ.
- Lên kế hoạch cải thiện và chủ động xin phản hồi lại sau một thời gian.
Câu mẫu: "Có lần lead góp ý code review của em quá dài, khó review. Lúc đầu em hơi chột dạ, nhưng em hỏi anh muốn chia thế nào cho dễ. Từ đó em tách pull request nhỏ theo từng thay đổi, thời gian review của team giảm hẳn và em cũng ít bị yêu cầu sửa lại hơn."
Hay sai: nói "em luôn vui vẻ đón nhận mọi góp ý" mà không có ví dụ.
Bạn đã phỏng vấn ở AlphaSphere?
Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.
Chia sẻ kinh nghiệm phỏng vấn