Tek kelimeyle cevap: asenkroniklik .
Önsözler
Bu konu, Stack Overflow'da burada en az binlerce kez tekrarlandı. Bu nedenle, ilk önce bazı son derece yararlı kaynaklara dikkat çekmek istiyorum:
Eldeki sorunun cevabı
Önce ortak davranışı izleyelim. Tüm örneklerde, outerScopeVar
bir işlev içinde değiştirilir . Bu işlev hemen yürütülmez, bağımsız değişken olarak atanır veya iletilir. Biz buna geri arama diyoruz .
Şimdi soru şu, bu geri arama ne zaman çağrılıyor?
Bu duruma bağlıdır. Ortak davranışları tekrar izlemeye çalışalım:
img.onload
görüntünün başarılı bir şekilde yüklendiği (ve yüklendiği durumlarda) gelecekte bir zamanda çağrılabilir .
setTimeout
gecikme süresi dolduktan ve zaman aşımı tarafından iptal edilmedikten sonra gelecekte bir zamanda çağrılabilir clearTimeout
. Not: 0
gecikme olarak kullanıldığında bile , tüm tarayıcıların minimum zaman aşımı gecikme sınırı vardır (HTML5 spesifikasyonunda 4 ms olarak belirtilir).
- jQuery'nin
$.post
geri çağrısı , Ajax isteği başarıyla tamamlandığında (ve yapıldığında) gelecekte bir zamanda çağrılabilir .
- Düğüm.js , gelecekte dosya başarıyla okunduğunda veya bir hata atıldığında gelecekte
fs.readFile
çağrılabilir .
Her durumda, ileride çalışabilecek bir geri çağrımız var . Bu "gelecekte bir zaman" asenkron akış olarak adlandırdığımız şeydir .
Asenkron yürütme, senkron akışın dışına itilir. Yani, eşzamansız kod yığını yürütülürken eşzamansız kod hiçbir zaman yürütülmez. JavaScript'in tek iş parçacıklı olmasının anlamı budur.
Daha spesifik olarak, JS motoru boştayken - (a) senkron kod yığınını yürütmüyorsa - zaman uyumsuz geri çağrıları tetikleyebilecek olaylar (örneğin, süresi dolmuş zaman aşımı, ağ yanıtı aldı) 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 eşzamansız kod, yalnızca ilgili kod bloklarında kalan tüm eşzamanlı kod yürütüldükten sonra yürütülebilir:
Kısacası, geri arama işlevleri eşzamanlı olarak oluşturulur, ancak eşzamansız olarak yürütülür. Yürütüldüğünü ve nasıl yapılacağını bilmeden eşzamansız bir işlevin yürütülmesine güvenemezsiniz?
Gerçekten basit. Eşzamansız işlev yürütmesine bağlı olan mantık, bu eşzamansız işlevin içinden başlatılmalı / çağrılmalıdır. Örneğin, alert
s ve console.log
s tuşlarını geri arama işlevinin içine taşımak, sonuç bu noktada mevcut olduğundan beklenen sonucu verir.
Kendi geri arama mantığınızı uygulama
Genellikle, eşzamansız bir işlevin sonucuyla daha fazla şey yapmanız veya eşzamansız işlevin nereden çağrıldığına bağlı olarak sonuçla farklı şeyler yapmanız gerekir. Biraz daha karmaşık bir örneği 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 muzdariptir, eşzamansız işlev yürütülene kadar beklemez.
Kendi geri arama sistemimizi uygulayalım. Öncelikle, outerScopeVar
bu durumda tamamen işe yaramaz olan çirkinden kurtuluyoruz . Sonra geri çağrımız olan bir işlev bağımsız değişkenini kabul eden bir parametre ekliyoruz. Eşzamansız işlem bittiğinde, bu geri çağrıyı sonucu geçen olarak adlandırırız. Uygulama (lütfen sırayla yorumları okuyun):
// 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 snippet'i:
// 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şlevselliğini sağlar ( helloCatAsync
bu örnekte uygulama). Yalnızca geri çağrı işlevini geçmeniz ve senkron akıştan yürütüldüğünü anlamanız ve kodunuzu buna göre yeniden yapılandırmanız gerekir.
Ayrıca, eşzamansız return
kod zaten yürütme işlemi bittikten çok sonra yürütüldüğünden , eşzamansız doğa nedeniyle, eşzamansız akıştan geri çağrının tanımlandığı eşzamanlı akışa geri dönüşü olmayan bir değere geri döndürülmesinin imkansız olduğunu göreceksiniz .
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
Vanilla JS ile geri çağırma cehennemini korumanın yolları olsa da , vaatler popülerlik kazanıyor ve şu anda ES6'da standartlaştırılıyor (bkz. Promise - MDN ).
Vaatler (diğer adıyla Futures) asenkron kodun daha doğrusal ve dolayısıyla hoş bir okumasını sağlar, ancak tüm işlevlerini açıklamak bu sorunun kapsamı dışındadır. Bunun yerine, bu mükemmel kaynakları ilgilenenler için bırakacağım:
JavaScript eşzamansızlığı hakkında daha fazla okuma materyali
- Art of Node - Callbacks , vanilla JS örnekleri ve Node.js koduyla eşzamansız kodu ve geri çağrıları çok iyi açıklar.
Not: Bu yanıtı Topluluk Wiki olarak işaretledim, bu nedenle en az 100 itibara sahip olan herkes düzenleyebilir ve geliştirebilir! Lütfen bu yanıtı geliştirmekten çekinmeyin veya isterseniz yeni bir yanıt gönderin.
Ajax ile ilgisiz olan asenkroniklik sorunlarını cevaplamak için bu soruyu kanonik bir konuya dönüştürmek istiyorum (bunun için bir AJAX çağrısından yanıt nasıl geri gönderilir? ), Bu nedenle bu konunun mümkün olduğunca iyi ve yardımcı olması için yardımınıza ihtiyacı var !