Khi bạn không biết cần bao nhiêu lần lặp
Vòng lặp for phát huy tác dụng khi bạn biết trước phạm vi: "làm việc này 10 lần", "đi qua mọi chỉ số của mảng này". Vòng lặp (loop) while dành cho trường hợp còn lại: cứ tiếp tục cho đến khi có gì đó thay đổi. Bạn không biết sẽ cần bao nhiêu lần lặp, chỉ biết điều kiện khiến bạn dừng lại.
Cấu trúc rất gọn:
JavaScript tính điều kiện trong cặp ngoặc tròn. Nếu nó truthy, khối lệnh được chạy. Sau đó nó kiểm tra điều kiện lần nữa, rồi lần nữa, cho đến khi điều kiện thành falsy. Quên count++ là điều kiện không bao giờ thay đổi, và bạn rơi vào vòng lặp vô hạn.
Mô hình tư duy: kiểm tra, chạy, lặp lại
Cần ba điều để một vòng lặp while có ý nghĩa:
- Một thứ gì đó mà điều kiện phụ thuộc vào.
- Một cách để phần thân thay đổi thứ đó.
- Cuối cùng, điều kiện trở thành sai.
Thiếu bất kỳ điều nào là bạn gặp rắc rối. Bug kinh điển là quên bước 2:
let count = 0;
while (count < 5) {
console.log(count);
// forgot to increment count - loops forever
}
Đoạn đó chạy mãi cho đến khi bạn tắt tiến trình. Sẽ gây thiệt hại thật sự nếu nó xảy ra trên máy chủ. Hãy luôn tự hỏi: thứ gì trong phần thân sẽ khiến điều kiện trở thành sai?
Một ví dụ thực tế: không biết trước điểm dừng
Đây là chỗ while thắng for. Bạn đang lấy các phần tử từ một nơi nào đó và không biết có bao nhiêu cho đến khi lấy hết:
shift() xóa và trả về phần tử đầu tiên. Vòng lặp chạy chừng nào hàng đợi còn phần tử, và tự nhiên thoát ra khi hàng đợi rỗng. Bạn có thể viết đoạn này bằng vòng lặp for, nhưng sẽ phải chống lại hình dạng của bài toán: while đọc lên đúng như ý định.
do...while: chạy trước, hỏi sau
do...while đảo thứ tự: chạy phần thân, rồi mới kiểm tra điều kiện. Điều này đảm bảo có ít nhất một lần lặp:
Phần thân luôn chạy một lần, kể cả khi điều kiện sai ngay từ đầu. So với while thông thường, nơi điều kiện sai nghĩa là không có lần lặp nào.
Trường hợp thường gặp: hỏi người dùng, kiểm tra câu trả lời và hỏi lại nếu câu trả lời không hợp lệ. Bạn luôn muốn hỏi ít nhất một lần, nên do...while rất hợp.
Để ý dấu chấm phẩy sau dấu ) đóng: khác với while và for, do...while cần có nó.
break: thoát sớm
break thoát ngay lập tức khỏi vòng lặp gần nhất. Đó là cách dừng lại khi điều kiện tự nhiên chưa hẳn phù hợp với công việc:
Khi break chạy, không còn gì khác trong vòng lặp được thực thi: không phải phần còn lại của thân, không phải lần kiểm tra điều kiện tiếp theo. Bạn đã ra ngoài.
while (true) với break
Khi điều kiện dừng dễ diễn đạt bên trong thân hơn là ở phần đầu, hãy đảo cấu trúc: lặp mãi mãi và break ra khi xong việc.
Hai lối thoát: thành công và một giới hạn an toàn. Giới hạn an toàn rất quan trọng. while (true) mà không có đường break chắc chắn là một vòng lặp vô hạn chực chờ xảy ra.
continue: nhảy sang lần lặp tiếp theo
continue nhảy tới lần kiểm tra điều kiện tiếp theo, bỏ qua phần còn lại của thân trong lần lặp hiện tại:
Một điểm cần lưu ý: nếu thứ giúp vòng lặp tiến lên nằm sau continue, bạn vừa tạo ra một vòng lặp vô hạn. Trong ví dụ trên, n++ nằm trước continue nên an toàn. Đặt nó ở sau thì n sẽ kẹt mãi ở một số chẵn.
while hay for: nên dùng cái nào
Một quy tắc chung máy móc:
- Đếm hoặc duyệt chỉ số trong một phạm vi đã biết →
for.for (let i = 0; i < arr.length; i++)gói phần khởi tạo bộ đếm, điều kiện và bước nhảy trên cùng một dòng. - Duyệt một tập hợp →
for...of. Gọn hơn việc tự quản lý chỉ số. - Lặp cho đến khi trạng thái nào đó thay đổi →
while. Xử lý hàng đợi, polling, đọc một stream, thử lại cho đến khi thành công. - Luôn chạy ít nhất một lần →
do...while.
Về lý thuyết chúng có thể thay thế cho nhau: vòng lặp for nào cũng viết lại được bằng while và ngược lại. Nhưng chọn đúng loại khớp với hình dạng bài toán giúp code tự giải thích chính nó.
Vòng lặp vô hạn: một quy tắc duy nhất
Mọi vòng lặp while đều cần một câu trả lời rõ ràng cho câu hỏi: điều gì khiến nó dừng lại?
Hoặc điều kiện ở phần đầu cuối cùng sẽ trở thành sai, hoặc có một lệnh break chắc chắn sẽ chạy. Nếu bạn không chỉ ra được một trong hai, vòng lặp đó chưa hoàn chỉnh.
// Bad: condition never changes
let ready = false;
while (!ready) {
console.log("waiting...");
// nothing in here flips `ready`
}
// Good: the body affects the condition
let ready = false;
let checks = 0;
while (!ready) {
checks++;
if (checks >= 3) ready = true;
}
Trên trình duyệt, vòng lặp vô hạn làm treo tab. Trong Node, nó chiếm trọn một nhân CPU cho đến khi bạn tắt tiến trình. Đáng để phát hiện trước khi phát hành.
Tiếp theo: for...of và for...in
while xử lý trường hợp "cho đến khi có gì đó thay đổi". Để đi qua từng phần tử của mảng, chuỗi và đối tượng, JavaScript có thêm hai dạng vòng lặp được thiết kế đúng cho việc đó: for...of và for...in, nội dung tiếp theo.
Câu hỏi thường gặp
Vòng lặp while trong JavaScript là gì?
Vòng lặp while chạy lặp lại phần thân chừng nào điều kiện còn truthy. JavaScript kiểm tra điều kiện trước, chạy khối lệnh, rồi kiểm tra lại. Nếu ngay lần đầu điều kiện đã sai, phần thân sẽ không chạy lần nào.
while và do...while khác nhau thế nào?
while kiểm tra điều kiện trước khi chạy phần thân, nên có thể không có lần lặp nào. do...while chạy phần thân một lần rồi mới kiểm tra điều kiện, nên luôn chạy ít nhất một lần. Dùng do...while khi lần lặp đầu tiên bắt buộc phải diễn ra, chẳng hạn như hỏi người dùng nhập dữ liệu.
Làm sao thoát khỏi vòng lặp while trong JavaScript?
Dùng câu lệnh break để thoát vòng lặp ngay lập tức, hoặc continue để nhảy sang lần lặp tiếp theo. break là cách viết vòng lặp while (true) một cách an toàn: lặp mãi mãi, rồi thoát ra khi một điều kiện nào đó trong thân được thỏa mãn.