js同步異步以及回調(diào)函數(shù)
1.背景介紹
什么是同步,什么是異步?
同步指的是一次只能完成一件任務(wù)。如果有多個(gè)任務(wù),就必須排隊(duì),前面一個(gè)任務(wù)完成,再執(zhí)行后面一個(gè)任務(wù),以此類推。
異步指的是每一個(gè)任務(wù)有一個(gè)或多個(gè)回調(diào)函數(shù)(callback),前一個(gè)任務(wù)結(jié)束后,不是執(zhí)行后一個(gè)任務(wù),而是執(zhí)行回調(diào)函數(shù),后一個(gè)任務(wù)則是不等前一個(gè)任務(wù)結(jié)束就執(zhí)行,所以程序的執(zhí)行順序與任務(wù)的排列順序是不一致的、異步的。
2.知識(shí)剖析
javascript實(shí)現(xiàn)異步的原理
首先js是單線程的語(yǔ)言,即同一時(shí)間只能做做一件事。那Js如何實(shí)現(xiàn)異步的,異步和單線程不是自相矛盾嗎?其實(shí),單線程和異步確實(shí)不能同時(shí)成為一個(gè)語(yǔ)言的特性。js選擇了成為單線程的語(yǔ)言,所以它本身不可能是異步的,但js的宿主環(huán)境(比如瀏覽器,Node)是多線程的,宿主環(huán)境通過(guò)某種方式(事件驅(qū)動(dòng),下文會(huì)講)使得js具備了異步的屬性
瀏覽器的內(nèi)核是多線程的,它們?cè)趦?nèi)核制控下相互配合以保持同步,一個(gè)瀏覽器至少實(shí)現(xiàn)三個(gè)常駐線程:javascript引擎線程,UI渲染線程,瀏覽器事件觸發(fā)線程。
javascript引擎線程是基于事件驅(qū)動(dòng)單線程執(zhí)行的,JS引擎一直等待著任務(wù)隊(duì)列中任務(wù)的到來(lái),然后加以處理,瀏覽器無(wú)論什么時(shí)候都只有一個(gè)JS線程在運(yùn)行JS程序。
UI渲染線程負(fù)責(zé)渲染瀏覽器界面,當(dāng)界面需要重繪(Repaint)或由于某種操作引發(fā)回流(reflow)時(shí),該線程就會(huì)執(zhí)行。但需要注意UI渲染線程與JS引擎是互斥的,當(dāng)JS引擎執(zhí)行時(shí)UI線程會(huì)被掛起,UI更新會(huì)被保存在一個(gè)隊(duì)列中等到JS引擎空閑時(shí)立即被執(zhí)行.
事件觸發(fā)線程,當(dāng)一個(gè)事件被觸發(fā)時(shí)該線程會(huì)把事件添加到待處理隊(duì)列的隊(duì)尾,等待JS引擎的處理。這些事件可來(lái)自JavaScript引擎當(dāng)前執(zhí)行的代碼塊如setTimeOut、也可來(lái)自瀏覽器內(nèi)核的其他線程如鼠標(biāo)點(diǎn)擊、AJAX異步請(qǐng)求等,但由于JS的單線程關(guān)系所有這些事件都得排隊(duì)等待JS引擎處理。

