前言
一個(gè)剛?cè)肭岸说男〔耍m然以前看到過(guò)關(guān)于回調(diào)的文章,但是呢,理解起來(lái)有點(diǎn)費(fèi)勁啊。當(dāng)時(shí)的腦海里就一個(gè)概念。
回調(diào):大多出現(xiàn)在Ajax請(qǐng)求,用于處理收到的請(qǐng)求結(jié)果。
嘿嘿,當(dāng)時(shí)真的就是這一個(gè)想法啊?,F(xiàn)在真的入這行,而且這個(gè)概念也非常重要,用的地方太多太多,是時(shí)候把它撿起來(lái)好好理解一番。
當(dāng)然,本文適合菜鳥(niǎo),因?yàn)槲沂且砸粋€(gè)菜鳥(niǎo)的思維去理解的。
回調(diào)概念
理解一個(gè)新東西,很有必須去理解下它的概念,因?yàn)檫@是最簡(jiǎn)潔明了,前人總結(jié)的。
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.
中文意思:回調(diào)是一個(gè)函數(shù)被作為一個(gè)參數(shù)傳遞到另一個(gè)函數(shù)里,在那個(gè)函數(shù)執(zhí)行完后再執(zhí)行。
有點(diǎn)繞,好,咱們說(shuō)大白話。就是 B函數(shù)被作為參數(shù)傳遞到A函數(shù)里,在A函數(shù)執(zhí)行完后再執(zhí)行B。
下面咱們看看代碼怎么實(shí)現(xiàn)回調(diào)。
function A(callback){
console.log("I am A");
callback(); //調(diào)用該函數(shù)
}
function B(){
console.log("I am B");
}
A(B);
這應(yīng)該是最最簡(jiǎn)單的回調(diào)了,我想大家應(yīng)該明白回調(diào)的釋義了吧。
當(dāng)然,這么簡(jiǎn)單的同步回調(diào)代碼是不會(huì)用的,現(xiàn)實(shí)中用都是相對(duì)比較復(fù)雜帶傳參。
回調(diào)函數(shù)和異步
一開(kāi)始我被回調(diào)和異步有點(diǎn)搞暈了。還以為回調(diào)就一定是異步的呢。
其實(shí)不然,相信上面的A,B函數(shù)的例子我們已經(jīng)明白,回調(diào)并不一定就是異步。他們自己并沒(méi)有直接關(guān)系。
下面我們可以理解下 同步回調(diào)和異步回調(diào)(同步異步我就不單獨(dú)講了,概念很簡(jiǎn)單)。
同步回調(diào)
就是上面的A B函數(shù)例子,它們就是同步的回調(diào)。
異步回調(diào)
因?yàn)閖s是單線程的,但是有很多情況的執(zhí)行步驟(ajax請(qǐng)求遠(yuǎn)程數(shù)據(jù),IO等)是非常耗時(shí)的,如果一直單線程的堵塞下去會(huì)導(dǎo)致程序的等待時(shí)間過(guò)長(zhǎng)頁(yè)面失去響應(yīng),影響用戶體驗(yàn)了。
如何去解決這個(gè)問(wèn)題呢,我們可以這么想。耗時(shí)的我們都扔給異步去做,做好了再通知下我們做完了,我們拿到數(shù)據(jù)繼續(xù)往下走。
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true); //第三個(gè)參數(shù)決定是否采用異步的方式
xhr.send(data);
xhr.onreadystatechange = function(){
if(xhr.readystate === 4 && xhr.status === 200){
///xxxx
}
}
上面是一個(gè)代碼,瀏覽器在發(fā)起一個(gè)ajax請(qǐng)求,會(huì)單開(kāi)一個(gè)線程去發(fā)起http請(qǐng)求,這樣的話就能把這個(gè)耗時(shí)的過(guò)程單獨(dú)去自己跑了,在這個(gè)線程的請(qǐng)求過(guò)程中,readystate 的值會(huì)有個(gè)變化的過(guò)程,每一次變化就觸發(fā)一次onreadystatechange 函數(shù),進(jìn)行判斷是否正確拿到返回結(jié)果。
異步編程的實(shí)現(xiàn)
就我目前知道兩種 回調(diào)函數(shù) 和 事件監(jiān)聽(tīng) ,其實(shí)看了阮神的 [異步編程的文章][2] 和下面的評(píng)論之后得出的理解。下面咱們就看看這兩種異步編程的方式吧。
回調(diào)函數(shù)
假定有三個(gè)函數(shù)
f1()
f2()
f3()
但是,f1執(zhí)行很耗時(shí),而 f2需要在f1執(zhí)行完之后執(zhí)行。
為了不影響 f3的執(zhí)行,我們可以把f2寫(xiě)成f1的回調(diào)函數(shù)。
//最原始的寫(xiě)法-同步寫(xiě)法
f1(); //耗時(shí)很長(zhǎng),嚴(yán)重堵塞
f2();
f3(); //導(dǎo)致f3執(zhí)行受到影響
//改進(jìn)版-異步寫(xiě)法
function f1(callback){
setTimeout(function () {
// f1的任務(wù)代碼
callback();
}, 1000);
}
f1(f2); //
f3();
上面的寫(xiě)法是利用 setTimeOut把f1的邏輯包括起來(lái),實(shí)現(xiàn)javascript中的異步編程。這樣的話,f1異步了,不再堵塞f3的執(zhí)行。
順道說(shuō)下,js是單線程的,這里所謂的異步也是偽異步,并不是開(kāi)了多線程的異步。它是什么原理呢,其實(shí)是任務(wù)棧,setTimeOut方法的原理是根據(jù)后面的定時(shí)時(shí)間,過(guò)了這個(gè)定時(shí)時(shí)間后,將f1加入任務(wù)棧,注意僅僅是加入任務(wù)棧,并不是放進(jìn)去就執(zhí)行,而是根據(jù)任務(wù)棧里的任務(wù)數(shù)量來(lái)確定的。
事件監(jiān)聽(tīng)
這里我直接用阮神的例子,通過(guò)事件觸發(fā)操作,就是類似于咱們點(diǎn)擊事件里的處理邏輯。
同樣f1 , f2兩個(gè)函數(shù)。
f1()
f2()
f1 我們給它加一個(gè)事件,事件觸發(fā) f2 函數(shù)。
function f1(){
setTimeOut(function(){
f1.trigger('click');
})
}
f1.on('click' , f2);
另外多說(shuō)點(diǎn),這上面的兩種方式都是 js 中的偽異步,而 ajax的異步是底層多線程函數(shù)異步。
寫(xiě)在最后
由于時(shí)間問(wèn)題,后續(xù)的理解會(huì)再補(bǔ)上,再理理思路。另外如果有錯(cuò)誤,也請(qǐng)各位前輩給予指正,感激不盡。
參考文獻(xiàn)
http://blog.csdn.net/kobejayandy/article/details/17654967
http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
https://segmentfault.com/a/1190000002999668
https://segmentfault.com/a/1190000003961547
感謝上面4篇文章的作者的辛勤付出,看完很有收獲。