摘要:這種問(wèn)題在設(shè)置倒計(jì)時(shí)的經(jīng)常遇到,倒計(jì)時(shí)開(kāi)始的時(shí)候設(shè)置的時(shí)間是從服務(wù)器拿到的系統(tǒng)時(shí)間很準(zhǔn)確,但是如果后面不定期像服務(wù)期請(qǐng)求系統(tǒng)時(shí)間進(jìn)行校準(zhǔn)的話,你可能會(huì)發(fā)現(xiàn)倒計(jì)時(shí)的偏差越來(lái)越來(lái)大,這就是主線程執(zhí)行的時(shí)間比設(shè)定的延遲時(shí)間長(zhǎng)導(dǎo)致的。
關(guān)于js執(zhí)行機(jī)制,老早之前就一直想寫(xiě)篇文章做個(gè)總結(jié),因?yàn)楹蚸s執(zhí)行順序的面試題碰到的特別多,每次碰到總是會(huì)去網(wǎng)上查,沒(méi)有系統(tǒng)地總結(jié),搞得每次碰到都是似懂非懂的感覺(jué),這篇文章就系統(tǒng)的總結(jié)一下js執(zhí)行機(jī)制。
任務(wù)隊(duì)列大家都知道js最大的特點(diǎn)就是單線程執(zhí)行,這就是為什么js簡(jiǎn)單易學(xué)的一個(gè)重要原因,不需要考慮復(fù)雜的同步問(wèn)題,但是單線程也會(huì)有一個(gè)問(wèn)題,所有的任務(wù)在執(zhí)行的過(guò)程中都必須等待前一個(gè)任務(wù)執(zhí)行完成才能執(zhí)行,這樣就會(huì)帶來(lái)一個(gè)效率的問(wèn)題,為了解決這個(gè)問(wèn)題,js將任務(wù)分為兩種:同步任務(wù)和異步任務(wù),同步任務(wù)就是之前說(shuō)后一個(gè)任務(wù)必須等待前一個(gè)任務(wù)執(zhí)行完成才能執(zhí)行,是在主線程上執(zhí)行的,而異步任務(wù)不會(huì)直接進(jìn)入主線程執(zhí)行,而是進(jìn)入任務(wù)隊(duì)列,只有在任務(wù)隊(duì)列通知異步任務(wù)可以執(zhí)行時(shí),才會(huì)被推入主線程執(zhí)行。讓我們來(lái)看一個(gè)更加直觀的流程圖:
setTimeout和setInterval說(shuō)到異步任務(wù),最常見(jiàn)就是setTimeout和setInterval兩兄弟了,setTimeout是延遲一定時(shí)間后執(zhí)行,但是只執(zhí)行一次,setInterval是每隔一定的時(shí)間執(zhí)行一次,會(huì)執(zhí)行多次,但是有時(shí)候我們會(huì)發(fā)現(xiàn)設(shè)置一定的延遲時(shí)間后,回調(diào)函數(shù)的執(zhí)行時(shí)間會(huì)比我們?cè)O(shè)置的時(shí)間要晚,這是為什么呢?上面我們說(shuō)過(guò),在任務(wù)執(zhí)行的時(shí)候setTimeout這類異步任務(wù)的回調(diào)會(huì)被放到異步隊(duì)列中等待執(zhí)行,當(dāng)延遲時(shí)間結(jié)束時(shí),如果主線程的任務(wù)已經(jīng)執(zhí)行完了,也就是處在空閑狀態(tài)時(shí),就會(huì)將任務(wù)隊(duì)列的回調(diào)推到主線程執(zhí)行,但是當(dāng)主線程的任務(wù)還沒(méi)有執(zhí)行完成時(shí),就只能繼續(xù)等待,來(lái)看一個(gè)例子:
let before = new Date() setTimeout(() => { console.log(new Date() - before) }, 1000) for (let i = 0; i < 300000; i++) { console.log("time delay") }
從上面的例子就可以看到:當(dāng)我們執(zhí)行完setTimeout之后,立刻執(zhí)行20萬(wàn)次的循環(huán),從執(zhí)行結(jié)果可以看到,setTimeout回調(diào)函數(shù)中的時(shí)間遠(yuǎn)高于設(shè)置1000ms,這就是因?yàn)闀r(shí)間到了,但是主線程的任務(wù)還沒(méi)有執(zhí)行完成導(dǎo)致。這種問(wèn)題在setInterval設(shè)置倒計(jì)時(shí)的經(jīng)常遇到,倒計(jì)時(shí)開(kāi)始的時(shí)候設(shè)置的時(shí)間是從服務(wù)器拿到的系統(tǒng)時(shí)間很準(zhǔn)確,但是如果后面不定期像服務(wù)期請(qǐng)求系統(tǒng)時(shí)間進(jìn)行校準(zhǔn)的話,你可能會(huì)發(fā)現(xiàn)倒計(jì)時(shí)的偏差越來(lái)越來(lái)大,這就是主線程執(zhí)行的時(shí)間比設(shè)定的延遲時(shí)間長(zhǎng)導(dǎo)致的。
macrotask和microtask在js中,異步任務(wù)除了有setTimeout這類的異步任務(wù),還有一類就是es6中很常用promise...then這類的異步任務(wù),因此除了同步任務(wù)和異步任務(wù),任務(wù)還可以更加細(xì)分為macrotask(宏任務(wù))和microtask(微任務(wù))
macrotask: 包括setTimeout、setInterval和執(zhí)行棧
microtask: 包括Promise、process.nextTick
要想理解這兩個(gè)概念,直接從一道簡(jiǎn)單的面試題入手,來(lái)看一個(gè)例子:
setTimeout(function() { console.log(1) }, 0); new Promise(function(resolve, reject) { console.log(2); resolve() }).then(function() { console.log(3) }); process.nextTick(function () { console.log(4) }) console.log(5)
思考一下上面例子的輸出結(jié)果,我們來(lái)仔細(xì)分析一下執(zhí)行過(guò)程:
第一輪:主線程開(kāi)始執(zhí)行,遇到setTimeout,將setTimeout的回調(diào)函數(shù)丟到宏任務(wù)隊(duì)列中,在往下執(zhí)行new Promise立即執(zhí)行,輸出2,then的回調(diào)函數(shù)丟到微任務(wù)隊(duì)列中,再繼續(xù)執(zhí)行,遇到process.nextTick,同樣將回調(diào)函數(shù)扔到為任務(wù)隊(duì)列,再繼續(xù)執(zhí)行,輸出5,當(dāng)所有宏任務(wù)執(zhí)行完成后看有沒(méi)有可以執(zhí)行的微任務(wù),發(fā)現(xiàn)有then函數(shù)和nextTick兩個(gè)微任務(wù),先執(zhí)行哪個(gè)呢?process.nextTick指定的異步任務(wù)總是發(fā)生在所有異步任務(wù)之前,因此先執(zhí)行process.nextTick輸出4然后執(zhí)行then函數(shù)輸出3,第一輪執(zhí)行結(jié)束。
第二輪從宏任務(wù)隊(duì)列開(kāi)始,發(fā)現(xiàn)setTimeout回調(diào),輸出1執(zhí)行完畢,因此結(jié)果是25431
最后用一張圖來(lái)總結(jié)一下:
總結(jié)這篇文章簡(jiǎn)單介紹了js執(zhí)行機(jī)制,希望看了之后,可以對(duì)大家認(rèn)識(shí)js的執(zhí)行機(jī)制會(huì)有所幫助。
如果有錯(cuò)誤或不嚴(yán)謹(jǐn)?shù)牡胤?,歡迎批評(píng)指正,如果喜歡,歡迎點(diǎn)贊收藏
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/100076.html
摘要:事件循環(huán)背景是一門(mén)單線程非阻塞的腳本語(yǔ)言,單線程意味著,代碼在執(zhí)行的任何時(shí)候,都只有一個(gè)主線程來(lái)處理所有的任務(wù)。在意識(shí)到該問(wèn)題之際,新特性中的可以讓成為一門(mén)多線程語(yǔ)言,但實(shí)際開(kāi)發(fā)中使用存在著諸多限制。這個(gè)地方被稱為執(zhí)行棧。 事件循環(huán)(Event Loop) 背景 JavaScript是一門(mén)單線程非阻塞的腳本語(yǔ)言,單線程意味著,JavaScript代碼在執(zhí)行的任何時(shí)候,都只有一個(gè)主線程來(lái)...
摘要:以多線程的形式,允許單個(gè)任務(wù)分成不同的部分進(jìn)行運(yùn)行。提供協(xié)調(diào)機(jī)制,一方面防止進(jìn)程之間和線程之間產(chǎn)生沖突,另一方面允許進(jìn)程之間和線程之間共享資源。主線程會(huì)不斷的重復(fù)上訴過(guò)程。 眾所周知,js是單線程的,說(shuō)到線程,我們首先來(lái)仔細(xì)辨析一下線程和進(jìn)程的知識(shí)。 一、進(jìn)程與線程 阮一峰老師的一篇文章寫(xiě)的很好 cpu會(huì)給當(dāng)前進(jìn)程分配資源,進(jìn)程是資源分配的最小單位,進(jìn)程的資源會(huì)分配給線程使用,線程是C...
摘要:如果沒(méi)有其他異步任務(wù)要處理比如到期的定時(shí)器,會(huì)一直停留在這個(gè)階段,等待請(qǐng)求返回結(jié)果。執(zhí)行的執(zhí)行事件關(guān)閉請(qǐng)求的,例如事件循環(huán)的每一次循環(huán)都需要依次經(jīng)過(guò)上述的階段。因此,才會(huì)早于執(zhí)行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任務(wù)(Synchronous) 在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù) ...
摘要:小程序的基礎(chǔ)庫(kù)不會(huì)被打包在某個(gè)小程序的代碼包里邊,它會(huì)被提前內(nèi)置在微信客戶端。小程序沒(méi)有重啟的概念當(dāng)小程序進(jìn)入后臺(tái),客戶端會(huì)維持一段時(shí)間的運(yùn)行狀態(tài),超過(guò)一定時(shí)間后目前是分鐘會(huì)被微信主動(dòng)銷毀當(dāng)短時(shí)間內(nèi)連續(xù)收到兩次 寫(xiě)作背景 接觸小程序有一段時(shí)間了,總得來(lái)說(shuō)小程序開(kāi)發(fā)門(mén)檻比較低,但其中基本的運(yùn)行機(jī)制和原理還是要懂的。比如我在面試的時(shí)候問(wèn)到一個(gè)關(guān)于小程序的問(wèn)題,問(wèn)小程序有window對(duì)象嗎?...
閱讀 2022·2021-11-22 14:44
閱讀 1830·2021-11-02 14:46
閱讀 3875·2021-10-13 09:40
閱讀 2721·2021-09-07 09:58
閱讀 1851·2021-09-03 10:28
閱讀 1736·2019-08-29 15:30
閱讀 1065·2019-08-29 15:28
閱讀 1552·2019-08-26 12:20