Tại sao biến của tôi không được thay đổi sau khi tôi sửa đổi nó bên trong hàm? - Tham chiếu mã không đồng bộ


738

Cho các ví dụ sau, tại sao outerScopeVarkhông xác định trong mọi trường hợp?

var outerScopeVar;

var img = document.createElement('img');
img.onload = function() {
    outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);

var outerScopeVar;
setTimeout(function() {
    outerScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);

// Example using some jQuery
var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
});
alert(outerScopeVar);

// Node.js example
var outerScopeVar;
fs.readFile('./catdog.html', function(err, data) {
    outerScopeVar = data;
});
console.log(outerScopeVar);

// with promises
var outerScopeVar;
myPromise.then(function (response) {
    outerScopeVar = response;
});
console.log(outerScopeVar);

// geolocation API
var outerScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
    outerScopeVar = pos;
});
console.log(outerScopeVar);

Tại sao nó xuất ra undefinedtrong tất cả các ví dụ này? Tôi không muốn giải pháp, tôi muốn biết tại sao điều này xảy ra.


Lưu ý: Đây là một câu hỏi chính tắc cho tính không điển hình của JavaScript . Vui lòng cải thiện câu hỏi này và thêm các ví dụ đơn giản hơn mà cộng đồng có thể xác định.



@Dukeling cảm ơn, tôi khá chắc chắn rằng tôi đã bình luận với liên kết đó nhưng rõ ràng có một số bình luận bị thiếu. Ngoài ra, liên quan đến chỉnh sửa của bạn: Tôi tin rằng việc có "chính tắc" và "không điển hình" trong tiêu đề sẽ giúp ích khi tìm kiếm câu hỏi này để đánh dấu một câu hỏi khác là một bản sao. Và tất nhiên, nó cũng giúp tìm ra câu hỏi này từ Google khi tìm kiếm những lời giải thích không điển hình.
Fabrício Matté

5
Đặt thêm một chút suy nghĩ, "chủ đề không điển hình chính tắc" hơi nặng về tiêu đề, "tham chiếu mã không đồng bộ" đơn giản và khách quan hơn. Tôi cũng tin rằng hầu hết mọi người tìm kiếm "không đồng bộ" thay vì "không đồng bộ".
Fabrício Matté

2
Một số người khởi tạo biến của họ trước khi gọi hàm. Làm thế nào về việc thay đổi tiêu đề mà bằng cách nào đó cũng đại diện cho điều đó? Giống như "Tại sao biến của tôi không được thay đổi sau khi tôi sửa đổi nó bên trong hàm?" ?
Felix Kling

Trong tất cả các ví dụ mã mà bạn đã đề cập ở trên, "alert (outsScopeVar);" thực thi NGAY BÂY GIỜ, trong khi việc gán giá trị cho "outsScopeVar" xảy ra LATER (không đồng bộ).
tái cấu trúc

Câu trả lời:


581

Một từ trả lời: không điển hình .

Lời nói đầu

Chủ đề này đã được lặp đi lặp lại ít nhất vài nghìn lần, ở đây, trong Stack Overflow. Do đó, trước hết tôi muốn chỉ ra một số tài nguyên cực kỳ hữu ích:


Câu trả lời cho câu hỏi trong tầm tay

Trước tiên hãy theo dõi hành vi phổ biến. Trong tất cả các ví dụ, phần outerScopeVarđược sửa đổi bên trong hàm . Hàm đó rõ ràng không được thực thi ngay lập tức, nó đang được gán hoặc chuyển qua làm đối số. Đó là những gì chúng ta gọi là một cuộc gọi lại .

Bây giờ câu hỏi là, khi nào cuộc gọi lại được gọi?

Nó phụ thuộc vào trường hợp. Hãy thử theo dõi một số hành vi phổ biến một lần nữa:

  • img.onloadđôi khi có thể được gọi trong tương lai , khi (và nếu) hình ảnh đã được tải thành công.
  • setTimeoutđôi khi có thể được gọi trong tương lai , sau khi sự chậm trễ đã hết và thời gian chờ chưa bị hủy bỏ clearTimeout. Lưu ý: ngay cả khi sử dụng 0làm độ trễ, tất cả các trình duyệt đều có giới hạn thời gian chờ tối thiểu (được chỉ định là 4ms trong thông số HTML5).
  • $.postCuộc gọi lại của jQuery có thể được gọi đôi khi trong tương lai , khi (và nếu) yêu cầu Ajax đã được hoàn thành thành công.
  • Node.js fs.readFilecó thể được gọi vào lúc nào đó trong tương lai , khi tệp đã được đọc thành công hoặc bị lỗi.

