您好,登錄后才能下訂單哦!
本篇文章為大家展示了怎樣掌握J(rèn)avaScript執(zhí)行機(jī)制,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
如果想了解JavaScript為什么是單線程的,我們就要從JavaScript是用來做什么工作的來入手。
JavaScript作為瀏覽器的腳本語言,產(chǎn)出的目的就是為了瀏覽器與用戶進(jìn)行交互,操作DOM元素,從而提升用戶的交互及體驗(yàn)感。JavaScript要操作瀏覽器的DOM元素,因此導(dǎo)致JavaScript無法成為多線程語言,我們假設(shè)一個(gè)場景,如果JavaScript是多線程語言,兩個(gè)線程同時(shí)操作一個(gè)DOM元素,一個(gè)線程需要編輯更新DOM元素,而另一個(gè)則是刪除DOM元素節(jié)點(diǎn),這是瀏覽器應(yīng)該以哪個(gè)為準(zhǔn)呢?
同一時(shí)間只能做同一件事情,因?yàn)椴僮鱀OM元素的原因,導(dǎo)致單線程是JavaScript這門語言的核心,也是這門語言特點(diǎn)。
HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個(gè)線程,但是子線程完全受主線程控制,且不得操作DOM。即使這樣的改動(dòng)也并沒有改變js單線程的本質(zhì)。
javaScript的單線程機(jī)制,就導(dǎo)致同一時(shí)間只能做一件事情。就像一堆人在ATM取款機(jī)取款,即使后面再多的人在著急,也只能一個(gè)一個(gè)的排隊(duì),等待前一個(gè)人取完款,才能輪到后一人。
可是這樣會(huì)導(dǎo)致如果說前一個(gè)任務(wù)消耗時(shí)間過長,后一個(gè)任務(wù)就會(huì)等待非常久,比如,我們需要加載一個(gè)數(shù)據(jù)量非常大的Ajax請求,我們不得不等待請求相應(yīng)結(jié)果,再繼續(xù)往下行執(zhí)后續(xù)任務(wù)。
那我們該如何處理這種情況呢?既然我們無法改變JavaScript的單線程機(jī)制,我們是否可以將一些耗時(shí)久的任務(wù)進(jìn)行暫時(shí)掛起,等到主任務(wù)執(zhí)行完成之后,再將這些掛載的任務(wù)執(zhí)行。JavaScript的作者也想到了這樣的方式,JavaScript擁有了同步任務(wù)與異步任務(wù)。
同步任務(wù)就是,任務(wù)在主線程上進(jìn)行排隊(duì),下一個(gè)任務(wù)必須等待上一個(gè)任務(wù)執(zhí)行完成,才可以執(zhí)行。而異步任務(wù)是指,任務(wù)不進(jìn)入主線程,而進(jìn)入任務(wù)隊(duì)列(task queue)進(jìn)行等待,進(jìn)入任務(wù)隊(duì)列的任務(wù)只有"任務(wù)隊(duì)列"通知主線程,某個(gè)異步任務(wù)可以執(zhí)行了,該任務(wù)才會(huì)進(jìn)入主線程執(zhí)行。
JavaScript的所有同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧。
任務(wù)隊(duì)列是先進(jìn)先出的原則,先進(jìn)隊(duì)列的事件先執(zhí)行,后進(jìn)隊(duì)列的事件后執(zhí)行。
Event Loop
執(zhí)行執(zhí)行棧中的同步任務(wù)。
當(dāng)遇到一個(gè)異步任務(wù)后不會(huì)一直等待其返回結(jié)果,會(huì)先將異步任務(wù)進(jìn)行暫時(shí)掛起,繼續(xù)執(zhí)行其他的同步任務(wù)。
當(dāng)異步任務(wù)有結(jié)果之后,JavaScript會(huì)任務(wù)將添加進(jìn)任務(wù)隊(duì)列中。被添加進(jìn)任務(wù)隊(duì)列中的任務(wù)不會(huì)立刻進(jìn)行回調(diào)執(zhí)行而是等待主線程(執(zhí)行棧)空閑時(shí)才加入到執(zhí)行棧中進(jìn)行回調(diào)執(zhí)行
等待執(zhí)行棧中的任務(wù)執(zhí)行完畢。
將任務(wù)隊(duì)列中的任務(wù)加入執(zhí)行棧后執(zhí)行。
如此反復(fù),這樣就形成了一個(gè)無限的循環(huán)(event loop)。(下圖來自網(wǎng)絡(luò))
學(xué)習(xí)了 Event Loop 我們一起來看看下面這道題:
setTimeout(function(){ console.log('setTimeout start') }); new Promise(function(resolve){ console.log('promise start'); resolve() }).then(function(){ console.log('promise then') }); console.log('script end');
嘗試按照,上文我們剛學(xué)到的JavaScript執(zhí)行機(jī)制去分析
1. 首先執(zhí)行同步任務(wù),執(zhí)行到setTimeout,但是setTimeout是異步任務(wù)的暫時(shí)掛起,等待計(jì)時(shí)超時(shí),添加進(jìn)任務(wù)隊(duì)列中。
2. 繼續(xù)往下,在執(zhí)行到new Promise,new Promise里面的是同步任務(wù),打印 "promise start"。
3. 在執(zhí)行到resolve將.then添加進(jìn)任務(wù)隊(duì)列中。
4. 在執(zhí)行 console.log('script end');打印"script end"。
5. 這時(shí)主任務(wù)都已經(jīng)執(zhí)行完成,在將異步任務(wù)添加進(jìn)主任務(wù)中直接執(zhí)行,打印"setTimeout start",再將.then添加進(jìn)主任務(wù)中,打印"promise then"。
所以結(jié)果應(yīng)該是:promise start -> script end -> setTimeout start -> promise then 嗎?
親自在瀏覽器執(zhí)行后,結(jié)果居然不是這樣,而是 promise start -> script end -> promise then -> setTimeout start
那為什么上文中的結(jié)果為什么跟我們預(yù)想的不一致,為什么 setTimeout start 會(huì)在 promise 之后打印。
其實(shí)是因?yàn)楫惒降膱?zhí)行也是有先后順序的。其實(shí)用異步跟同步的方式去劃分任務(wù)隊(duì)列的執(zhí)行順序是不準(zhǔn)確的。應(yīng)該劃分為 微任務(wù) 與 宏任務(wù)。
宏任務(wù)(macro-task):script 代碼、setTimeout、setInterval
微任務(wù)(micro-task):Promise、process.nextTick
所以說setTimeout是異步任務(wù)中的 宏任務(wù) ,而Promise是異步任務(wù)中的 微任務(wù) 。不管是 微任務(wù) 還是 宏任務(wù),都會(huì)進(jìn)入相應(yīng)的 Event Queue, 接下來我們在看一個(gè)流程圖。
我們來稍微理解一下:
1. 執(zhí)行宏任務(wù)(script代碼)
2. 當(dāng)執(zhí)行宏任務(wù)的時(shí)遇到了微任務(wù),就會(huì)將微任務(wù)添加進(jìn) Event Queue
3. 在當(dāng)前宏任務(wù)執(zhí)行完成后,會(huì)查看微任務(wù)的 Event Queue ,并將里面全部的微任務(wù)依次執(zhí)行完
4. 在執(zhí)行玩所有的為微任務(wù)之后,繼續(xù)進(jìn)行第一步,以此循環(huán)
這便也是 javaScript 的運(yùn)行機(jī)制,結(jié)合這個(gè)我們再重新的分析一下上面的例子。
1. 首先執(zhí)宏任務(wù)(script代碼 ),遇到setTimeout將其添加進(jìn)宏任務(wù)的Event Queue。
2. 繼續(xù)往下,在執(zhí)行到new Promise,打印 "promise start"。
3. 在執(zhí)行到resolve,.then是微任務(wù),添加進(jìn)微任務(wù)的Event Queue。
4. 在執(zhí)行 console.log('script end');打印 "script end"。
5. 到這里本輪的宏任務(wù)就已經(jīng)全部執(zhí)行結(jié)束了,這時(shí)查找微任務(wù)的 Eevent Queue 是否存在可執(zhí)行的微任務(wù), 發(fā)現(xiàn)有剛才在第三步添加進(jìn)去額度.then,執(zhí)行并打印 "promise then"
6. 這時(shí)第一輪的 event loop 就已經(jīng)徹底結(jié)束了,下一輪 event loop 先執(zhí)行一個(gè)宏任務(wù),發(fā)現(xiàn)宏任務(wù)的Event Queue中有添加進(jìn)去的setTimeout,執(zhí)行并打印 "setTimeout start"
永遠(yuǎn)記住JavaScript是單線程,以前是、現(xiàn)在是、將來也會(huì)是。所有的多線程說法都是扯淡。
即使是Event Queue,也只不是實(shí)現(xiàn)異步的方式,也是js的執(zhí)行機(jī)制。
以后能用JavaScript實(shí)現(xiàn)的。都將會(huì)用JavaScript來實(shí)現(xiàn)。
JS是JavaScript的簡稱,它是一種直譯式的腳本語言,其解釋器被稱為JavaScript引擎,是瀏覽器的一部分,主要用于web的開發(fā),可以給網(wǎng)站添加各種各樣的動(dòng)態(tài)效果,讓網(wǎng)頁更加美觀。
上述內(nèi)容就是怎樣掌握J(rèn)avaScript執(zhí)行機(jī)制,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。