Geri çağrıları düz İngilizce olarak nasıl açıklayabilirim? Arama işlevinden bir bağlam alan bir işlevi başka bir işlevden çağırmaktan ne farkı vardır? Güçleri acemi bir programcıya nasıl açıklanabilir?
Geri çağrıları düz İngilizce olarak nasıl açıklayabilirim? Arama işlevinden bir bağlam alan bir işlevi başka bir işlevden çağırmaktan ne farkı vardır? Güçleri acemi bir programcıya nasıl açıklanabilir?
Yanıtlar:
Genellikle bir uygulamanın bağlamına / durumuna göre farklı işlevler yürütmesi gerekir. Bunun için çağrılacak fonksiyonla ilgili bilgileri saklayacağımız bir değişken kullanıyoruz. İhtiyaçlarına göre uygulama, bu değişkeni çağrılacak fonksiyonla ilgili bilgilerle ayarlayacak ve aynı değişkeni kullanarak fonksiyonu çağıracaktır.
Javascript'te örnek aşağıdadır. Burada işlev hakkında bilgi depoladığımız değişken olarak yöntem argümanını kullanıyoruz.
function processArray(arr, callback) {
var resultArr = new Array();
for (var i = arr.length-1; i >= 0; i--)
resultArr[i] = callback(arr[i]);
return resultArr;
}
var arr = [1, 2, 3, 4];
var arrReturned = processArray(arr, function(arg) {return arg * -1;});
// arrReturned would be [-1, -2, -3, -4]
function(arg)
olarak) processArray(arr,callback)
fonksiyonu
Bu ölüleri basit tutmaya çalışacağım. "Geri arama", ilk işlevi parametre olarak alan başka bir işlev tarafından çağrılan herhangi bir işlevdir. Çoğu zaman, "geri arama" bir şey olduğunda çağrılan bir işlevdir . Yani bir şey programcı-konuşmak bir "olay" olarak adlandırılabilir.
Şu senaryoyu düşünün: Birkaç gün içinde bir paket bekliyorsunuz. Paket komşunuz için bir hediye. Bu nedenle, paketi aldıktan sonra komşularına getirilmesini istersiniz. Şehir dışındasınız ve eşiniz için talimatlar bırakıyorsunuz.
Onlara paketi almasını ve komşularına getirmesini söyleyebilirsiniz. Eşiniz bir bilgisayar kadar aptal olsaydı, kapıda otururlardı ve gelene kadar paketi beklerlerdi (başka bir şey yapmadan) ve sonra bir kez geldiğinde komşularına getirirlerdi. Ama daha iyi bir yol var. Eşinize SADECE paketi aldıklarını, komşuları getirmeleri gerektiğini söyleyin. Daha sonra, paketi alana kadar normal bir şekilde hayat sürebilirler.
Örneğimizde, paketin alınması "olay" dır ve onu komşulara getirmek "geri arama" dır. Sadece Eşiniz "çalışır" talimatlarınız üzerinde paket getirmek için zaman paket geldi. Çok daha iyi!
Bu tür bir düşünce günlük hayatta açıktır, ancak bilgisayarların aynı sağduyu yoktur. Programcıların normalde bir dosyaya nasıl yazdığını düşünün:
fileObject = open(file)
# now that we have WAITED for the file to open, we can write to it
fileObject.write("We are writing to the file.")
# now we can continue doing the other, totally unrelated things our program does
Burada, yazmadan önce dosyanın açılmasını BEKLİYORUZ. Bu, yürütme akışını "engeller" ve programımız yapması gereken diğer şeylerden hiçbirini yapamaz! Bunun yerine bunu yapabilirsek:
# we pass writeToFile (A CALLBACK FUNCTION!) to the open function
fileObject = open(file, writeToFile)
# execution continues flowing -- we don't wait for the file to be opened
# ONCE the file is opened we write to it, but while we wait WE CAN DO OTHER THINGS!
Bazı diller ve çerçevelerle bunu yaptığımız anlaşılıyor. Oldukça havalı! Check out node.js düşünce bu tür bazı gerçek pratik almak için.
open
çalıştığına dair varsayımlar var gibi görünüyor . open
İşletim sisteminin geri çağırma işleminin yapıldığı kara büyüsünü yapmasını beklerken dahili olarak engellenmesi mantıklıdır . Böyle bir durumda sonuçta hiçbir fark yoktur.
Geri çağrıları düz İngilizce olarak nasıl açıklayabilirim?
Basit İngilizce'de, geri arama işlevi, bir Görevi tamamladığında Yöneticisini "geri çağıran" bir İşçi gibidir .
Arama işlevinden bir bağlam alan bir işlevi başka bir işlevden çağırmaktan ne farkı vardır?
Başka bir işlevden bir işlev çağırdığınız doğrudur, ancak anahtar, geri aramanın bir Nesne gibi muamele görmesidir, böylece sistemin işlevine (Strateji Tasarım Deseni gibi) göre hangi İşlevin çağrılacağını değiştirebilirsiniz.
Güçleri acemi bir programcıya nasıl açıklanabilir?
Geri aramaların gücü, sunucudan veri çekmesi gereken AJAX tarzı web sitelerinde kolayca görülebilir. Yeni verilerin indirilmesi biraz zaman alabilir. Geri aramalar olmadan, yeni verileri indirirken tüm Kullanıcı Arayüzünüz "donar" veya sayfanın bir kısmı yerine tüm sayfayı yenilemeniz gerekir. Geri arama ile, "şimdi yükleniyor" bir resim ekleyebilir ve yüklendikten sonra yeni verilerle değiştirebilirsiniz.
function grabAndFreeze() {
showNowLoading(true);
var jsondata = getData('http://yourserver.com/data/messages.json');
/* User Interface 'freezes' while getting data */
processData(jsondata);
showNowLoading(false);
do_other_stuff(); // not called until data fully downloaded
}
function processData(jsondata) { // do something with the data
var count = jsondata.results ? jsondata.results.length : 0;
$('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
$('#results_messages').html(jsondata.results || '(no new messages)');
}
Aşağıda, jQuery'nin getJSON'unu kullanan bir geri arama örneği :
function processDataCB(jsondata) { // callback: update UI with results
showNowLoading(false);
var count = jsondata.results ? jsondata.results.length : 0;
$('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
$('#results_messages').html(jsondata.results || '(no new messages)');
}
function grabAndGo() { // and don't freeze
showNowLoading(true);
$('#results_messages').html(now_loading_image);
$.getJSON("http://yourserver.com/data/messages.json", processDataCB);
/* Call processDataCB when data is downloaded, no frozen User Interface! */
do_other_stuff(); // called immediately
}
Genellikle geri state
aramanın closure
, İşçinin Görevini tamamlayabilmesi için Yönetici'den bilgi alması gereken bir a gibi arama işlevinden erişmesi gerekir . Oluşturmak için bu çağıran bağlamında verileri gördüğünü öyleyse, işlevini satır içine alabilirsiniz:closure
/* Grab messages, chat users, etc by changing dtable. Run callback cb when done.*/
function grab(dtable, cb) {
if (null == dtable) { dtable = "messages"; }
var uiElem = "_" + dtable;
showNowLoading(true, dtable);
$('#results' + uiElem).html(now_loading_image);
$.getJSON("http://yourserver.com/user/"+dtable+".json", cb || function (jsondata) {
// Using a closure: can "see" dtable argument and uiElem variables above.
var count = jsondata.results ? jsondata.results.length : 0,
counterMsg = ['Fetched', count, 'new', dtable].join(' '),
// no new chatters/messages/etc
defaultResultsMsg = ['(no new ', dtable, ')'].join('');
showNowLoading(false, dtable);
$('#counter' + uiElem).text(counterMsg);
$('#results'+ uiElem).html(jsondata.results || defaultResultsMsg);
});
/* User Interface calls cb when data is downloaded */
do_other_stuff(); // called immediately
}
// update results_chatters when chatters.json data is downloaded:
grab("chatters");
// update results_messages when messages.json data is downloaded
grab("messages");
// call myCallback(jsondata) when "history.json" data is loaded:
grab("history", myCallback);
Son olarak, burada bir tanımıdır closure
gelen Douglas Crockford :
Fonksiyonlar diğer fonksiyonların içinde tanımlanabilir. İç fonksiyon, dış fonksiyonun değişkenlerine ve parametrelerine erişime sahiptir. Bir iç fonksiyona bir referans hayatta kalırsa (örneğin, bir geri çağırma fonksiyonu olarak), dış fonksiyonun değişkenleri de hayatta kalır.
Ayrıca bakınız:
O kadar zeki insanın, "geri arama" kelimesinin iki tutarsız şekilde kullanıldığı gerçeğini vurgulamadığını görmek beni şaşırttı.
Her iki yol da mevcut bir işleve ek işlevsellik (anonim veya adlandırılmış bir işlev tanımı) ileterek bir işlevin özelleştirilmesini içerir. yani.
customizableFunc(customFunctionality)
Özel işlevsellik kod bloğuna basitçe bağlıysa, işlevi özelleştirmişsinizdir.
customizableFucn(customFunctionality) {
var data = doSomthing();
customFunctionality(data);
...
}
Bu tür enjekte edilmiş işlevsellik genellikle "geri arama" olarak adlandırılsa da, bununla ilgili bir şey yoktur. Çok açık bir örnek, diziyi değiştirmek için dizideki her öğeye uygulanacak bağımsız değişken olarak özel bir işlevin sağlandığı forEach yöntemidir.
Ama bunun için "geri arama" işlevleri kullanımından temelde farklıdır asenkron programlama AJAX veya node.js veya basitçe (fare tıklaması gibi) kullanıcı etkileşim olaylara işlevsellik atama gibi. Bu durumda, tüm fikir, özel işlevselliği çalıştırmadan önce bir koşullu olayın gerçekleşmesini beklemektir. Bu, kullanıcı etkileşimi durumunda açıktır, ancak dosyaları diskten okumak gibi zaman alabilen i / o (giriş / çıkış) işlemlerinde de önemlidir. "Geri arama" terimi burada en açık anlamı taşır. Bir g / Ç işlemi başlatıldıktan sonra (bir dosyadan diskten veya sunucudan http isteğinden veri döndürmesini istemek gibi) eşzamansızprogram bitmesini beklemiyor. Daha sonra planlanan görevlerle devam edebilir ve özel işleve ancak okuma dosyasının veya http isteğinin tamamlandığı (veya başarısız olduğu) ve verilerin özel işlevsellik için kullanılabilir olduğu bildirildikten sonra yanıt verebilir. Bu, bir işletmeyi telefonla aramak ve "geri arama" numaranızı bırakmak gibi bir şeydir, böylece size geri dönebilecek biri olduğunda sizi arayabilirler. Bu, kimin ne kadar sürebileceğini ve diğer işlere katılamamayı bilenler için asmaktan daha iyidir.
Eşzamansız kullanım, istenen olayı dinlemenin bazı yollarını (örn., G / Ç işleminin tamamlanması) içerir, böylece gerçekleştiğinde (ve yalnızca gerçekleştiğinde) özel "geri arama" işlevselliği yürütülür. Açık AJAX örneğinde, veriler sunucudan gerçekten geldiğinde, DOM'da değişiklik yapmak ve bu nedenle tarayıcı penceresini yeniden çizmek için bu verileri kullanmak üzere "geri arama" işlevi tetiklenir.
Özetlemek gerekirse. Bazı insanlar, mevcut bir işleve bağımsız değişken olarak enjekte edilebilen her türlü özel işlevselliğe atıfta bulunmak için "geri arama" sözcüğünü kullanır. Ancak, en azından benim için, kelimenin en uygun kullanımı, enjekte edilen "geri arama" işlevinin eşzamansız olarak kullanıldığı - yalnızca bildirilmeyi bekleyen bir olayın gerçekleşmesi üzerine yürütülmesidir.
Array.prototype.forEach()
iletilen işlev ile arg olarak iletilen işlev arasındaki setTimeout()
fark ve programınızla ilgili aklınıza geldiği kadar farklı renkteki atlardır. .
Programcı olmayan terimlerle, geri arama, bir programdaki boşluğu doldurmaktır.
Birçok kağıt formunda ortak bir madde "Acil durumlarda aranacak kişi" dir. Orada boş bir çizgi var. Birinin adını ve telefon numarasını yazıyorsunuz. Acil bir durum meydana gelirse, o kişi aranır.
Bu anahtar. Formu değiştirmezsiniz (kod, genellikle başkasının). Ancak bilgilerin (parçalarını eksik doldurabilir sizin sayı).
Örnek 1:
Geri çağrılar, muhtemelen bir programın davranışını eklemek / değiştirmek için özelleştirilmiş yöntemler olarak kullanılır. Örneğin, bir işlevi gerçekleştiren ancak çıktıyı nasıl yazdıracağını bilmeyen bazı C kodlarını alın. Tek yapabileceği bir dize yapmak. Dize ile ne yapılacağını anlamaya çalıştığında boş bir satır görür. Ancak, programcı geri arama yazmak için boş verdi!
Bu örnekte, bir kağıdın boşluğunu doldurmak için kalem kullanmazsınız, işlevi kullanırsınız set_print_callback(the_callback)
.
set_print_callback
kalem mithe_callback
doldurduğunuz bilgidir.Şimdi programdaki bu boş satırı doldurdunuz. Çıktıyı yazdırması gerektiğinde, bu boş satıra bakacak ve oradaki talimatları izleyecektir (yani oraya koyduğunuz işlevi çağırın.) Pratik olarak, bu, bir günlük dosyasına, bir günlük dosyasına, bir yazıcıya yazdırma, bir ağ bağlantısı veya bunların herhangi bir kombinasyonu üzerinden. Boşluğu, yapmak istediğiniz şeyle doldurdunuz.
Örnek 2:
Acil bir numarayı aramanız gerektiğini söylediğinizde, kağıt formda yazılanları okuyup okuduğunuz numarayı ararsınız. Bu satır boşsa hiçbir şey yapılmaz.
Gui programlama aynı şekilde çalışır. Bir düğmeye tıklandığında, programın daha sonra ne yapacağını bulması gerekir. Gidiyor ve geri aramayı arıyor. Bu geri arama, "Button1 tıklandığında yaptığınız işlemler" etiketli boş bir alanda olur
Çoğu IDE, boş alanı sizin için otomatik olarak doldurur (temel yöntemi yazın) (örneğin button1_clicked
). Ancak bu boşlukta iyi bir yöntem olabilir lütfen . Yöntemi çağırabilirsiniz run_computations
veya butter_the_biscuits
bu geri aramanın adını uygun boşluğa koyduğunuz sürece. Acil durum numarasına "555-555-1212" koyabilirsiniz. Çok mantıklı değil, ama izin verilebilir.
Son not: Geri arama ile doldurduğunuz boş satır? İstendiğinde silinebilir ve yeniden yazılabilir. (gerekip gerekmediği başka bir soru, ama bu onların gücünün bir parçası)
Bir örnekle başlamak her zaman daha iyidir :).
İki modül A ve B'ye sahip olduğunuzu varsayalım.
Modül B'de bir olay / durum meydana geldiğinde modül A'nın bilgilendirilmesini istiyorsunuz . Bununla birlikte, modül B'nin modülünüz A hakkında bir fikri yoktur. Tek bildiği, belirli bir fonksiyonun (modül A) bir fonksiyon işaretçisi aracılığıyla modül A tarafından sağlanır.
Dolayısıyla, B'nin tek yapması gereken, işlev işaretçisi kullanılarak belirli bir olay / koşul oluştuğunda A modülüne "geri çağrı" dır. A geri arama fonksiyonu içinde daha fazla işlem yapabilir.
*) Burada açık bir avantaj, modül A'dan B modülüyle ilgili her şeyi B modülünden soyutlamanızdır. Modül B, modül A'nın kim / ne olduğunu önemsemek zorunda değildir.
10 kare döndüren bir işleve ihtiyacınız olduğunu düşünün, böylece bir işlev yazın:
function tenSquared() {return 10*10;}
Daha sonra 9 kareye ihtiyacınız olacak, böylece başka bir işlev yazacaksınız:
function nineSquared() {return 9*9;}
Sonunda bunların hepsini genel bir işlevle değiştireceksiniz:
function square(x) {return x*x;}
Aynı düşünce geri aramalar için de geçerlidir. Bir şey yapan bir işleviniz var ve aramalar bittiğinde doA:
function computeA(){
...
doA(result);
}
Daha sonra, tam olarak aynı işlevin doB'yi çağırmasını istiyorsunuz, bunun yerine tüm işlevi çoğaltabilirsiniz:
function computeB(){
...
doB(result);
}
Veya değişken olarak geri çağrı işlevini iletebilir ve yalnızca bir kez işleve sahip olmanız gerekir:
function compute(callback){
...
callback(result);
}
Sonra sadece compute (doA) ve compute (doB) 'yi çağırmanız gerekir.
Kodu basitleştirmenin ötesinde, eşzamanlı kodun, telefondaki birini aradığınızda ve geri arama numarası bıraktığınız zamanki gibi, keyfi işlevinizi tamamlandığında çağırarak size bildirilmesini sağlar.
Johny programcı bir zımbaya ihtiyaç duyar, bu yüzden ofis tedarik bölümüne gider ve bir tane isteyin, istek formunu doldurduktan sonra orada durabilir ve katipin zımba için depoya bakmasını bekleyebilir (engelleme işlevi çağrısı gibi) ) veya bu arada başka bir şey yapın.
bu genellikle zaman aldığından, johny istek formuyla birlikte zımba almaya hazır olduğunda onu aramasını isteyen bir not koyar, bu arada masasına uyuklamak gibi başka bir şey yapabilir.
Hasta hissediyorsun, doktora gidiyorsun. Sizi muayene ediyor ve bazı ilaçlara ihtiyacınız olduğunu belirliyor. Bazı ilaçları reçete eder ve reçeteyi yerel eczanenize çağırır. Eve git. Daha sonra eczaneniz reçetenizi hazır olduğunu söylemek için arar. Git ve al.
Açıklamak için iki nokta vardır, bir geri çağırma nasıl çalışır (bağlamı hakkında herhangi bir bilgi olmadan çağrılabilen bir fonksiyondan geçer), diğeri ne için kullanılır (olayları eşzamansız olarak işlemek).
Diğer cevaplar tarafından kullanılan bir paketin gelmesini beklemenin benzetmesi her ikisini de açıklamak için iyi bir yöntemdir. Bir bilgisayar programında, bilgisayara bir paket beklemesini söylerdiniz. Normalde, şimdi orada oturur ve parsel gelene kadar bekler (ve başka bir şey yapmaz), muhtemelen hiç gelmezse muhtemelen süresiz olarak. İnsanlar için bu kulağa aptalca geliyor, ancak başka önlemler alınmadan, bu bir bilgisayar için tamamen doğal.
Şimdi geri arama ön kapınızdaki çan olacaktır. Parsel servisine, evde nerede olduğunuzu (hatta) ve zilin nasıl çalıştığını bilmek zorunda kalmadan parselin varışını size bildirecek bir yol sağlarsınız. (Örneğin, bazı "ziller" aslında bir telefon çağrısı gönderir.) Bağlam dışında herhangi bir zamanda "çağrılabilecek" bir "geri arama işlevi" sağladığınızdan, artık ön sundurmada oturmayı durdurabilir ve olay "(parsel varış) zamanı geldiğinde.
Bir arkadaşınızın evinizden ayrıldığını düşünün ve ona "Eve geldiğinizde beni arayın, böylece güvenli bir şekilde geldiğinizi biliyorum" deyin; (tam anlamıyla) bir geri arama . Dile bakılmaksızın geri arama işlevi budur. Bazı işlemlerin, bir görevi tamamladığında kontrolü size geri vermesini istersiniz, böylece size geri çağırmak için kullanılacak bir işlev verirsiniz.
Örneğin Python'da,
grabDBValue( (lambda x: passValueToGUIWindow(x) ))
grabDBValue
yalnızca bir veritabanından bir değer almak ve daha sonra değerle ne yapacağınızı belirtmenize izin vermek için yazılabilir, böylece bir işlevi kabul eder. Ne zaman grabDBValue
döneceğini veya dönüp dönmeyeceğini bilmiyorsunuz , ancak eğer / ne zaman döneceğini, ne yapmasını istediğinizi biliyorsunuz. Burada, değeri bir GUI penceresine gönderen anonim bir işlev (veya lambda ) geçiriyorum . Bunu yaparak programın davranışını kolayca değiştirebilirim:
grabDBValue( (lambda x: passToLogger(x) ))
Geri aramalar, işlevlerin her zamanki tamsayılar, karakter dizeleri, booleans, vb. Gibi birinci sınıf değerler olduğu dillerde iyi çalışır . C'de, bir işlevi bir işaretçiyi ona ileterek "arayan" olabilir ve arayan bunu kullanabilir; Java'da, sınıf dışında işlevler ("yöntemler," gerçekten) olmadığından, arayan belirli bir yöntem adına sahip belirli bir türde statik bir sınıf isteyecektir; ve diğer birçok dinamik dilde basit sözdizimiyle bir işlevi iletebilirsiniz.
Sözcüksel kapsam belirleme içeren dillerde (Şema veya Perl gibi) aşağıdaki gibi bir numara çekebilirsiniz:
my $var = 2;
my $val = someCallerBackFunction(sub callback { return $var * 3; });
# Perlistas note: I know the sub doesn't need a name, this is for illustration
$val
bu durumda 6
geri çağırma , tanımlandığı sözcüksel ortamda bildirilen değişkenlere erişebileceğinden kaynaklanır . Sözcüksel kapsam ve anonim geri çağrılar, acemi programcı için daha fazla çalışma yapılmasını gerektiren güçlü bir kombinasyondur.
Çalıştırmak istediğiniz bazı kodlarınız var. Normalde, aradığınızda, devam etmeden önce bitmesini beklersiniz (bu, uygulamanızın gri olmasına / imleç için bir dönme süresi üretmesine neden olabilir).
Alternatif bir yöntem, bu kodu paralel olarak çalıştırmak ve kendi işinize devam etmektir. Peki ya orijinal kodunuzun çağırdığı koddan gelen cevaba bağlı olarak farklı şeyler yapması gerekiyorsa? Bu durumda, kod bittiğinde çağırmasını istediğiniz kodun adını / konumunu iletebilirsiniz. Bu bir "geri arama".
Normal kod: Bilgi İsteyin-> İşlem Bilgileri-> İşleme sonuçları ile ilgilenin-> Başka şeyler yapmaya devam edin.
Geri aramalarla: Bilgi İsteyin -> İşlem Bilgileri -> Başka şeyler yapmaya devam edin. Ve daha sonraki bir noktada-> İşleme sonuçları ile ilgilenin.
Geri arama olmadan başka hiçbir özel programlama kaynağı (iş parçacığı ve diğerleri gibi), bir program tam olarak arka arkaya ve hatta belirli koşullar tarafından belirlenen bir tür "dinamik davranış" ile, tüm olası senaryolarla yürütülen bir talimatlar dizisidir. önceden programlanmalıdır .
Dolayısıyla, bir programa gerçek bir dinamik davranış sağlamamız gerekirse, geri aramayı kullanabiliriz. Geri arama ile, parametrelerle talimat verebilir, daha önce tanımlanmış bazı parametreler sağlayan başka bir programı çağırmak için bir program çağırabilir ve bazı sonuçlar bekleyebilir ( bu sözleşme veya işlem imzasıdır ), bu nedenle bu sonuçlar olmayan üçüncü taraf program tarafından üretilebilir / işlenebilir daha önce bilinmiyordu.
Bu teknik, programlar, fonksiyonlar, nesneler ve bilgisayar tarafından yönetilen diğer tüm kod birimlerine uygulanan polimorfizmin temelidir.
Geri arama için örnek olarak kullanılan insan dünyası, bir iş yaparken açıklanır, bir ressam olduğunuzu varsayalım ( burada ana resim, boyalar ) ve müşterinizi bazen işinizin sonucunu onaylamasını istemek için arayın , bu nedenle resmin iyi olup olmadığına karar verir ( müşteriniz üçüncü taraf programıdır ).
Yukarıdaki örnekte bir ressamsınız ve sonucu onaylama işini başkalarına "delege edersiniz", resim parametredir ve her yeni istemci (geri çağrılan "işlev") çalışmanızın sonucunu değiştirerek ne istediğine karar verir resim hakkında ( müşteriler tarafından alınan karar "geri arama fonksiyonu" döndürülen sonuçtur ).
Umarım bu açıklama faydalı olabilir.
Bana potansiyel olarak uzun süren bir görev verdiğinizi varsayalım: karşılaştığınız ilk beş eşsiz kişinin adını alın. Seyrek nüfuslu bir bölgedeysem bu günler alabilir. Ben koşarken gerçekten ellerinizin üzerinde oturmakla ilgilenmiyorsunuz, "Listeye sahip olduğunuzda, beni cep telefonumdan arayın ve bana tekrar okuyun. İşte numara."
Bana bir geri arama referansı verdiniz - daha fazla işleme dağıtmak için yürütmem gereken bir işlev.
JavaScript'te şöyle görünebilir:
var lottoNumbers = [];
var callback = function(theNames) {
for (var i=0; i<theNames.length; i++) {
lottoNumbers.push(theNames[i].length);
}
};
db.executeQuery("SELECT name " +
"FROM tblEveryOneInTheWholeWorld " +
"ORDER BY proximity DESC " +
"LIMIT 5", callback);
while (lottoNumbers.length < 5) {
playGolf();
}
playLotto(lottoNumbers);
Bu muhtemelen birçok yönden iyileştirilebilir. Örneğin, ikinci bir geri arama yapabilirsiniz: bir saatten uzun sürerse, kırmızı telefonu arayın ve yanıtladığınız kişiye zaman aşımına uğradığını söyleyin.
Geri aramalar, telefon sistemi açısından en kolay tanımlanır. İşlev çağrısı, birini telefonla aramaya, ona bir soru sormaya, bir cevap almaya ve telefonu kapatmaya benzer; bir geri arama eklemek benzetmeyi değiştirir, böylece ona bir soru sorduktan sonra, ona yanıtınızı arayabilmesi için ona adınızı ve numaranızı da verebilirsiniz. - Paul Jakubik, "C ++ 'da Geri Arama Uygulamaları"
Geri arama, ikinci bir işlev tarafından çağrılacak bir işlevdir. Bu ikinci işlev önceden hangi işlevi çağıracağını bilmiyor. Böylece geri arama işlevinin kimliği bir yerde saklanır veya ikinci işleve parametre olarak aktarılır. Programlama diline bağlı olarak bu "kimlik", geri aramanın adresi veya başka bir tür işaretçi olabilir veya işlevin adı olabilir. Müdür aynıdır, işlevi açıkça tanımlayan bazı bilgileri saklarız veya aktarırız.
Zaman geldiğinde, ikinci fonksiyon o andaki koşullara bağlı olarak parametreler sağlayarak geri aramayı çağırabilir. Hatta geri aramayı bir dizi olası geri arama arasından seçebilir. Programlama dili, ikinci işlevin "kimliğini" bilerek geri aramayı çağırmasına izin vermek için bir tür sözdizimi sağlamalıdır.
Bu mekanizmanın birçok olası kullanımı vardır. Geri aramalarla, bir işlevin tasarımcısı, sağlanan geri çağrıları çağırmasını sağlayarak işlevinin özelleştirilmesine izin verebilir. Örneğin, bir sıralama işlevi parametre olarak geri aramayı alabilir ve bu geri arama, hangisinin önce geleceğine karar vermek için iki öğeyi karşılaştırmak için bir işlev olabilir.
Bu arada, programlama diline bağlı olarak, yukarıdaki tartışmadaki "işlev" kelimesi "blok", "kapatma", "lambda" vb. İle değiştirilebilir.
Genellikle fonksiyonlara değişkenler gönderdik. Bağımsız değişken olarak verilmeden önce değişkenin işlenmesi gereken bir göreviniz olduğunu varsayalım - geri aramayı kullanabilirsiniz.
function1(var1, var2)
olağan yoldur.
var2
İşlenip argüman olarak gönderilmek istersem ne olur ?
function1(var1, function2(var2))
Bu, bir function2
kod yürüttüğü ve bir değişkeni ilk işleve geri döndürdüğü bir geri arama türüdür .
Mecazi bir açıklama:
Bir arkadaşa teslim etmek istediğim bir parselim var ve ayrıca arkadaşımın ne zaman aldığını bilmek istiyorum.
Bu yüzden parseli postaneye götürüp teslim etmelerini istiyorum. Arkadaşımın parseli ne zaman aldığını bilmek istersem, iki seçeneğim var:
(a) Postaneye teslim edilene kadar bekleyebilirim.
(b) Teslim edildiğinde bir e-posta alırım.
Seçenek (b) bir geri aramaya benzer.
Geri çağrıları öğretmek için önce işaretçiyi öğretmeniz gerekir. Öğrenciler bir değişkene işaret etme fikrini anladıktan sonra, geri aramalar fikri daha kolay olacaktır. C / C ++ kullandığınızı varsayarsak, bu adımlar takip edilebilir.
Daha birçok şey olabilir. Öğrencileri dahil edin ve keşfedeceksiniz. Bu yardımcı olur umarım.
Sade ingilizce bir geri arama bir sözdür. Joe, Jane, David ve Samantha çalışmak için bir araba paylaşıyorlar. Joe bugün araba kullanıyor. Jane, David ve Samantha'nın birkaç seçeneği var:
Seçenek 1: Bu daha çok Jane'in Joe'nun dışarıda olup olmadığını kontrol eden bir "döngüde" sıkışıp kaldığı bir seçim örneğine benziyor. Jane bu arada başka bir şey yapamaz.
Seçenek 2: Bu geri arama örneğidir. Jane, Joe'ya dışarıdayken kapı zilini çalmasını söyler. Kapı zilini çalması için ona bir "işlev" veriyor. Joe'nun kapı zilinin nasıl çalıştığını veya nerede olduğunu bilmesine gerek yok, sadece o işlevi çağırması gerekiyor, yani oradayken kapı zilini çalıyor.
Geri aramalar "olaylar" tarafından yönlendirilir. Bu örnekte "olay" Joe'nun gelişidir. Örneğin Ajax'ta olaylar, eşzamansız isteğin "başarılı" veya "başarısızlığı" olabilir ve her biri aynı veya farklı geri çağrılara sahip olabilir.
JavaScript uygulamaları ve geri aramalar açısından. Ayrıca "kapanışları" ve uygulama bağlamını da anlamamız gerekir. "Bunun" ne anlama geldiği, JavaScript geliştiricilerini kolayca karıştırabilir. Bu örnekte, her bir kişinin "ring_the_door_bell ()" yöntemi / geri çağrısı içinde, her bir kişinin sabah rutinine göre yapması gereken başka yöntemler de olabilir. "televizyonu kapat()". "Bu" nun "Jane" nesnesine veya "David" nesnesine atıfta bulunmasını isteriz, böylece her biri Joe onları almadan önce yapmaları gereken her şeyi ayarlayabilir. Joe ile geri çağrıyı ayarlamak, yöntemi bu şekilde parodi gerektirir, böylece "this" doğru nesneyi ifade eder.
Umarım yardımcı olur!
Bence bunu açıklamak oldukça kolay.
İlk geri arama sadece sıradan işlevlerdir.
Dahası, başka bir fonksiyonun içinden bu fonksiyonu (buna A diyelim) diyoruz (buna B diyelim).
Bununla ilgili sihir, hangi fonksiyonun B dışından fonksiyon tarafından çağrılması gerektiğine karar vermemdir .
Ben BI fonksiyonunu yazarken hangi geri çağırma fonksiyonunun çağrılması gerektiğini bilmiyorum. BI işlevini çağırdığımda, bu işleve A işlevini çağırmasını da söyleyin. Hepsi bu.
Geri Arama İşlevi Nedir?
Bu ilk sorunun basit cevabı, bir geri çağırma işlevinin, bir işlev işaretçisi aracılığıyla çağrılan bir işlev olmasıdır. Bir işlevin işaretçisini (adresini) başka bir değişkene argüman olarak iletirseniz, bu işaretçi işlevi çağırmak için kullanıldığında işaret ettiği bir geri arama yapılır.
Geri arama işlevini izlemek zordur, ancak bazen çok yararlıdır. Özellikle kütüphaneler tasarlarken. Geri arama işlevi, kullanıcıdan size bir işlev adı vermesini istemek gibidir ve bu işlevi belirli koşullar altında çağıracaksınız.
Örneğin, bir geri arama zamanlayıcısı yazarsınız. Süreyi ve hangi işlevi çağıracağınızı belirlemenizi sağlar ve işlev buna göre geri çağrılacaktır. “Her 10 saniyede bir 5 kez fonksiyonumu çalıştır ()”
Veya işlev adı listesini ileterek bir işlev dizini oluşturabilir ve kitaplıktan buna göre geri arama isteyebilirsiniz. “Başarılı olursa geri arama başarılı (), başarısız olursa geri arama başarısız ().”
Basit bir işlev işaretçisi örneğine bakalım
void cbfunc()
{
printf("called");
}
int main ()
{
/* function pointer */
void (*callback)(void);
/* point to your callback function */
callback=(void *)cbfunc;
/* perform callback */
callback();
return 0;
}
Bağımsız değişken geri arama işlevine nasıl geçirilir?
Geri arama yapmak için bu işlev işaretçisi, yapı da dahil olmak üzere herhangi bir değişken türünde olabileceğini gösteren void * değerini alır. Bu nedenle yapıya göre birden fazla argüman iletebilirsiniz.
typedef struct myst
{
int a;
char b[10];
}myst;
void cbfunc(myst *mt)
{
fprintf(stdout,"called %d %s.",mt->a,mt->b);
}
int main()
{
/* func pointer */
void (*callback)(void *); //param
myst m;
m.a=10;
strcpy(m.b,"123");
callback = (void*)cbfunc; /* point to callback function */
callback(&m); /* perform callback and pass in the param */
return 0;
}
Geri arama, bir koşul karşılandığında yürütülmesi planlanan bir yöntemdir.
"Gerçek dünya" örneği yerel bir video oyun mağazasıdır. Half-Life 3'ü bekliyorsunuz. Mağazada olup olmadığını görmek için her gün mağazaya gitmek yerine, e-postanızı oyun hazır olduğunda bildirilmek üzere bir listeye kaydedersiniz. E-posta "geri arama" haline gelir ve karşılanması gereken koşul oyunun kullanılabilirliğidir.
"Programcılar" örneği, bir düğme tıklatıldığında eylem gerçekleştirmek istediğiniz bir web sayfasıdır. Bir düğme için geri arama yöntemi kaydedersiniz ve diğer görevleri yapmaya devam edersiniz. Kullanıcı düğmeyi tıkladığında / tıkladığında, tarayıcı o etkinliğin geri arama listesine bakar ve yönteminizi çağırır.
Geri arama, olayları eşzamansız olarak işlemenin bir yoludur. Geri aramanın ne zaman yürütüleceğini veya hiç yürütülüp yürütülmeyeceğini asla bilemezsiniz. Avantajı, yanıtı beklerken diğer görevleri gerçekleştirmek için programınızı ve CPU döngülerinizi serbest bırakmasıdır.
Basit ve basit: Geri arama, başka bir işleve verdiğiniz bir işlevdir, böylece onu çağırabilir .
Genellikle bazı işlemler tamamlandığında çağrılır. Geri aramayı başka bir işleve vermeden önce oluşturduğunuzdan, çağrı sitesinden bağlam bilgileriyle başlatabilirsiniz. Bu nedenle çağrı * geri * olarak adlandırılır - ilk işlev, çağrıldığı yerden içeri çağırır.
“Bilgisayar programlamasında geri arama, diğer koda argüman olarak iletilen yürütülebilir koda ya da yürütülebilir koda bir referanstır. Bu, daha düşük seviyeli bir yazılım katmanının daha yüksek seviyedeki bir katmanda tanımlanan bir alt programı (veya işlevi) çağırmasını sağlar. ” - Wikipedia
İşlev İşaretçisi'ni kullanarak C'de geri arama
C'de geri arama İşlev İşaretçisi kullanılarak gerçekleştirilir. İşlev İşaretçisi - adından da anlaşılacağı gibi, bir işlevin işaretçisidir.
Örneğin, int (* ptrFunc) ();
Burada, ptrFunc argüman almayan ve bir tamsayı döndüren bir fonksiyonun göstergesidir. Parantez içine koymayı unutmayın, aksi takdirde derleyici hiçbir şey almayan ve bir tamsayıya bir işaretçi döndüren normal bir işlev adı olduğunu varsayar.
İşlev işaretçisini göstermek için bazı kodlar.
#include<stdio.h>
int func(int, int);
int main(void)
{
int result1,result2;
/* declaring a pointer to a function which takes
two int arguments and returns an integer as result */
int (*ptrFunc)(int,int);
/* assigning ptrFunc to func's address */
ptrFunc=func;
/* calling func() through explicit dereference */
result1 = (*ptrFunc)(10,20);
/* calling func() through implicit dereference */
result2 = ptrFunc(10,20);
printf("result1 = %d result2 = %d\n",result1,result2);
return 0;
}
int func(int x, int y)
{
return x+y;
}
Şimdi fonksiyon göstergesini kullanarak C'deki Geri Arama kavramını anlamaya çalışalım.
Tüm programın üç dosyası vardır: callback.c, reg_callback.h ve reg_callback.c.
/* callback.c */
#include<stdio.h>
#include"reg_callback.h"
/* callback function definition goes here */
void my_callback(void)
{
printf("inside my_callback\n");
}
int main(void)
{
/* initialize function pointer to
my_callback */
callback ptr_my_callback=my_callback;
printf("This is a program demonstrating function callback\n");
/* register our callback function */
register_callback(ptr_my_callback);
printf("back inside main program\n");
return 0;
}
/* reg_callback.h */
typedef void (*callback)(void);
void register_callback(callback ptr_reg_callback);
/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"
/* registration goes here */
void register_callback(callback ptr_reg_callback)
{
printf("inside register_callback\n");
/* calling our callback function my_callback */
(*ptr_reg_callback)();
}
Bu programı çalıştırırsak, çıktı
Bu, ana programın içindeki my_callback içinde register_callback içinde fonksiyon geri aramasını gösteren bir programdır
Yüksek katman işlevi, alt katman işlevini normal çağrı olarak çağırır ve geri arama mekanizması, alt katman işlevinin, daha yüksek katman işlevini bir işaretçi aracılığıyla geri arama işlevine çağırmasını sağlar.
Arabirimi Kullanarak Java'da Geri Arama
Java işlev işaretçisi kavramına sahip değildir Arayüz mekanizması aracılığıyla Geri Arama mekanizmasını uygular Burada işlev işaretçisi yerine, callee görevini bitirdiğinde çağrılacak bir yönteme sahip bir Arabirim bildiririz
Bir örnekle göstereyim:
Geri Arama Arayüzü
public interface Callback
{
public void notify(Result result);
}
Arayan veya Üst Düzey Sınıfı
public Class Caller implements Callback
{
Callee ce = new Callee(this); //pass self to the callee
//Other functionality
//Call the Asynctask
ce.doAsynctask();
public void notify(Result result){
//Got the result after the callee has finished the task
//Can do whatever i want with the result
}
}
Callee veya alt katman işlevi
public Class Callee {
Callback cb;
Callee(Callback cb){
this.cb = cb;
}
doAsynctask(){
//do the long running task
//get the result
cb.notify(result);//after the task is completed, notify the caller
}
}
EventListener desenini kullanarak geri arama
Bu model, belirli bir görevin tamamlandığını 0 ile n arasında sayıda Gözlemci / Dinleyici bildirmek için kullanılır
Geri arama mekanizması ile EventListener / Observer mekanizması arasındaki fark, geri aramada, arayanın tek bir arayanı bildirmesidir, oysa Eventlisener / Observer'da, callee bu olayla ilgilenen herkesi bilgilendirebilir (bildirim, görevi tetiklemeyen uygulama)
Bir örnekle açıklayayım.
Olay Arayüzü
public interface Events {
public void clickEvent();
public void longClickEvent();
}
Sınıf Widget'ı
package com.som_itsolutions.training.java.exampleeventlistener;
import java.util.ArrayList;
import java.util.Iterator;
public class Widget implements Events{
ArrayList<OnClickEventListener> mClickEventListener = new ArrayList<OnClickEventListener>();
ArrayList<OnLongClickEventListener> mLongClickEventListener = new ArrayList<OnLongClickEventListener>();
@Override
public void clickEvent() {
// TODO Auto-generated method stub
Iterator<OnClickEventListener> it = mClickEventListener.iterator();
while(it.hasNext()){
OnClickEventListener li = it.next();
li.onClick(this);
}
}
@Override
public void longClickEvent() {
// TODO Auto-generated method stub
Iterator<OnLongClickEventListener> it = mLongClickEventListener.iterator();
while(it.hasNext()){
OnLongClickEventListener li = it.next();
li.onLongClick(this);
}
}
public interface OnClickEventListener
{
public void onClick (Widget source);
}
public interface OnLongClickEventListener
{
public void onLongClick (Widget source);
}
public void setOnClickEventListner(OnClickEventListener li){
mClickEventListener.add(li);
}
public void setOnLongClickEventListner(OnLongClickEventListener li){
mLongClickEventListener.add(li);
}
}
Sınıf Düğmesi
public class Button extends Widget{
private String mButtonText;
public Button (){
}
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}
Sınıf Onay Kutusu
public class CheckBox extends Widget{
private boolean checked;
public CheckBox() {
checked = false;
}
public boolean isChecked(){
return (checked == true);
}
public void setCheck(boolean checked){
this.checked = checked;
}
}
Etkinlik Sınıfı
paket com.som_itsolutions.training.java.exampleeventlistener;
public class Activity implements Widget.OnClickEventListener
{
public Button mButton;
public CheckBox mCheckBox;
private static Activity mActivityHandler;
public static Activity getActivityHandle(){
return mActivityHandler;
}
public Activity ()
{
mActivityHandler = this;
mButton = new Button();
mButton.setOnClickEventListner(this);
mCheckBox = new CheckBox();
mCheckBox.setOnClickEventListner(this);
}
public void onClick (Widget source)
{
if(source == mButton){
mButton.setButtonText("Thank you for clicking me...");
System.out.println(((Button) mButton).getButtonText());
}
if(source == mCheckBox){
if(mCheckBox.isChecked()==false){
mCheckBox.setCheck(true);
System.out.println("The checkbox is checked...");
}
else{
mCheckBox.setCheck(false);
System.out.println("The checkbox is not checked...");
}
}
}
public void doSomeWork(Widget source){
source.clickEvent();
}
}
Diğer Sınıf
public class OtherClass implements Widget.OnClickEventListener{
Button mButton;
public OtherClass(){
mButton = Activity.getActivityHandle().mButton;
mButton.setOnClickEventListner(this);//interested in the click event //of the button
}
@Override
public void onClick(Widget source) {
if(source == mButton){
System.out.println("Other Class has also received the event notification...");
}
}
Ana sınıf
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Activity a = new Activity();
OtherClass o = new OtherClass();
a.doSomeWork(a.mButton);
a.doSomeWork(a.mCheckBox);
}
}
Yukarıdaki koddan da görebileceğiniz gibi, temelde uygulamamız için olabilecek tüm olayları listeleyen olaylar adında bir arayüze sahibiz. Widget sınıfı, Button, Checkbox gibi tüm UI bileşenleri için temel sınıftır. Bu kullanıcı arabirimi bileşenleri, olayları çerçeve kodundan gerçekten alan nesnelerdir. Widget sınıfı Olaylar arabirimini uygular ve ayrıca OnClickEventListener ve OnLongClickEventListener adlı iki iç içe arabirim vardır
Bu iki arayüz, Düğme veya Onay Kutusu gibi Widget'tan türetilen UI bileşenlerinde meydana gelebilecek olayları dinlemekten sorumludur. Dolayısıyla, bu örneği Java Arayüzü kullanan önceki Geri Arama örneğiyle karşılaştırırsak, bu iki arabirim Geri Arama arabirimi olarak çalışır. Böylece daha yüksek seviye kodu (Here Activity) bu iki arayüzü uygular. Bir widget'ta her olay gerçekleştiğinde, daha yüksek düzey kodu (veya burada Etkinlik olan üst düzey kodda uygulanan bu arabirimlerin yöntemi) çağrılır.
Şimdi Callback ve Eventlistener kalıbı arasındaki temel farkı tartışalım. Daha önce de belirttiğimiz gibi, Callee sadece bir Arayanı bilgilendirebilir. Ancak EventListener örneği söz konusu olduğunda, Uygulamanın herhangi bir kısmı veya sınıfı Düğme veya Onay Kutusunda meydana gelebilecek olaylara kaydolabilir. Bu tür bir sınıf örneği OtherClass'tır. OtherClass kodunu görürseniz, etkinlikte tanımlanan Düğmede oluşabilecek ClickEvent öğesini dinleyici olarak kaydettiğini görürsünüz. İlginç olan kısım, Aktivitenin (Arayan) yanı sıra, Düğme üzerinde tıklama olayı gerçekleştiğinde bu Diğer Sınıfın da bilgilendirilmesidir.
Geri aramalar, kendi kodunuzu başka bir zamanda yürütülecek başka bir kod bloğuna eklemenize olanak tanır; Daha sürdürülebilir bir koda sahip olurken esneklik ve özelleştirilebilirlik kazanırsınız.
Daha az sabit kod = bakımı ve değiştirilmesi daha kolay = daha az zaman = daha fazla iş değeri = mükemmel.
Örneğin, javascript'te Underscore.js kullanarak, bir dizideki tüm çift öğeleri aşağıdaki gibi bulabilirsiniz:
var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]
Underscore.js'nin izniyle: http://documentcloud.github.com/underscore/#filter
Biz iki işlevi varken söylemek [Düzenlenen] FunctionA ve functionB eğer functiona bağlıdır functionB .
o zaman diyoruz functionB bir şekilde geri çağırma işlevi yaygın Bahar çerçevesinde kullanılır .bu.
Bir yöntemi bir iş arkadaşınıza görev vermek olarak düşünün. Basit bir görev aşağıdakiler olabilir:
Solve these equations:
x + 2 = y
2 * x = 3 * y
İş arkadaşınız özenle matematik yapıyor ve size aşağıdaki sonucu veriyor:
x = -6
y = -4
Ancak iş arkadaşınızın bir sorunu var, her zaman gösterimleri anlamaz ^
, ancak açıklamalarıyla anlar. Gibi exponent
. Bunlardan birini her bulduğunda aşağıdakileri geri alırsınız:
I don't understand "^"
Bu, karakterin iş arkadaşınız için ne anlama geldiğini açıkladıktan sonra tüm talimat setinizi yeniden yazmanızı gerektirir ve sorular arasında her zaman hatırlamaz. Ve sadece bana sor gibi ipuçlarını hatırlamakta zorlanıyor. Bununla birlikte, yazılı talimatlarınızı her zaman olabildiğince iyi takip eder.
Bir çözüm düşünüyorsunuz, tüm talimatlarınıza aşağıdakileri eklemeniz yeterlidir:
If you have any questions about symbols, call me at extension 1234 and I will tell you its name.
Şimdi ne zaman bir problemi olursa size kötü bir yanıt vermek ve sürecin yeniden başlatılmasından ziyade sizi arar ve sorar.
Bir web sayfasını indirme açısından bu:
Programınız bir cep telefonunda çalışıyor ve http://www.google.com web sayfasını istiyor . Programınızı eşzamanlı olarak yazarsanız, verileri indirmek için yazdığınız işlev, tüm veriler indirilene kadar sürekli olarak çalışır. Bu, kullanıcı arayüzünüzün yenilenmeyeceği ve temel olarak donmuş görüneceği anlamına gelir. Programınızı geri aramalar kullanarak yazarsanız, verileri istersiniz ve "işiniz bittiğinde bu işlevi yürüt" dersiniz. Bu, kullanıcı arayüzünün dosya indirilirken kullanıcı etkileşimine izin vermesine izin verir. Web sayfasının indirilmesi bittiğinde, sonuç işleviniz (geri arama) çağrılır ve verileri işleyebilirsiniz.
Temel olarak, bir şey istemenizi ve sonucu beklerken yürütmeye devam etmenizi sağlar. Sonuç bir geri arama işlevi aracılığıyla size geri döndüğünde, işlemi kaldığı yerden alabilirsiniz.