Trong mọi trường hợp, chúng tôi có một cuộc gọi lại có thể chạy trong tương lai . "Đôi khi trong tương lai" này là những gì chúng ta gọi là dòng không đồng bộ .

Thực thi không đồng bộ được đẩy ra khỏi luồng đồng bộ. Đó là, mã không đồng bộ sẽ không bao giờ thực thi trong khi ngăn xếp mã đồng bộ đang thực thi. Đây là ý nghĩa của JavaScript là đơn luồng.

Cụ thể hơn, khi công cụ JS không hoạt động - không thực thi một chồng mã (a) mã đồng bộ - nó sẽ thăm dò các sự kiện có thể đã kích hoạt các cuộc gọi lại không đồng bộ (ví dụ hết thời gian chờ, đã nhận được phản hồi mạng). Đây được coi là Vòng lặp sự kiện .

Nghĩa là, mã không đồng bộ được tô sáng trong các hình dạng màu đỏ được vẽ bằng tay chỉ có thể thực thi sau khi tất cả các mã đồng bộ còn lại trong các khối mã tương ứng của chúng đã được thực thi:

mã async được tô sáng

Nói tóm lại, các hàm gọi lại được tạo đồng bộ nhưng được thực thi không đồng bộ. Bạn không thể dựa vào việc thực thi một hàm không đồng bộ cho đến khi bạn biết nó đã thực thi và làm thế nào để làm điều đó?

Nó là đơn giản, thực sự. Logic phụ thuộc vào việc thực thi chức năng không đồng bộ nên được khởi động / gọi từ bên trong chức năng không đồng bộ này. Ví dụ, di chuyển alerts và console.logs bên trong hàm gọi lại sẽ cho kết quả mong đợi, vì kết quả có sẵn tại thời điểm đó.

Thực hiện logic gọi lại của riêng bạn

Thường thì bạn cần làm nhiều việc hơn với kết quả từ hàm không đồng bộ hoặc thực hiện các thao tác khác nhau với kết quả tùy thuộc vào vị trí của hàm không đồng bộ được gọi. Hãy giải quyết một ví dụ phức tạp hơn một chút:

var outerScopeVar;
helloCatAsync();
alert(outerScopeVar);

function helloCatAsync() {
    setTimeout(function() {
        outerScopeVar = 'Nya';
    }, Math.random() * 2000);
}

Lưu ý: Tôi đang sử dụng setTimeoutvới một sự chậm trễ ngẫu nhiên như một chức năng không đồng bộ chung chung, ví dụ tương tự áp dụng cho Ajax, readFile, onloadvà bất kỳ dòng chảy không đồng bộ khác.

Ví dụ này rõ ràng bị vấn đề tương tự như các ví dụ khác, nó không chờ cho đến khi hàm không đồng bộ thực thi.

