Tek kelimeyle cevap: eşzamansızlık .
Önsözler
Bu konu, Stack Overflow'ta en az birkaç kez yinelenmiştir. Bu nedenle, ilk önce, bazı son derece yararlı kaynakları belirtmek isterim:
Eldeki sorunun cevabı
İlk önce ortak davranışı izleyelim. Tüm örneklerde, outerScopeVar
bir fonksiyonun içinde değiştirilir . Bu fonksiyon açıkça hemen çalıştırılmaz, atanır veya bir argüman olarak iletilir. Buna geri çağrı diyoruz .
Şimdi soru şu, ne zaman geri arama denir?
Bu duruma bağlı. Bazı ortak davranışları tekrar izlemeye çalışalım:
img.onload
Gelecekte bazen görülebilir , (ve eğer) görüntünün başarıyla yüklendiği zaman.
setTimeout
gelecekte gecikme süresi dolduktan ve zaman aşımı tarafından iptal edilmedikten sonra aranabilir clearTimeout
. Not: 0
Gecikme olarak kullanırken bile , tüm tarayıcılarda minimum zaman aşımı gecikme sınırı vardır (HTML5 spesifikasyonunda 4ms olarak belirtilmiştir).
- jQuery'nin
$.post
geri çağrısı , gelecekte Ajax isteğinin başarılı bir şekilde tamamlandığında (ve eğer) gelecekte çağrılabilir .
- Node.js , gelecekte başarılı bir şekilde okunduğunda veya bir hata atıldığında ileride
fs.readFile
çağrılabilir .
Her durumda, ileride bir süre geçebilecek olan bir geri çağırma var . Bu "gelecekte bir süre", zaman uyumsuz akış olarak adlandırdığımız şeydir .
Eşzamansız çalıştırma, eşzamanlı akıştan dışarı itilir. Başka bir deyişle, zaman uyumsuz kod, zaman uyumlu kod yığını yürütülürken yürütülmez. JavaScript'in tek iş parçacıklı olmasının anlamı budur.
Daha spesifik olarak, JS motoru rölantide olduğunda - (a) senkron kodunun bir yığınını çalıştırmazken, asenkron geri aramaları tetiklemiş olabilecek olayları (örn; süresi dolmuş zaman aşımı, alınan ağ yanıtı) ardı ardına alır ve bunları birbiri ardına yürütür. Bu Olay Döngüsü olarak kabul edilir .
Yani, elle çizilmiş kırmızı şekillerde vurgulanan asenkron kod, sadece ilgili kod bloklarında kalan tüm senkron kod çalıştırıldıktan sonra çalıştırılabilir:
Kısacası, geri çağırma işlevleri eşzamanlı olarak oluşturulur, ancak eşzamansız olarak yürütülür. Sadece asenkron bir fonksiyonun yürütülmesine güvenip dayanamadığını ve bunun nasıl yapıldığını bilemezsiniz.
Gerçekten basit. Eşzamansız işlev çalıştırmasına bağlı olan mantık, bu eşzamansız fonksiyonun içinden başlatılmalı / çağrılmalıdır. Örneğin, alert
s ve console.log
s'leri geriçağırım işlevi içinde hareket ettirmek beklenen sonucu verir çünkü sonuç bu noktada kullanılabilir.
Kendi geri arama mantığınızı uygulama
Genellikle, bir eşzamansız fonksiyonun sonucu ile daha fazla şey yapmanız veya eşzamansız fonksiyonun çağrıldığı yere bağlı olarak, sonuç ile farklı şeyler yapmanız gerekir. Biraz daha karmaşık bir örnek ele alalım:
var outerScopeVar;
helloCatAsync();
alert(outerScopeVar);
function helloCatAsync() {
setTimeout(function() {
outerScopeVar = 'Nya';
}, Math.random() * 2000);
}
Not: Ben kullanıyorum setTimeout
genel asenkron fonksiyonu olarak rastgele bir gecikme ile, aynı örnek Ajax için geçerlidir readFile
, onload
ve diğer asenkron akışı.
Bu örnek açıkça diğer örneklerle aynı sorundan muzdarip, zaman uyumsuz işlevi yerine getirene kadar beklemiyor.
Kendimize ait bir geri çağırma sistemi uygulayarak başa çıkalım. Öncelikle, outerScopeVar
bu durumda tamamen işe yaramaz olan çirkinlerden kurtuluruz . Ardından bir fonksiyon argümanını kabul eden bir parametre ekleriz: Eşzamansız işlem bittiğinde, sonucu geçen bu geri çağrıyı çağırırız. Uygulama (lütfen yorumları sırayla okuyunuz):
// 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);
}
Yukarıdaki örneğin kod pasajı:
// 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);
}
Çoğu zaman gerçek kullanım durumlarında, DOM API ve çoğu kitaplık zaten geri arama işlevini sağlar ( helloCatAsync
bu örnek örnekteki uygulama). Geri arama işlevini geçmeniz ve senkronize akış dışında çalışacağını anlamanız ve kodunuzu buna uyması için yeniden yapılandırmanız yeterlidir.
Ayrıca, eşzamansız yapı nedeniyle return
, eşzamansız geri çağırma işlemlerinin senkronizasyon kodunun yürütülmesi tamamlandıktan uzun bir süre sonra yürütüldüğü için eşzamanlı olmayan bir akıştan geri çağrının tanımlandığı eşzamanlı akışa kadar bir değerin imkansız olduğunu fark edeceksiniz .
Yerine return
uyumsuz bir geri çağırma arasında bir değer ing, sen ... Promises geri arama desen yararlanmak varsa, ya da olacaktır.
sözler
Geri arama cehennemini vanilya JS ile birlikte uzak tutmanın yolları olsa da , sözler popülaritesi artıyor ve şu anda ES6'da standartlaştırılıyor (bkz. Promise - MDN ).
Sözler (aka Vadeli İşlemler), asenkron kodun okunmasını daha doğrusal ve dolayısıyla hoş bir şekilde sağlar, ancak tüm işlevlerini açıklamak bu sorunun kapsamı dışındadır. Bunun yerine, bu mükemmel kaynakları ilgilenenlere bırakacağım:
JavaScript eşzamansızlığı hakkında daha fazla okuma materyali
- Düğüm Sanatı - Geri aramalar, zaman uyumsuz kodu ve geri aramaları vanilla JS örnekleri ve Node.js kodu ile de çok iyi açıklar.
Not: Bu cevabı Community Wiki olarak işaretledim, bu yüzden en az 100 itibarı olan herkes düzenleyebilir ve geliştirebilir! Lütfen bu cevabı geliştirmek için çekinmeyin veya isterseniz tamamen yeni bir cevap gönderin.
Bu soruyu Ajax ile ilgili olmayan eşzamansızlık sorunlarını cevaplamak için kanonik bir konuya dönüştürmek istiyorum (bunun için bir AJAX çağrısından yanıtı nasıl geri alabilirim? ), Bu nedenle bu konunun mümkün olduğunca iyi ve yararlı olması için yardımınıza ihtiyacı var !