Câu trả lời của Fabrício là tại chỗ; nhưng tôi muốn bổ sung cho câu trả lời của anh ấy bằng một cái gì đó ít kỹ thuật hơn, nó tập trung vào một sự tương tự để giúp giải thích khái niệm về sự không điển hình .
Tương tự ...
Hôm qua, công việc tôi đang làm yêu cầu một số thông tin từ một đồng nghiệp. Tôi gọi anh ta dậy; Đây là cuộc trò chuyện đã diễn ra như thế nào:
Me : Hi Bob, tôi cần phải biết làm thế nào chúng ta foo 'd các thanh ' d tuần trước. Jim muốn báo cáo về nó, và bạn là người duy nhất biết chi tiết về nó.
Bob : Chắc chắn rồi, nhưng tôi sẽ mất khoảng 30 phút?
Tôi : Đó là Bob tuyệt vời. Trả lại cho tôi khi bạn có thông tin!
Lúc này, tôi cúp điện thoại. Vì tôi cần thông tin từ Bob để hoàn thành báo cáo của mình, tôi đã rời khỏi báo cáo và đi uống cà phê, sau đó tôi bắt gặp một số email. 40 phút sau (Bob chậm), Bob gọi lại và đưa cho tôi thông tin tôi cần. Tại thời điểm này, tôi đã tiếp tục công việc của mình với báo cáo của mình, vì tôi có tất cả thông tin tôi cần.
Thay vào đó hãy tưởng tượng nếu cuộc trò chuyện đã diễn ra như thế này;
Me : Hi Bob, tôi cần phải biết làm thế nào chúng ta foo 'd các thanh ' d tuần trước. Jim muốn có một bản báo cáo về nó, và bạn là người duy nhất biết chi tiết về nó.
Bob : Chắc chắn rồi, nhưng tôi sẽ mất khoảng 30 phút?
Tôi : Đó là Bob tuyệt vời. Tôi sẽ đợi.
Và tôi ngồi đó và chờ đợi. Và chờ đợi. Và chờ đợi. Trong 40 phút. Không làm gì ngoài việc chờ đợi. Cuối cùng, Bob đưa cho tôi thông tin, chúng tôi gác máy và tôi đã hoàn thành báo cáo của mình. Nhưng tôi đã mất 40 phút năng suất.
Đây là hành vi không đồng bộ so với đồng bộ
Đây chính xác là những gì đang xảy ra trong tất cả các ví dụ trong câu hỏi của chúng tôi. Tải hình ảnh, tải tệp ra khỏi đĩa và yêu cầu một trang qua AJAX đều là các hoạt động chậm (trong bối cảnh điện toán hiện đại).
Thay vì chờ các hoạt động chậm này hoàn thành, JavaScript cho phép bạn đăng ký chức năng gọi lại sẽ được thực thi khi hoạt động chậm hoàn thành. Tuy nhiên, trong thời gian đó, JavaScript sẽ tiếp tục thực thi mã khác. Việc JavaScript thực thi mã khác trong khi chờ hoạt động chậm hoàn thành khiến hành vi không đồng bộ . Nếu JavaScript chờ đợi để hoạt động hoàn tất trước khi thực hiện bất kỳ mã nào khác, thì đây sẽ là hành vi đồng bộ .
var outerScopeVar;
var img = document.createElement('img');
// Here we register the callback function.
img.onload = function() {
// Code within this function will be executed once the image has loaded.
outerScopeVar = this.width;
};
// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);
Trong đoạn mã trên, chúng tôi yêu cầu JavaScript tải lolcat.png
, đây là thao tác sloooow . Hàm gọi lại sẽ được thực thi khi hoạt động chậm này đã được thực hiện, nhưng trong thời gian đó, JavaScript sẽ tiếp tục xử lý các dòng mã tiếp theo; tức alert(outerScopeVar)
.
Đây là lý do tại sao chúng ta thấy cảnh báo hiển thị undefined
; kể từ khi alert()
được xử lý ngay lập tức, thay vì sau khi hình ảnh đã được tải.
Để sửa mã của chúng tôi, tất cả những gì chúng tôi phải làm là di chuyển alert(outerScopeVar)
mã vào chức năng gọi lại. Do đó, chúng ta không còn cần outerScopeVar
biến được khai báo là biến toàn cục.
var img = document.createElement('img');
img.onload = function() {
var localScopeVar = this.width;
alert(localScopeVar);
};
img.src = 'lolcat.png';
Bạn sẽ luôn thấy một cuộc gọi lại được chỉ định là một hàm, bởi vì đó là cách * duy nhất trong JavaScript để xác định một số mã, nhưng không thực hiện nó cho đến sau này.
Do đó, trong tất cả các ví dụ của chúng tôi, function() { /* Do something */ }
là cuộc gọi lại; để sửa tất cả các ví dụ, tất cả những gì chúng ta phải làm là chuyển mã cần phản hồi của thao tác vào đó!
* Về mặt kỹ thuật bạn cũng có thể sử dụng eval()
, nhưng eval()
là xấu cho mục đích này
Làm thế nào để tôi giữ cho người gọi của tôi chờ đợi?
Bạn hiện có thể có một số mã tương tự như thế này;
function getWidthOfImage(src) {
var outerScopeVar;
var img = document.createElement('img');
img.onload = function() {
outerScopeVar = this.width;
};
img.src = src;
return outerScopeVar;
}
var width = getWidthOfImage('lolcat.png');
alert(width);
Tuy nhiên, bây giờ chúng tôi biết rằng điều đó return outerScopeVar
xảy ra ngay lập tức; trước khi onload
hàm gọi lại đã cập nhật biến. Điều này dẫn đến việc getWidthOfImage()
trở lại undefined
, và undefined
được cảnh báo.
Để khắc phục điều này, chúng ta cần cho phép chức năng gọi getWidthOfImage()
để đăng ký gọi lại, sau đó di chuyển cảnh báo về chiều rộng để nằm trong cuộc gọi lại đó;
function getWidthOfImage(src, cb) {
var img = document.createElement('img');
img.onload = function() {
cb(this.width);
};
img.src = src;
}
getWidthOfImage('lolcat.png', function (width) {
alert(width);
});
... Như trước đây, lưu ý rằng chúng tôi đã có thể xóa các biến toàn cục (trong trường hợp này width
).