Hãy giải quyết nó bằng cách thực hiện một hệ thống gọi lại của chính chúng ta. Trước hết, chúng ta thoát khỏi sự xấu xí outerScopeVarđó là hoàn toàn vô dụng trong trường hợp này. Sau đó, chúng tôi thêm một tham số chấp nhận một đối số chức năng, gọi lại của chúng tôi. Khi hoạt động không đồng bộ kết thúc, chúng tôi gọi cuộc gọi lại này đi qua kết quả. Việc thực hiện (vui lòng đọc các bình luận theo thứ tự):

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    alert(result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as argument from the helloCatAsync call
function helloCatAsync(callback) {
    // 3. Start async operation:
    setTimeout(function() {
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

Đoạn mã của ví dụ trên:

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
console.log("1. function called...")
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    console.log("5. result is: ", result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as argument from the helloCatAsync call
function helloCatAsync(callback) {
    console.log("2. callback here is the function passed as argument above...")
    // 3. Start async operation:
    setTimeout(function() {
    console.log("3. start async operation...")
    console.log("4. finished async operation, calling the callback, passing the result...")
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

Thông thường trong các trường hợp sử dụng thực tế, API DOM và hầu hết các thư viện đã cung cấp chức năng gọi lại (việc helloCatAsynctriển khai trong ví dụ minh họa này). Bạn chỉ cần truyền chức năng gọi lại và hiểu rằng nó sẽ thực hiện ngoài luồng đồng bộ và cơ cấu lại mã của bạn để phù hợp với điều đó.

Bạn cũng sẽ nhận thấy rằng do tính chất không đồng bộ, không thể return có giá trị từ luồng không đồng bộ trở lại luồng đồng bộ nơi gọi lại được xác định, vì các cuộc gọi lại không đồng bộ được thực thi lâu sau khi mã đồng bộ đã thực hiện xong.

Thay vì lấy returnmột giá trị từ một cuộc gọi lại không đồng bộ, bạn sẽ phải sử dụng mẫu gọi lại hoặc ... Hứa hẹn.

Hứa

Mặc dù có nhiều cách để giữ cho địa ngục gọi lại bằng vanilla JS, nhưng những lời hứa đang ngày càng phổ biến và hiện đang được chuẩn hóa trong ES6 (xem Promise - MDN ).

Promise (còn gọi là Futures) cung cấp cách đọc mã không đồng bộ tuyến tính hơn và dễ chịu hơn, nhưng giải thích toàn bộ chức năng của chúng nằm ngoài phạm vi của câu hỏi này. Thay vào đó, tôi sẽ để lại những tài nguyên tuyệt vời này cho những người quan tâm:


Đọc thêm tài liệu về tính không điển hình của JavaScript

  • The Art of Node - Callbacks giải thích mã không đồng bộ và gọi lại rất tốt với các ví dụ về vanilla JS và mã Node.js.

Lưu ý: Tôi đã đánh dấu câu trả lời này là Community Wiki, do đó bất kỳ ai có ít nhất 100 danh tiếng đều có thể chỉnh sửa và cải thiện nó! Xin vui lòng cải thiện câu trả lời này hoặc gửi câu trả lời hoàn toàn mới nếu bạn cũng muốn.

Tôi muốn biến câu hỏi này thành một chủ đề kinh điển để trả lời các vấn đề không điển hình không liên quan đến Ajax (có cách nào để trả lời phản hồi từ một cuộc gọi AJAX? Vì vậy, chủ đề này cần sự giúp đỡ của bạn tốt và hữu ích nhất có thể !


1
Trong ví dụ cuối cùng của bạn, có một lý do cụ thể tại sao bạn sử dụng các hàm ẩn danh hoặc nó sẽ hoạt động tương tự bằng cách sử dụng các hàm được đặt tên?
JDelage

1
Các ví dụ mã hơi kỳ lạ khi bạn khai báo hàm sau khi gọi nó. Hoạt động vì cẩu tất nhiên, nhưng nó có chủ ý?
Bergi

2
Là bế tắc. felix kling đang chỉ vào câu trả lời của bạn và bạn đang chỉ vào câu trả lời của felix
Mahi

1
Bạn cần hiểu rằng mã vòng tròn màu đỏ chỉ là không đồng bộ vì nó đang được thực thi bởi các chức năng javascript async. Đây là một tính năng của công cụ javascript của bạn - cho dù đó là Node.js hay trình duyệt. Nó không đồng bộ vì nó được truyền vào dưới dạng "gọi lại" cho một chức năng thực chất là một hộp đen (được triển khai trong C, v.v.). Đối với nhà phát triển không may mắn, họ không đồng bộ ... chỉ vì. Nếu bạn muốn viết chức năng async của riêng mình, bạn phải hack nó bằng cách gửi nó đến SetTimeout (myfunc, 0). Bạn có nên làm điều đó? Một cuộc tranh luận khác .... có lẽ là không.
Sean Anderson

@Fovenio Tôi đã tìm thông số kỹ thuật xác định "> = 4ms kẹp", nhưng không thể tìm thấy nó - Tôi tìm thấy một số đề cập đến cơ chế tương tự (để kẹp các cuộc gọi lồng nhau) trên MDN - developer.mozilla.org/en-US/docs / Web / API /, - có ai có liên kết đến phần bên phải của thông số HTML không.
Sebi

156

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)vào chức năng gọi lại. Do đó, chúng ta không còn cần outerScopeVarbiế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 outerScopeVarxảy ra ngay lập tức; trước khi onloadhà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).


4
Nhưng làm thế nào để cảnh báo hoặc gửi đến bàn điều khiển hữu ích nếu bạn muốn sử dụng các kết quả trong một phép tính khác hoặc lưu trữ nó trong một biến đối tượng?
Ken Ingram

73

Đây là một câu trả lời ngắn gọn hơn cho những người đang tìm kiếm một tài liệu tham khảo nhanh cũng như một số ví dụ sử dụng lời hứa và async / await.

Bắt đầu với cách tiếp cận ngây thơ (không hoạt động) cho một hàm gọi phương thức không đồng bộ (trong trường hợp này setTimeout) và trả về một thông báo:

function getMessage() {
  var outerScopeVar;
  setTimeout(function() {
    outerScopeVar = 'Hello asynchronous world!';
  }, 0);
  return outerScopeVar;
}
console.log(getMessage());

undefinedđược đăng nhập trong trường hợp này vì getMessagetrả về trước khi setTimeoutgọi lại và cập nhật outerScopeVar.

Hai cách chính để giải quyết nó là sử dụng các cuộc gọi lạilời hứa :

Gọi lại

Thay đổi ở đây là getMessagechấp nhận một callbacktham số sẽ được gọi để đưa kết quả trở lại mã gọi khi có sẵn.

function getMessage(callback) {
  setTimeout(function() {
    callback('Hello asynchronous world!');
  }, 0);
}
getMessage(function(message) {
  console.log(message);
});

Hứa

Promise cung cấp một giải pháp thay thế linh hoạt hơn so với các cuộc gọi lại bởi vì chúng có thể được kết hợp một cách tự nhiên để phối hợp nhiều hoạt động không đồng bộ. Một Promises / A + thực hiện tiêu chuẩn được quy định tại natively Node.js (0.12+) và nhiều trình duyệt hiện nay, mà còn được thực hiện trong các thư viện như BluebirdQ .

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello asynchronous world!');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);  
});

Trì hoãn jQuery

jQuery cung cấp chức năng tương tự như lời hứa với Trì hoãn.

function getMessage() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve('Hello asynchronous world!');
  }, 0);
  return deferred.promise();
}

getMessage().done(function(message) {
  console.log(message);  
});

không đồng bộ / đang chờ

Nếu môi trường JavaScript của bạn bao gồm hỗ trợ cho asyncawait(như Node.js 7.6+), thì bạn có thể sử dụng lời hứa một cách đồng bộ trong các asynchàm:

function getMessage () {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello asynchronous world!');
        }, 0);
    });
}

