javascript
理解JavaScript的执行机制
一直沒有深入了解過JavaScript的事件執(zhí)行機(jī)制,直到看到了這篇文章:《這一次,徹底弄懂JavaScript執(zhí)行機(jī)制》?才發(fā)覺熟悉JavaScript的執(zhí)行機(jī)制非常重要。
畢竟在跟進(jìn)項(xiàng)目中偶爾需要排查為什么會(huì)出現(xiàn)函數(shù)執(zhí)行順序不一樣的情況。
感謝作者淺顯易懂的文字讓我獲益匪淺,以下是自己對(duì)JavaScript執(zhí)行機(jī)制的理解,全是流水賬。
?
文章主要敘述:
1:單線程和異步任務(wù)
2: 任務(wù)更精細(xì)的分類
3:setTimeout 和 setInterval 的執(zhí)行方式
?
同步任務(wù)和異步任務(wù)
JavaScript的特點(diǎn)就是單線程,也就是說,同一個(gè)時(shí)間只能做一件事。換句話說就是一行一行地按照順序執(zhí)行代碼
?
1:同步任務(wù)指的是直接進(jìn)入主線程執(zhí)行的任務(wù)。
2:異步任務(wù)指的是,不進(jìn)入主線程、而進(jìn)入"任務(wù)隊(duì)列"(task queue)等待同步任務(wù)全部執(zhí)行完畢主線程執(zhí)行棧為空時(shí)再去任務(wù)隊(duì)列查找等待被調(diào)用的函數(shù)
?
console.log(1);let timeId = setTimeout(() => {console.log(2); },0); console.log(3);?運(yùn)行上面的代碼打印出來的是:1,3,2
?
這里執(zhí)行順序是這樣的:
1:執(zhí)行console.log(1)
2:遇到timeId是異步任務(wù),先放到任務(wù)隊(duì)列
3:立即執(zhí)行console.log(3)
4:同步任務(wù)執(zhí)行完畢,去查找任務(wù)隊(duì)列里面的事件,發(fā)現(xiàn)timeId有運(yùn)行結(jié)果了,執(zhí)行timeId。隊(duì)列中沒有其他的事件了,主線程運(yùn)行完畢。
?
?也就是常說的JS事件循環(huán):
- 同步和異步任務(wù)分別進(jìn)入不同的執(zhí)行"場(chǎng)所",同步的進(jìn)入主線程,異步的進(jìn)入Event Table并注冊(cè)函數(shù)。
- 當(dāng)指定的事情完成時(shí),Event Table會(huì)將這個(gè)函數(shù)移入Event Queue(任務(wù)隊(duì)列)。
- 主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會(huì)去Event Queue讀取對(duì)應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行。
- 上述過程會(huì)不斷重復(fù),也就是常說的Event Loop(事件循環(huán)) 直到任務(wù)隊(duì)列清空。
那怎么知道主線程執(zhí)行棧為空啊?js引擎存在monitoring process進(jìn)程,會(huì)持續(xù)不斷的檢查主線程執(zhí)行棧是否為空,一旦為空,就會(huì)去Event Queue那里檢查是否有等待被調(diào)用的函數(shù)。
?
?
再來看段代碼:
let timeId = setTimeout(() => {function task() {console.log('會(huì)2秒之后運(yùn)行嗎');}task();},2000);let oldTime = new Date();function sleep(){ let newTime = new Date(); console.log('time:' + ( newTime.getSeconds() - oldTime.getSeconds())); if( newTime.getSeconds() - oldTime.getSeconds() < 5) { sleep(); } } sleep();?
運(yùn)行上面代碼,發(fā)現(xiàn)task并沒有在2秒之內(nèi)執(zhí)行而是在5秒之后才執(zhí)行。
?
這是因?yàn)殡m然timeId暫時(shí)被掛起,并且在2秒后有了運(yùn)行結(jié)果后在"任務(wù)隊(duì)列"之中放置一個(gè)事件通知主線程timeId可以執(zhí)行了。
但因?yàn)橹骶€程中的同步任務(wù)sleep要5秒之后才運(yùn)行完畢,導(dǎo)致執(zhí)行棧5秒后才去任務(wù)隊(duì)列中執(zhí)行等待中的timeId函數(shù)
?
(好比約了朋友去玩,出門前要換衣服,要約滴滴打車。用手機(jī)約好車之后就去換衣服,但是換衣服實(shí)在太久了,車都來了衣服還沒換好。司機(jī)打電話說我到了,你快過來坐車吧。
我跟司機(jī)說,衣服沒換好,你先等等吧。過了半小時(shí)之后終于換好了衣服(司機(jī)等得要砍人了)終于可以去坐車了)
?
總得來說其實(shí)JavaScript只有一個(gè)主線程,運(yùn)行過程中碰到異步任務(wù)就先掛起。而有了運(yùn)行結(jié)果表示準(zhǔn)備好了可以執(zhí)行的異步任務(wù)就進(jìn)入執(zhí)行棧中在同步任務(wù)后面去排隊(duì)。
?
?
任務(wù)更精細(xì)的定義
macro-task(宏任務(wù)):包括整體代碼script、setTimeout、setInterval、setImmediate、I/O(ajax)、UI交互事件(onClick,onScroll...)
micro-task(微任務(wù)):Promise、process.nextTick、MutaionObserver
?
不同的API注冊(cè)的異步任務(wù)會(huì)依次進(jìn)入自身對(duì)應(yīng)的隊(duì)列中,然后等待Event Loop(事件循環(huán))將它們依次放入主線程中執(zhí)行
- 進(jìn)入整體代碼script(宏任務(wù))后開始第一次循環(huán)。接著執(zhí)行當(dāng)前所有的微任務(wù)。
- 然后再次從宏任務(wù)開始,找到其中一個(gè)任務(wù)隊(duì)列執(zhí)行完畢再執(zhí)行當(dāng)前宏任務(wù)中所有的微任務(wù)。
- 繼續(xù)執(zhí)行以上一步過程,直到所有任務(wù)執(zhí)行完畢
?
測(cè)試一下:
console.log(1);let timeId01 = setTimeout(() => {console.log(2);let Promise02 = new Promise(resolve => {console.log(3);resolve();}).then(() => {console.log(4);}); },0);let Promise01 = new Promise(resolve => {console.log(5);resolve(); }).then(() => {console.log(6); });let timeId02 = setTimeout(() => {console.log(8);let Promise03 = new Promise(resolve => {console.log(9);resolve();}).then(() => {console.log(10);}); }, 0) console.log(7);?
打印出來是:1,5,7,6,2,3,4,8,9,10
執(zhí)行順序?qū)嶋H上是這樣的:
1:執(zhí)行宏任務(wù)(整體代碼)// 1,5,7
2:執(zhí)行微任務(wù)(Promise01的then方法)// 6
3:執(zhí)行宏任務(wù)(timeId)//2,3
4:執(zhí)行微任務(wù)(timeId里面Promise02的then方法)// 4
5:執(zhí)行宏任務(wù)(timeId02)// 8,9
6:執(zhí)行微任務(wù)(timeId02里面Promise03的then方法)// 10?
?
setTimeout和setInterval的執(zhí)行方式
setTimeout和setInterval都是異步可以延時(shí)執(zhí)行的方法。
setTimeout(fn,ms)到了ms秒后fn會(huì)進(jìn)入任務(wù)隊(duì)列去等待執(zhí)行,而setInterval(fn, ms)則是每到了ms秒都會(huì)有一個(gè)fn進(jìn)入任務(wù)隊(duì)列去排隊(duì)等待執(zhí)行,直到某個(gè)條件結(jié)束。
?
關(guān)于setTimeout(fn,ms)中的ms設(shè)置:
let timeId01 = setTimeout(() => {console.log(1); },1); let timeId02 = setTimeout(() => {console.log(2); },0);?
上面代碼中timeId01的timer設(shè)置為1,timeId02的timer設(shè)置為0。按理說timeId02的執(zhí)行順序比timeId01要優(yōu)先,但實(shí)際上打印結(jié)果是:1,2
?
這是因?yàn)橐话銇碚ftimer最小只能設(shè)置4ms(嵌套層超過5,并且設(shè)置的timeout的時(shí)間少于4才有效)。但為了向?qū)崿F(xiàn)看齊可最小設(shè)置1ms,0ms的時(shí)候會(huì)被設(shè)置為1ms
?
其中setTimeout(fn,0)的含義是,指定某個(gè)任務(wù)在主線程最早可得的空閑時(shí)間執(zhí)行,意思就是不用再等多少秒了,只要主線程執(zhí)行棧內(nèi)的同步任務(wù)全部執(zhí)行完成,棧為空就馬上執(zhí)行。
?
轉(zhuǎn)載于:https://www.cnblogs.com/Travel/p/8320761.html
總結(jié)
以上是生活随笔為你收集整理的理解JavaScript的执行机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: socks5 运行几个小时后 端口108
- 下一篇: 好用的wordpress主题