javascript運(yùn)行環(huán)境和機(jī)制的介紹
搞了那么久的前端,從開始用jquery快速寫一些效果,還有一些類似于kissy的框架等等,這些東西,通過模塊的規(guī)范,加載的機(jī)制,還有api的封裝,可以快速的實(shí)現(xiàn)一些效果,開發(fā)一些應(yīng)用,以及后面流行下來的是聲明是渲染,函數(shù)式編程。
可是,底層的JavaScript的運(yùn)行機(jī)制,沒有怎么了解過,我們來看看。 JavaScript的運(yùn)行機(jī)制里面,非阻塞,單線程的機(jī)制,是不是。。
下圖所示(也許這個(gè)圖可以重新花一遍):
stack 棧
棧,push push push 然后pop pop pop 好了,一個(gè)先進(jìn)后出的數(shù)據(jù)模型,在js里面,有這么一個(gè)stack,我們用一個(gè)函數(shù)call它出來看看。
這個(gè)函數(shù)的意思是,callstack函數(shù),內(nèi)部return 然后調(diào)用他本身,運(yùn)行下來,應(yīng)該是,一直調(diào)用,往下走,好了,很明顯,計(jì)算機(jī)不是無窮的,給stack最大長度設(shè)了限制,然后,觸達(dá)了最大的調(diào)用限制,v8 print到window上面來了。過程大概是這樣的,如下圖:
再比如,JavaScript的鏈?zhǔn)阶饔糜,作用于或者函?shù)之間的調(diào)用,如下所示:
這樣的話,看起來,過程是這樣的,進(jìn)棧的順序是 c b a ,然后,從a開始return。
非阻塞(no-blocking)方案
接著上面的棧來講,v8會(huì)把運(yùn)行時(shí)里面的函數(shù)推到棧里面去,然后一個(gè)個(gè)的去執(zhí)行。 首先,計(jì)算機(jī)系統(tǒng)里面,你拿資源是耗時(shí)間的,但是,你去拿raw,很快,幾乎不用等,你去拿硬盤數(shù)據(jù),可能稍微慢一點(diǎn),但是也還可以,你去拿網(wǎng)絡(luò)數(shù)據(jù),發(fā)起請(qǐng)求,就很慢了,所以,如果函數(shù)中有長時(shí)間的等待過程,如果是blocking的,而js是單線程的,所以,你只能一次做一件事情。
不知大家是否有影響,一些寫的非常不好代碼,頁面里面聲明了一些按鈕,一些操作,然后,因?yàn)閖s還在運(yùn)行,導(dǎo)致了你的操作滯后了,瀏覽器記住了你的`操作,然后,后面一個(gè)一個(gè)的執(zhí)行了出來。
還有就是電腦非常落后的時(shí)候,cpu很慢的時(shí)候,也要類似的現(xiàn)象。所以,這個(gè)時(shí)候,瀏覽器其實(shí)是在做什么呢? 瀏覽器其實(shí)是一次可以完成非常多的工作,只是隱藏了一些你所不知道的細(xì)節(jié)在里面。
下面我們來介紹這個(gè)單線程,但是一次性可以做非常多工作的模型。
js的運(yùn)行棧里面,我們一個(gè)工程上,往棧里面壓函數(shù),然后執(zhí)行,返回,下一個(gè),這樣的節(jié)奏。而這個(gè)時(shí)候,如果是一個(gè)異步的網(wǎng)絡(luò)請(qǐng)求,會(huì)發(fā)生什么呢?
執(zhí)行棧里面,其實(shí)很快就可以繼續(xù)下一個(gè)函數(shù)的操作,為什么?為什么非阻塞,因?yàn)橛辛硗獾哪P,在處理等待,也即是callback咯。callback底層有是怎么做的呢?
所以,首先,要認(rèn)識(shí)到,瀏覽器的webapi其實(shí)是一個(gè)獨(dú)立出來的api環(huán)境,包括捕捉用戶的鼠標(biāo)點(diǎn)擊,雙擊,滑動(dòng),選擇,等等操作。我們不知道瀏覽器內(nèi)部的線程情況,比如,ui線程負(fù)責(zé)顯示,什么線程負(fù)責(zé)repaint這些,什么線程負(fù)責(zé)解析樹,什么線程負(fù)責(zé),不清楚,但是,我們可以推斷的是,webapi是另外的線程在控制,所以,不占用V8的線程。其他的部件也是不占用v8線程的,具體怎么排布可能要去研究瀏覽器內(nèi)部的實(shí)現(xiàn),而且每個(gè)瀏覽器也不一樣。大體上,模型是一致的。
所以,在這樣的前提下,我們把模型畫出來,如下圖所示:
webapi管著定時(shí)器,管著xhr異步,管著跟用戶的操作,但是,這些都有一個(gè)特征,就是callback,callback可以索引到一塊定義好的函數(shù)區(qū)域,然后,webapi在自身準(zhǔn)備好的時(shí)候,(這個(gè)準(zhǔn)備好ready的過程不描述,可能是用戶滾動(dòng),移動(dòng)端上,用戶的操作,或者各種內(nèi)置的接口)推出callback,把callback交給任務(wù)隊(duì)列,而事件循環(huán)做的事情,就是把任務(wù)隊(duì)列里面的時(shí)間放到執(zhí)行棧里面去執(zhí)行。
好,我們腦補(bǔ)一個(gè)xhr的過程,把某個(gè)執(zhí)行的上下文壓入stack里面,然后執(zhí)行到xhr調(diào)用,這個(gè)時(shí)候,xhr調(diào)用執(zhí)行完退出執(zhí)行stack,webapi里面預(yù)制起了xhr的callback,這個(gè)callback是定義好的。然后,執(zhí)行棧繼續(xù)做下面的操作。不同線程不相干。 然后,xhr在自己的process完成之后,把數(shù)據(jù)結(jié)構(gòu)拿回到callback,然后,callback的索引放到任務(wù)隊(duì)列里面,這個(gè)時(shí)候,有兩個(gè)while(true)一直跑著,一個(gè)是事件循環(huán),一個(gè)是執(zhí)行棧。任務(wù)隊(duì)列是先進(jìn)先出,事件循環(huán)拿出去,然后放到執(zhí)行棧里面執(zhí)行,over。
好,我們?cè)倌X補(bǔ)一個(gè)click過程,某個(gè)執(zhí)行上下文壓人stack里面,click的bind,這個(gè)時(shí)候,bind推出棧,然后,webapi里面聲明了綁定事件,以及callback,然后,等用戶click的時(shí)候,callback開始往任務(wù)隊(duì)列里面壓callback,然后,時(shí)間循環(huán)又來了,執(zhí)行callback。
【本文來源于(微信公眾號(hào):豬豬俠雜談)】
本文為原創(chuàng)文章,版權(quán)歸作者所有,未經(jīng)授權(quán)不得轉(zhuǎn)載!
【javascript運(yùn)行環(huán)境和機(jī)制的介紹】相關(guān)文章:
職業(yè)與環(huán)境管理體系的建立和運(yùn)行08-24
1.5操作系統(tǒng)的運(yùn)行環(huán)境簡單介紹說明11-18
如何設(shè)置java的運(yùn)行環(huán)境10-05
Java程序開發(fā)與運(yùn)行環(huán)境11-10
Java程序開發(fā)與運(yùn)行環(huán)境11-09
JavaScript的應(yīng)用11-11