async function main() {
    let message = await getMessage();
    console.log(message);
}

main();

Mẫu của bạn trên Promise về cơ bản là những gì tôi đã tìm kiếm trong vài giờ qua. Ví dụ của bạn rất hay và giải thích về Lời hứa cùng một lúc. Tại sao điều này không phải là bất cứ nơi nào khác là boggling.
Vincent P

Điều này là tốt, nhưng nếu bạn cần gọi getMessage () với các tham số thì sao? Làm thế nào bạn sẽ viết ở trên trong kịch bản đó?
Chiwda

2
@Chiwda Bạn chỉ cần đặt tham số gọi lại lần cuối : function getMessage(param1, param2, callback) {...}.
JohnnyHK

Tôi đang thử async/awaitmẫu của bạn , nhưng tôi đang gặp vấn đề. Thay vì bắt đầu a new Promise, tôi đang thực hiện một .Get()cuộc gọi và do đó không có quyền truy cập vào bất kỳ resolve()phương thức nào . Do đó, tôi getMessage()đang trả lại Lời hứa và không phải là kết quả. Bạn có thể chỉnh sửa câu trả lời của mình một chút để hiển thị cú pháp làm việc cho việc này không?
InteXX

@InteXX Tôi không chắc ý của bạn về việc thực hiện .Get()cuộc gọi. Có lẽ tốt nhất để gửi một câu hỏi mới.
JohnnyHK

56

Để nói rõ ràng, chiếc cốc đại diện outerScopeVar.

Các hàm không đồng bộ giống như ...

cuộc gọi không đồng bộ cho cà phê


14
Trong khi cố gắng làm cho một chức năng không đồng bộ hoạt động đồng bộ sẽ cố gắng uống cà phê trong 1 giây và để nó rót vào lòng bạn sau 1 phút.
Teepeemm

Nếu nó được nêu rõ ràng, tôi không nghĩ câu hỏi sẽ được hỏi, Không?
bông cải xanh

2
@ broccoli2000 Điều đó không có nghĩa là câu hỏi đó là hiển nhiên, nhưng rõ ràng cái cốc thể hiện trong bản vẽ là gì :)
Johannes Fahrenkrug

14

