Câu 1Nếu một vòng lặp while bắt đầu khi điều kiện đang đúng và thân không bao giờ thay đổi biến mà điều kiện kiểm tra, chuyện gì xảy ra?
Lặp theo điều kiện — while, do...while, break và continue
Học while và do...while trong JavaScript: lặp khi không biết trước số lần, cách tránh vòng lặp vô hạn, và khi nào dùng continue, khi nào dùng break.
Xếp chỗ cho người trong danh sách chờ khi có chỗ trống, hay mua thêm hàng chừng nào số dư còn đủ — với những việc như vậy, bạn biết khi nào cần dừng, nhưng không biết trước phải lặp bao nhiêu lần.
Bài này nói về while và do...while để lặp dựa trên điều kiện, continue để bỏ qua một vòng, và break để thoát khỏi vòng lặp.
Lặp mà không cố định số lần — while và việc cập nhật điều kiện
Giả sử có vài chỗ ngồi vừa trống, và bạn muốn lần lượt xếp chỗ cho người ở đầu danh sách chờ. Mỗi người được xếp sẽ chiếm một chỗ, và bạn dừng lại khi còn 0 chỗ. Thứ khiến vòng lặp dừng là một trạng thái — "chừng nào còn chỗ trống" — chứ không phải số vòng, nên vòng lặp for vừa đếm vừa lặp không diễn đạt được điều này một cách tự nhiên.
Vòng lặp while (câu lệnh lặp lại phần thân trong cặp ngoặc nhọn chừng nào điều kiện trong cặp ngoặc tròn còn đúng) chỉ nhận một điều kiện trong cặp ngoặc tròn. Điều kiện được kiểm tra ở mỗi vòng, trước khi vào thân.
Bạn tự đặt giá trị ban đầu ở trước vòng lặp, và tự cập nhật biến mà điều kiện kiểm tra ở bên trong thân.
const waitingList = ["Minh", "Linh", "Hùng"]; // danh sách chờ
let openSeats = 2; // số chỗ còn trống
// Chỉ xếp chỗ khi còn chỗ trống
while (openSeats > 0) {
const name = waitingList.shift();
openSeats--; // tương đương openSeats = openSeats - 1
console.log(`Đã xếp chỗ cho ${name} (còn ${openSeats} chỗ)`);
}
// Đã xếp chỗ cho Minh (còn 1 chỗ)
// Đã xếp chỗ cho Linh (còn 0 chỗ)
console.log(`Số người còn chờ: ${waitingList.length}`); // Số người còn chờ: 1
Trong vòng lặp for, biểu thức cập nhật có chỗ cố định bên trong cặp ngoặc tròn, còn với while thì bạn tự quyết định đặt nó ở đâu trong thân. Mặt trái của sự tự do đó là không có gì ngăn bạn quên viết nó: thiếu phần cập nhật thì code vẫn hoàn toàn đúng cú pháp.
Quên cập nhật thì vòng lặp không bao giờ dừng
Nếu điều kiện không bao giờ thành false, thân sẽ lặp lại mãi mãi. Nếu quên viết openSeats--, số chỗ vẫn giữ nguyên là 2, nên lần kiểm tra nào cũng cho cùng một kết quả. Console sẽ báo timeout sau 15 giây nhưng vòng lặp vẫn không dừng, và các bài tập khác cũng không chạy được cho tới khi bạn tải lại trang. Luôn thay đổi biến mà điều kiện kiểm tra ở đâu đó trong thân.
Chạy trước một lần — thứ tự kiểm tra của do...while
Khi gửi thông báo thất bại, bạn sẽ gửi lại vài lần cho tới khi thành công. Bạn phải gửi ít nhất một lần thì mới có kết quả để kiểm tra, nhưng while kiểm tra điều kiện trước khi vào thân, nên tùy vào giá trị ban đầu của biến trong điều kiện, vòng lặp có thể kết thúc mà không gửi lần nào.
Vòng lặp do...while (câu lệnh chạy thân một lần rồi mới kiểm tra điều kiện) được viết là do { thân } while (điều_kiện);, và toàn bộ phần tính đến dấu chấm phẩy cuối cùng là một câu lệnh. Vì việc kiểm tra đứng sau thân, thân vẫn chạy một lần kể cả khi điều kiện sai ngay từ đầu.
const attempts = [false, false, true]; // kết quả gửi được chuẩn bị sẵn (true là thành công)
let sent = true; // vẫn còn là true từ thông báo trước
let tried = 0;
// while kiểm tra trước, nên kết thúc mà không gửi lần nào
while (!sent && tried < attempts.length) {
sent = attempts[tried];
tried++;
}
console.log(`Số lần gửi với while: ${tried}`); // Số lần gửi với while: 0
// do...while gửi lần đầu trước khi kiểm tra, rồi dựa vào kết quả để quyết định có tiếp tục không
do {
sent = attempts[tried];
tried++;
console.log(`Lần gửi thứ ${tried}: ${sent ? "thành công" : "thất bại"}`);
} while (!sent && tried < attempts.length);
// Lần gửi thứ 1: thất bại / Lần gửi thứ 2: thất bại / Lần gửi thứ 3: thành công
sent vẫn còn là true. while kết thúc ngay sau lần kiểm tra, còn do...while gửi một lần rồi mới quyết định có tiếp tục không.Chỉ vòng đầu tiên là bỏ qua bước kiểm tra; từ đó trở đi, vòng lặp chỉ lặp lại khi điều kiện còn đúng, giống hệt while. Nếu bạn không muốn thân chạy lần nào khi điều kiện sai, hãy chọn while. Nếu muốn thân chạy một lần trước khi xem kết quả, hãy chọn do...while.
Chỉ bỏ qua một vòng — khi nào dùng continue
Khi xử lý danh sách xuất hàng từ trên xuống, bạn sẽ muốn bỏ qua những dòng đã hết hàng và chuyển sang dòng tiếp theo. Càng thêm nhiều điều kiện bỏ qua, các cặp ngoặc nhọn của if càng lồng sâu, và phần code xuất hàng mà bạn thực sự quan tâm bị chôn vùi bên trong.
Với continue (câu lệnh bỏ qua phần còn lại của vòng hiện tại và chuyển sang vòng tiếp theo), bạn có thể kết thúc sớm một vòng ngay khi gặp điều kiện muốn bỏ qua. Bản thân vòng lặp vẫn tiếp tục, nên vòng tiếp theo bắt đầu bình thường bằng việc kiểm tra điều kiện.
const names = ["Sổ tay A5", "Đèn bàn", "Hub USB"];
const stocks = [12, 0, 5]; // 0 nghĩa là hết hàng
let index = 0;
while (index < names.length) {
const name = names[index];
const stock = stocks[index];
index++; // tăng trước để continue không bỏ qua dòng này
// Dòng hết hàng thì bỏ qua phần còn lại của vòng này và sang vòng tiếp theo
if (stock === 0) {
console.log(`${name}: hết hàng, bỏ qua`);
continue;
}
console.log(`${name}: xuất ${stock} cái`);
}
// Sổ tay A5: xuất 12 cái
// Đèn bàn: hết hàng, bỏ qua
// Hub USB: xuất 5 cái
| Dòng được xử lý | Giá trị của stock và kết quả kiểm tra | Kết quả của vòng đó |
|---|---|---|
| Dòng 1: Sổ tay A5 | stock là 12, khác 0 | In ra xuất 12 cái |
| Dòng 2: Đèn bàn | stock là 0 nên continue | Dòng xuất hàng không chạy |
| Dòng 3: Hub USB | stock là 5, khác 0 | In ra xuất 5 cái |
continue chỉ bỏ qua các dòng nằm giữa nó và cuối thân. Bạn cũng có thể dùng continue trong thân của for và for...of, và vì biểu thức cập nhật trong cặp ngoặc tròn của for nằm ngoài thân, nó vẫn chạy ở vòng đã gặp continue.
continue cũng bỏ qua luôn dòng cập nhật
Nếu đặt index++ sau continue, index sẽ không tăng ở vòng bị bỏ qua. Vòng tiếp theo lại đọc đúng dòng đó, stock vẫn là 0, điều kiện không thay đổi, và vòng lặp không bao giờ kết thúc. Hãy cập nhật biến của điều kiện ở trước dòng continue.
Dừng ngay khi tìm thấy — break thoát ra tới đâu
Giả sử bạn muốn quét một file log từ trên xuống và chỉ tìm lỗi đầu tiên. Nếu vẫn đọc tiếp tới cuối sau khi đã tìm thấy thì đó là việc thừa, và tùy cách viết, một lỗi phía sau có thể ghi đè lên firstError.
Với break (câu lệnh kết thúc ngay lập tức vòng lặp đang chứa nó), bạn có thể thoát khỏi vòng lặp ở vòng mà điều kiện được thỏa. Đây cũng chính là câu lệnh bạn đã dùng để thoát khỏi switch trong bài về rẽ nhánh điều kiện. Vì bạn sẽ dùng giá trị tìm được ở bên ngoài vòng lặp, hãy khai báo firstError trước vòng lặp.
const logs = ["INFO Đã khởi động", "WARN Phản hồi chậm", "ERROR Thanh toán thất bại", "ERROR Gửi lại thất bại"];
let index = 0;
let firstError = "";
while (index < logs.length) {
const line = logs[index];
index++;
if (line.startsWith("ERROR")) {
firstError = line;
break; // đã tìm thấy lỗi đầu tiên nên không kiểm tra phần còn lại
}
}
console.log(`Lỗi đầu tiên: ${firstError}`); // Lỗi đầu tiên: ERROR Thanh toán thất bại
console.log(`Số dòng đã kiểm tra: ${index}`); // Số dòng đã kiểm tra: 3
const line = logs[index]— dòng được kiểm tra ở vòng này- Sau
break, chương trình ra khỏi cặp ngoặc nhọn và đi tới dòngconsole.logkế tiếp
firstError = line— lưu lại dòng tìm đượcbreak— thoát khỏi vòng while bên ngoài, chứ không phải cặp ngoặc này- Các dòng nằm dưới
breaksẽ không bao giờ chạy
break lẫn các vòng còn lại đều không chạy. Chương trình tiếp tục ở dòng nằm sau dấu ngoặc nhọn đóng của vòng lặp while.Ngay cả ở vòng gặp break, dòng index++ viết trước nó cũng đã chạy xong. Vì vậy index dừng ở 3, và "Số dòng đã kiểm tra: 3" in ra sau vòng lặp trùng với vị trí của dòng tìm được, tính từ 1.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Nếu điều kiện sai ngay từ đầu, thân của do...while chạy bao nhiêu lần?
Câu 3Khi continue chạy bên trong một vòng lặp, chuyện gì xảy ra tiếp theo?