回調(diào)函數(shù)
在計算機(jī)編程中,一個回調(diào)是對一段可執(zhí)行代碼的引用一姿,該代碼作為參數(shù)傳遞給其他代碼。
回調(diào)是一個函數(shù),它作為參數(shù)傳遞給另一個函數(shù)遥诉,并在父函數(shù)完成后執(zhí)行苞笨。
回調(diào)的特別之處在于宪塔,在“父”節(jié)點(diǎn)之后出現(xiàn)的函數(shù)可以在回調(diào)執(zhí)行之前執(zhí)行戚长。
另一個需要知道的重要事情是如何正確地傳遞回調(diào)盗冷。這就是我經(jīng)常忘記正確語法的地方。
回調(diào)函數(shù)就是一個通過函數(shù)指針調(diào)用的函數(shù)同廉。
如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個函數(shù)仪糖,當(dāng)這個指針被用為調(diào)用它所指向的函數(shù)時柑司,我們就說這是回調(diào)函數(shù)。
回調(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用锅劝,而是在特定的事件或條件發(fā)生時由另外的一方調(diào)用的攒驰,用于對該事件或條件進(jìn)行響應(yīng)。
因此故爵,回調(diào)本質(zhì)上是一種設(shè)計模式玻粪,并且jQuery(包括其他框架)的設(shè)計原則遵循了這個模式。
在JavaScript中诬垂,回調(diào)函數(shù)具體的定義為:函數(shù)A作為參數(shù)(函數(shù)引用)傳遞到另一個函數(shù)B中劲室,并且這個函數(shù)B執(zhí)行函數(shù)A。我們就說函數(shù)A叫做回調(diào)函數(shù)结窘。如果沒有名稱(函數(shù)表達(dá)式)很洋,就叫做匿名回調(diào)函數(shù)。
因此callback 不一定用于異步隧枫,一般同步(阻塞)的場景下也經(jīng)常用到回調(diào)喉磁,比如要求執(zhí)行某些操作后執(zhí)行回調(diào)函數(shù)。
例子
一個同步(阻塞)中使用回調(diào)的例子官脓,目的是在func1代碼執(zhí)行完成后執(zhí)行func2线定。
var func1=function(callback){
//do something.
(callback && typeof(callback) === "function") && callback();
}
func1(func2);
var func2=function(){
}
異步回調(diào)的例子:
$(document).ready(callback);
$.ajax({
url: "test.html",
context: document.body
}).done(function() {
$(this).addClass("done");
}).fail(function() { alert("error");
}).always(function() { alert("complete");
});
/**
注意的是,ajax請求確實(shí)是異步的,不過這請求是由瀏覽器新開一個線程請求,當(dāng)請求的狀態(tài)變更時,如果先前已設(shè)置回調(diào),這異步線程就產(chǎn)生狀態(tài)變更事件放到 JavaScript引擎的處理隊(duì)列中等待處理确买。見:http://www.phpv.net/html/1700.html
*/
回調(diào)什么時候執(zhí)行
回調(diào)函數(shù)斤讥,一般在同步情境下是最后執(zhí)行的,而在異步情境下有可能不執(zhí)行湾趾,因?yàn)槭录]有被觸發(fā)或者條件不滿足芭商。
回調(diào)函數(shù)的使用場合
- 資源加載:動態(tài)加載js文件后執(zhí)行回調(diào),加載iframe后執(zhí)行回調(diào)搀缠,ajax操作回調(diào)铛楣,圖片加載完成執(zhí)行回調(diào),AJAX等等艺普。
- DOM事件及Node.js事件基于回調(diào)機(jī)制(Node.js回調(diào)可能會出現(xiàn)多層回調(diào)嵌套的問題)簸州。
- setTimeout的延遲時間為0,這個hack經(jīng)常被用到歧譬,settimeout調(diào)用的函數(shù)其實(shí)就是一個callback的體現(xiàn)
- 鏈?zhǔn)秸{(diào)用:鏈?zhǔn)秸{(diào)用的時候岸浑,在賦值器(setter)方法中(或者本身沒有返回值的方法中)很容易實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,而取值器(getter)相對來說不好實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用瑰步,因?yàn)槟阈枰≈灯鞣祷啬阈枰臄?shù)據(jù)而不是this指針矢洲,如果要實(shí)現(xiàn)鏈?zhǔn)椒椒ǎ梢杂没卣{(diào)函數(shù)來實(shí)現(xiàn)
- setTimeout缩焦、setInterval的函數(shù)調(diào)用得到其返回值读虏。由于兩個函數(shù)都是異步的责静,即:他們的調(diào)用時序和程序的主流程是相對獨(dú)立的,所以沒有辦法在主體里面等待它們的返回值盖桥,它們被打開的時候程序也不會停下來等待灾螃,否則也就失去了setTimeout及setInterval的意義了,所以用return已經(jīng)沒有意義揩徊,只能使用callback睦焕。callback的意義在于將timer執(zhí)行的結(jié)果通知給代理函數(shù)進(jìn)行及時處理。
回調(diào)函數(shù)的傳遞
上面說了靴拱,要將函數(shù)引用或者函數(shù)表達(dá)式作為參數(shù)傳遞垃喊。
$.get('myhtmlpage.html', myCallBack);//這是對的
$.get('myhtmlpage.html', myCallBack('foo', 'bar'));//這是錯的,那么要帶參數(shù)呢袜炕?
$.get('myhtmlpage.html', function(){//帶參數(shù)的使用函數(shù)表達(dá)式
myCallBack('foo', 'bar');
});
另外本谜,最好保證回調(diào)存在且必須是函數(shù)引用或者函數(shù)表達(dá)式:
(callback && typeof(callback) === "function") && callback();
通俗易懂的例子
你有事去隔壁寢室找同學(xué),發(fā)現(xiàn)人不在偎窘,你怎么辦呢乌助?
方法1,每隔幾分鐘再去趟隔壁寢室陌知,看人在不
方法2他托,拜托與他同寢室的人,看到他回來時叫一下你
前者是輪詢仆葡,后者是回調(diào)赏参。
那你說,我直接在隔壁寢室等到同學(xué)回來可以嗎沿盅?
可以啊把篓,只不過這樣原本你可以省下時間做其他事,現(xiàn)在必須浪費(fèi)在等待上了腰涧。把原來的非阻塞的異步調(diào)用變成了阻塞的同步調(diào)用韧掩。
JavaScript的回調(diào)是在異步調(diào)用場景下使用的,使用回調(diào)性能好于輪詢窖铡。