Các câu trả lời khác là tuyệt vời và tôi chỉ muốn cung cấp một câu trả lời thẳng về vấn đề này. Chỉ giới hạn các cuộc gọi không đồng bộ jQuery

Tất cả các cuộc gọi ajax (bao gồm $.gethoặc $.posthoặc $.ajax) là không đồng bộ.

Xem xét ví dụ của bạn

var outerScopeVar;  //line 1
$.post('loldog', function(response) {  //line 2
    outerScopeVar = response;
});
alert(outerScopeVar);  //line 3

Việc thực thi mã bắt đầu từ dòng 1, khai báo biến và kích hoạt và cuộc gọi không đồng bộ trên dòng 2, (nghĩa là yêu cầu bài đăng) và nó tiếp tục thực hiện từ dòng 3, mà không cần chờ yêu cầu bài viết hoàn thành việc thực hiện.

Hãy nói rằng yêu cầu bài đăng mất 10 giây để hoàn thành, giá trị của outerScopeVarsẽ chỉ được đặt sau 10 giây đó.

Để thử

var outerScopeVar; //line 1
$.post('loldog', function(response) {  //line 2, takes 10 seconds to complete
    outerScopeVar = response;
});
alert("Lets wait for some time here! Waiting is fun");  //line 3
alert(outerScopeVar);  //line 4

Bây giờ khi bạn thực hiện điều này, bạn sẽ nhận được một cảnh báo trên dòng 3. Bây giờ hãy đợi một thời gian cho đến khi bạn chắc chắn rằng yêu cầu bài đăng đã trả về một số giá trị. Sau đó, khi bạn nhấp vào OK, trên hộp cảnh báo, cảnh báo tiếp theo sẽ in giá trị mong đợi, vì bạn đã đợi nó.

Trong kịch bản đời thực, mã trở thành,

var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
    alert(outerScopeVar);
});

Tất cả các mã phụ thuộc vào các cuộc gọi không đồng bộ, được di chuyển bên trong khối không đồng bộ hoặc bằng cách chờ đợi các cuộc gọi không đồng bộ.


or by waiting on the asynchronous callsLàm thế nào để làm điều đó?
InteXX

@InteXX Bằng cách sử dụng phương thức gọi lại
Teja

Bạn có một ví dụ cú pháp nhanh?
InteXX

11

Trong tất cả các kịch bản outerScopeVarnày được sửa đổi hoặc gán một giá trị không đồng bộ hoặc xảy ra trong thời gian sau đó (chờ hoặc nghe một sự kiện nào đó xảy ra), trong đó việc thực thi hiện tại sẽ không chờ .outerScopeVar = undefined

Hãy thảo luận về từng ví dụ (Tôi đã đánh dấu phần được gọi là không đồng bộ hoặc bị trì hoãn để một số sự kiện xảy ra):

1.

nhập mô tả hình ảnh ở đây

Ở đây chúng tôi đăng ký một danh sách sự kiện sẽ được thực hiện theo sự kiện cụ thể đó. Đang tải hình ảnh. Sau đó, việc thực hiện hiện tại liên tục với các dòng tiếp theo img.src = 'lolcat.png';alert(outerScopeVar);trong khi đó sự kiện có thể không xảy ra. tức là, funtion img.onloadchờ cho hình ảnh được tải lên, không đồng bộ. Điều này sẽ xảy ra tất cả các ví dụ sau đây - sự kiện có thể khác nhau.

2.

2

Ở đây, sự kiện hết thời gian đóng vai trò, sẽ gọi trình xử lý sau thời gian đã chỉ định. Nó đây 0, nhưng nó vẫn đăng ký một sự kiện không đồng bộ, nó sẽ được thêm vào vị trí cuối cùng của việc Event Queuethực thi, điều này làm cho độ trễ được bảo đảm.

3.

nhập mô tả hình ảnh ở đây Lần này gọi lại ajax.

4.

nhập mô tả hình ảnh ở đây

Nút có thể được coi là vua của mã hóa không đồng bộ. Đây là chức năng được đánh dấu được đăng ký như một trình xử lý gọi lại sẽ được thực thi sau khi đọc tệp được chỉ định.

5.

nhập mô tả hình ảnh ở đây

Lời hứa hiển nhiên (một cái gì đó sẽ được thực hiện trong tương lai) là không đồng bộ. xem sự khác biệt giữa Trì hoãn, Hứa hẹn và Tương lai trong JavaScript là gì?

https://www.quora.com/Whats-the-difference-b between-a-promise-and-a-callback-in-Javascript

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.