01/10/2018, 14:54
Hàm setTimeout() trong js
Theo e hiểu thì kết quả sẽ chạy ra là từ 0 đến 4 và mỗi lần chạy in ra kết quả thì sau 1s mới in,
Tại sao kết quả lại ra là 5 lần 5 ạ ? Em có đọc trên mạng về callback function mà vẫn chưa hiểu,
Anh chị nào thông não chi tiết hộ em với ạ,
for (var i = 0; i < 5; i++) {
setTimeout(function(){
console.log('Yo! ', i);
}, 1000);
}
Bài liên quan
var _loop_1 = function (i) {
setTimeout(function () {
console.log('Yo! ', i);
}, 1000);
};
for (var i = 0; i < 5; i++) {
_loop_1(i);
}
Cái này gọi là Closure nhé bạn
Sử dụng closure bên trong 1 vòng lặp
Closure là 1 chủ đề thường thấy trong các buổi phỏng vấn JavaScript, nó giúp người phỏng vấn đánh giá bạn thành thục ngôn ngữ đến đâu, bạn biết implement closure hay không.
Về cơ bản, closure là 1 hàm nội truy cập đến các biến bên ngoài phạm vi của nó. Closure có thể được sử dụng để implement privacy và tạo ra các function factory. Một câu hỏi phỏng vấn thường thấy về việc sử dụng closure sẽ có kiểu thế này:
Viết 1 function lặp qua 1 danh sách các số dạng integer và in ra index của mỗi giá trị sau thời gian chờ 3s.
Dưới đây là cách tôi hay thấy khi mọi người giải quyết bài toán này (thực ra là sai):
Nếu bạn chạy đoạn code trên, output bạn nhận được sẽ luôn là 4, mặc dù ta mong rằng output sẽ phải là 0, 1, 2, 3 sau mỗi 3s.
Tại sao lại như thế? Để hiểu rõ lý do, đương nhiên bạn cần nắm vững kiến thức về closure của JavaScript, bởi vì người phỏng vấn đang kiểm tra bạn về nó cơ mà!
Lý do là bởi vì hàm setTimeout sẽ tạo ra 1 function (closure) có thể truy cập phạm vi bên ngoài nó, vòng loop sẽ chứa index i. Sau 3s, hàm được thực thi và nó sẽ log ra giá trị của i, là giá trị cuối cùng của vòng lặp (4).
Có 1 số cách để viết hàm đúng. Dưới đây tôi sẽ chỉ nêu ra 2 cách:
Ref: https://www.topitworks.com/blogs/phong-van-javascript/
Bởi vì hàm setTimeout nó sẽ thực hiện một hàm mà được chỉ định sau t ms tính từ lúc gọi.
Bạn for 5 lần thì i cuối cùng sẽ có giá trị = 5 tuy nhiên hàm for chạy rất nhanh và nó sẽ thực hiện xong trước tất cả các hàm mà bạn set timeout chưa kịp chạy. Đến khi các hàm chỉ định bởi setTime chạy thì hàm for đã thực hiện xong rồi và tất nhiên lúc này chỉ thu được i =5.
Đọc cuốn YDKJS để hiểu rõ về internals của JS, chính xác hơn là ở đây.
tl;dr: Bởi vì
for(var i = ...
,i
làvar
nên scope củai
sẽ là toàn bộ function nơi vòng lặp được chạy (hoặc global scope nếu ở ngoài). Mỗi lần gọisetTimeout()
thì callback bên trong sẽ reference tới cùng biến i đó => khi vòng lặp kết thúc và callback được chạy thì tất cả sẽ đều in ra 5.Để fix thì chỉ cần bắt callback trỏ tới 1 bản i duy nhất của nó, khỏi phải xài chùa i nữa.
cám ơn các bác, để em tìm hiểu thêm về closure
Ko phải closure mà là event loop mới giải đáp được câu hỏi của em.
Xem cái này nhé
oh, ok anh. Em còn mơ hồ về cái này quá, tình hình là phải còn cày js dài dai
Đây là mô phỏng JS runtime environment
Đây là code của em anh viết lại dạng đơn giản hơn:
Các bước thực thi sẽ như sau:
Mô tả ở trên có thể gọi là cơ chế hoạt động của JS Event Loop
anh nhiệt tình quá. cái e cứ thắc mắc là tại sao i = 0 < 5 sao nó không in ra 0 sau 1 giây mà phải chạy đến tận 5 mới in. giờ thì giác ngộ rồi . cảm ơn anh lần nữa