大家好,我是IT修真院成都分院第09期學(xué)員励饵。今天分享的內(nèi)容是js中回調(diào)函數(shù)肿嘲?
1.背景介紹
在我們接觸ajax,與angularjs中的數(shù)據(jù)請(qǐng)求時(shí)次乓,我們總是聽到里面的回調(diào)函數(shù)吓歇,callback。我們?cè)谑褂玫倪^程中總感覺它是一個(gè)混淆的概念票腰,它仿佛是ajax請(qǐng)求后調(diào)用的那個(gè)函數(shù)城看,又感覺它是某一個(gè)函數(shù)的形參而已。在后面angular和promise的學(xué)習(xí)中杏慰,我們會(huì)遇到非常多的回調(diào)函數(shù)测柠,因此我們有必要弄清其中的概念
2.知識(shí)剖析
我們都知道js是單線程的炼鞠,這種設(shè)計(jì)模式給我們帶來(lái)了很多的方便之處,我們不需要考慮各個(gè)線程之間的通信轰胁,也不需要寫很多燒腦的代碼谒主,也就是說(shuō)js的引擎只能一件一件事的去完成和執(zhí)行相關(guān)的操作,所以所有需要執(zhí)行的事情都像排隊(duì)一樣赃阀,等待著被觸發(fā)和執(zhí)行霎肯,可是如果這樣的話,如果在隊(duì)列中有一件事情需要花費(fèi)很多的時(shí)間榛斯,那么后面的任務(wù)都將處于一種等待狀態(tài)观游,有時(shí)甚至?xí)霈F(xiàn)瀏覽器假死現(xiàn)象,例如其中有一件正在執(zhí)行的一個(gè)任務(wù)是一個(gè)死循環(huán)驮俗,那么會(huì)導(dǎo)致后續(xù)其他的任務(wù)無(wú)法正常執(zhí)行懂缕,所以js在同步機(jī)制的缺陷下設(shè)計(jì)出了異步模式
在異步執(zhí)行的模式下,每一個(gè)異步的任務(wù)都有其自己一個(gè)或著多個(gè)回調(diào)函數(shù)王凑,這樣當(dāng)前在執(zhí)行的異步任務(wù)執(zhí)行完之后搪柑,不會(huì)馬上執(zhí)行事件隊(duì)列中的下一項(xiàng)任務(wù),而是執(zhí)行它的回調(diào)函數(shù)荤崇,而下一項(xiàng)任務(wù)也不會(huì)等當(dāng)前這個(gè)回調(diào)函數(shù)執(zhí)行完拌屏,因?yàn)樗膊荒艽_定當(dāng)前的回調(diào)合適執(zhí)行完畢潮针,只要引它被觸發(fā)就會(huì)執(zhí)行
1.搞清楚異步和同步
1.早上起來(lái)不論你是先刷牙還是先洗臉术荤,都要等一個(gè)事情完畢后才能進(jìn)行下一項(xiàng),這就是一個(gè)同步的例子
2.然后刷牙的時(shí)候你也可以燒水喝 (不用等你刷完牙)這就是一個(gè)異步的例子
function a(){
console.log("執(zhí)行函數(shù)a");
setTimeout(function(){
console.log('執(zhí)行a的延遲函數(shù)')
},1000)
}
function b(){
console.log('執(zhí)行函數(shù)b')
}
a();
b();
? ? 調(diào)用 setTimeout 函數(shù)會(huì)在一個(gè)時(shí)間段過去后在隊(duì)列中添加一個(gè)消息每篷。這個(gè)時(shí)間段作為函數(shù)的第二個(gè)參數(shù)被傳入瓣戚。如果隊(duì)列中沒有其它消息,消息會(huì)被馬上處理焦读。但是子库,如果有其它消息,setTimeout 消息必須等待其它消息處理完矗晃。因此第二個(gè)參數(shù)僅僅表示最少的時(shí)間 而非確切的時(shí)間
var a=0;
function change() {
a=1;
}
function after(){
console.log(a);
}
change();
after();
2.回調(diào)函數(shù)到底是什么仑嗅?
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.
3.回調(diào)函數(shù)是怎么運(yùn)作的
因?yàn)楹瘮?shù)在Javascript中是第一類對(duì)象,我們像對(duì)待對(duì)象一樣對(duì)待函數(shù)张症,因此我們能像傳遞變量一樣傳遞函數(shù)仓技,在函數(shù)中返回函數(shù),在其他函數(shù)中使用函數(shù)俗他。當(dāng)我們將一個(gè)回調(diào)函數(shù)作為參數(shù)傳遞給另一個(gè)函數(shù)是脖捻,我們僅僅傳遞了函數(shù)定義。我們并沒有在參數(shù)中執(zhí)行函數(shù)兆衅。我們并不傳遞像我們平時(shí)執(zhí)行函數(shù)一樣帶有一對(duì)執(zhí)行小括號(hào)()的函數(shù)地沮。
? ? 人話:字面上理解下來(lái)就是嗜浮,回調(diào)就是一個(gè)函數(shù)的調(diào)用過程。那么就從理解這個(gè)調(diào)用過程開始吧摩疑。函數(shù)a有一個(gè)參數(shù)危融,這個(gè)參數(shù)是個(gè)函數(shù)b,當(dāng)函數(shù)a執(zhí)行完以后執(zhí)行函數(shù)b雷袋。那么這個(gè)過程就叫回調(diào)专挪。
這里必須清楚一點(diǎn):函數(shù)b是你以參數(shù)形式傳給函數(shù)a的,那么函數(shù)b就叫回調(diào)函數(shù)片排。
3寨腔、常見問題
1.為什么寫回調(diào)函數(shù)
2.回調(diào)函數(shù)一定要以參數(shù)形式傳過去嗎,我不可以直接在函數(shù)a里面調(diào)用函數(shù)b嗎率寡?
4迫卢、解決方案
function a(callback)
{
console.log("我是parent函數(shù)a!");
console.log("調(diào)用回調(diào)函數(shù)");
callback();
}
function b(){
console.log("我是回調(diào)函數(shù)b");
}
function c(){
console.log("我是回調(diào)函數(shù)c");
}
function test()
{
a(b);
a(c);
}
test();
如果你直接在函數(shù)a里調(diào)用的話冶共,那么這個(gè)回調(diào)函數(shù)就被限制死了乾蛤。但是使用函數(shù)做參數(shù)就有下面的好處:當(dāng)你a(b)的時(shí)候函數(shù)b就成了回調(diào)函數(shù),而你還可以a(c)這個(gè)時(shí)候捅僵,函數(shù)c就成了回調(diào)函數(shù)家卖。如果你寫成了function a(){...;b();}就失去了變量的靈活性。
啟示:函數(shù)在javascript中和數(shù)據(jù)一樣庙楚,可以賦值上荡,刪除,拷貝馒闷,自然也可以作為函數(shù)的參數(shù)
5.擴(kuò)展思考
回調(diào)函數(shù)與ES6中的promise
6.參考文獻(xiàn)
7酪捡、更多討論
問題1:能否利用回調(diào)函數(shù)避免angular?ajax異步請(qǐng)求延時(shí)導(dǎo)致的注入錯(cuò)誤
答:$http 請(qǐng)求時(shí)間太長(zhǎng),就會(huì)導(dǎo)致注入錯(cuò)誤
問題2:實(shí)現(xiàn)異步是js纳账,還是宿主瀏覽器逛薇?
答:JS的單線程是指一個(gè)瀏覽器進(jìn)程中只有一個(gè)JS的執(zhí)行線程,同一時(shí)刻內(nèi)只會(huì)有一段代碼在執(zhí)行(你可以使用IE的標(biāo)簽式瀏覽試試看效果疏虫,這時(shí)打開的多個(gè)頁(yè)面使用的都是同一個(gè)JS執(zhí)行線程永罚,如果其中一個(gè)頁(yè)面在執(zhí)行一個(gè)運(yùn)算量較大的function時(shí),其他窗口的JS就會(huì)停止工作)卧秘。
而異步機(jī)制是瀏覽器的兩個(gè)或以上常駐線程共同完成的呢袱,例如異步請(qǐng)求是由兩個(gè)常駐線程:JS執(zhí)行線程和事件觸發(fā)線程共同完成的,JS的執(zhí)行線程發(fā)起異步請(qǐng)求(這時(shí)瀏覽器會(huì)開一條新的HTTP請(qǐng)求線程來(lái)執(zhí)行請(qǐng)求斯议,這時(shí)JS的任務(wù)已完成产捞,繼續(xù)執(zhí)行線程隊(duì)列中剩下的其他任務(wù)),然后在未來(lái)的某一時(shí)刻事件觸發(fā)線程監(jiān)視到之前的發(fā)起的HTTP請(qǐng)求已完成哼御,它就會(huì)把完成事件插入到JS執(zhí)行隊(duì)列的尾部等待JS處理坯临。又例如定時(shí)觸發(fā)(settimeout和setinterval)是由瀏覽器的定時(shí)器線程執(zhí)行的定時(shí)計(jì)數(shù)焊唬,然后在定時(shí)時(shí)間把定時(shí)處理函數(shù)的執(zhí)行請(qǐng)求插入到JS執(zhí)行隊(duì)列的尾端(所以用這兩個(gè)函數(shù)的時(shí)候,實(shí)際的執(zhí)行時(shí)間是大于或等于指定時(shí)間的看靠,不保證能準(zhǔn)確定時(shí)的)赶促。
所以,所謂的JS的單線程和異步更多的應(yīng)該是屬于瀏覽器的行為挟炬,他們之間沒有沖突鸥滨,更不是同一種事物,沒有什么區(qū)別不區(qū)別的谤祖。
問題3:js每次只執(zhí)行一個(gè)任務(wù)婿滓,他是如何實(shí)現(xiàn)異步的?
答:見代碼
鳴謝
感謝大家觀看粥喜!
------------------------------------------------------------------------------------------------------------------------
技能樹.IT修真院
“我們相信人人都可以成為一個(gè)工程師凸主,現(xiàn)在開始,找個(gè)師兄额湘,帶你入門卿吐,掌控自己學(xué)習(xí)的節(jié)奏,學(xué)習(xí)的路上不再迷梅婊”嗡官。
這里是技能樹.IT修真院,成千上萬(wàn)的師兄在這里找到了自己的學(xué)習(xí)路線毯焕,學(xué)習(xí)透明化衍腥,成長(zhǎng)可見化,師兄1對(duì)1免費(fèi)指導(dǎo)芥丧〗衾快來(lái)與我一起學(xué)習(xí)吧 坊罢!