image
分析:第一個(gè)在keydown的時(shí)候,彈出來(lái)的是input里原來(lái)的value,而第2個(gè)在keydown的時(shí)候,卻能彈出更新后的value,就是因?yàn)閟etTimeout,雖然他的delay設(shè)置為0,幾乎是即時(shí)觸發(fā),但還是被添加到了執(zhí)行隊(duì)列后面,但就是這個(gè)過(guò)程,渲染已經(jīng)完成了,當(dāng)他回調(diào)函數(shù)執(zhí)行時(shí),輸出來(lái)的已經(jīng)是更新后的value了。
注意:js的工作機(jī)制是當(dāng)線程空閑的情況下才會(huì)執(zhí)行異步代碼的回調(diào)函數(shù)
即當(dāng)所有同步任務(wù)執(zhí)行完畢后才會(huì)執(zhí)行異步任務(wù)的回調(diào)函數(shù)
總結(jié):當(dāng)Js執(zhí)行到異步任務(wù)后,會(huì)將異步任務(wù)交給瀏覽器進(jìn)行執(zhí)行,當(dāng)執(zhí)行有結(jié)果時(shí)會(huì)把異步任務(wù)的回調(diào)函數(shù)插入待處理隊(duì)列的隊(duì)尾。
3.常見(jiàn)問(wèn)題
ajax發(fā)送異步請(qǐng)求瀏覽器做了什么
有哪些常見(jiàn)異步回調(diào)函數(shù)?
4.解決方案
ajax發(fā)送異步請(qǐng)求瀏覽器做了什么?
Js創(chuàng)建了一個(gè)ajax請(qǐng)求
瀏覽器另外開(kāi)啟一個(gè)ajax引擎線程,執(zhí)行ajax請(qǐng)求
執(zhí)行得到響應(yīng)后將回調(diào)函數(shù)放入任務(wù)隊(duì)列中。
Js執(zhí)行任務(wù)隊(duì)列中的回調(diào)函數(shù)。
有哪些常見(jiàn)的異步回調(diào)函數(shù)?
點(diǎn)擊事件
Ajax請(qǐng)求
定時(shí)器
瀏覽器處理點(diǎn)擊事件的過(guò)程
瀏覽器開(kāi)啟事件觸發(fā)線程,等待用戶動(dòng)作,事件觸發(fā)線程解析為響應(yīng)事件,轉(zhuǎn)移到j(luò)avascript引擎線程,排隊(duì)等候,等待javascript引擎的處理。
例:
click mefunctionclickme(){console.log('點(diǎn)擊事件')? ? }for(i=0;i<50000;i++){console.log(i)? ? }
這個(gè)點(diǎn)擊事件會(huì)等到for循環(huán)執(zhí)行完畢后才會(huì)執(zhí)行,即我們點(diǎn)擊模塊它直到for循環(huán)執(zhí)行完畢才會(huì)執(zhí)行
5.編碼實(shí)戰(zhàn)
6.擴(kuò)展思考
如何實(shí)現(xiàn)js的多線程操作?
Html5的web worker
7.更多討論
8.參考文獻(xiàn)
參考一:js的單線程和異步
參考二:深入理解javascript異步編程障眼法&&h5 web worker實(shí)現(xiàn)多線程
參考三:談?wù)凧avaScript的異步實(shí)現(xiàn)- 小方- 博客園
Q1:瀏覽器的UI線程和Js線程為什么是互斥的?
A1:而因?yàn)镴S可以操作DOM元素,進(jìn)而會(huì)影響到GUI的渲染結(jié)果,因此JS引擎線程與GUI渲染線程是互斥的。
Q2:異步函數(shù)有哪些優(yōu)點(diǎn)和缺點(diǎn)
A2:
優(yōu)點(diǎn):
a)對(duì)CPU的使用率高。
b)不用考慮線程間同步互斥問(wèn)題。
缺點(diǎn):
a)實(shí)現(xiàn)較復(fù)雜,要把所有會(huì)導(dǎo)致阻塞的操作轉(zhuǎn)化為異步操作。
b)并發(fā)性不好,在有的事件需要長(zhǎng)時(shí)間占用CPU處理的情況下,其他事件會(huì)長(zhǎng)時(shí)間等待得不到處理。
c)在多CPU時(shí)不如多線程高效。
Q3:異步函數(shù)跟promise之間有什么關(guān)系?
A3:Promise它可以用于異步的回調(diào)函數(shù),它跟傳統(tǒng)的回調(diào)函數(shù)相比,promise的異步回調(diào)函數(shù)代碼書寫起來(lái)更優(yōu)雅,更便于閱讀。