成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript 運(yùn)行機(jī)制--Event Loop詳解

snifes / 1832人閱讀

摘要:上代碼代碼可以看出,不僅函數(shù)比指定的回調(diào)函數(shù)先執(zhí)行,而且函數(shù)也比先執(zhí)行。這是因?yàn)楹笠粋€(gè)事件進(jìn)入的時(shí)候,事件環(huán)可能處于不同的階段導(dǎo)致結(jié)果的不確定。這是因?yàn)橐驗(yàn)閳?zhí)行完后,程序設(shè)定了和,因此階段不會(huì)被阻塞進(jìn)而進(jìn)入階段先執(zhí)行,后進(jìn)入階段執(zhí)行。

JavaScript(簡稱JS)是前端的首要研究語言,要想真正理解JavaScript就繞不開他的運(yùn)行機(jī)制--Event Loop(事件環(huán))

JS是一門單線程的語言,異步操作是實(shí)際應(yīng)用中的重要的一部分,關(guān)于異步操作參考我的另一篇文章js異步發(fā)展歷史與Promise原理分析 這里不再贅述。

堆、棧、隊(duì)列 堆(heap)

堆(heap)是指程序運(yùn)行時(shí)申請(qǐng)的動(dòng)態(tài)內(nèi)存,在JS運(yùn)行時(shí)用來存放對(duì)象。

棧(stack)

棧(stack)遵循的原則是“先進(jìn)后出”,JS種的基本數(shù)據(jù)類型與指向?qū)ο蟮牡刂反娣旁跅?nèi)存中,此外還有一塊棧內(nèi)存用來執(zhí)行JS主線程--執(zhí)行棧(execution context stack),此文章中的棧只考慮執(zhí)行棧。

隊(duì)列(queue)

隊(duì)列(queue)遵循的原則是“先進(jìn)先出”,JS中除了主線程之外還存在一個(gè)“任務(wù)隊(duì)列”(其實(shí)有兩個(gè),后面再詳細(xì)說明)。

Event Loop

JS的單線程也就是說所有的任務(wù)都需要按照一定的規(guī)則順序排隊(duì)執(zhí)行,這個(gè)規(guī)則就是我們要說明的Event Loop事件環(huán)。Event Loop在不同的運(yùn)行環(huán)境下有著不同的方式。

瀏覽器環(huán)境下的Event Loop

先上圖(轉(zhuǎn)自Philip Roberts的演講《Help, I"m stuck in an event-loop》)

當(dāng)主線程運(yùn)行的時(shí)候,JS會(huì)產(chǎn)生堆和棧(執(zhí)行棧)

主線程中調(diào)用的webaip所產(chǎn)生的異步操作(dom事件、ajax回調(diào)、定時(shí)器等)只要產(chǎn)生結(jié)果,就把這個(gè)回調(diào)塞進(jìn)“任務(wù)隊(duì)列”中等待執(zhí)行。

當(dāng)主線程中的同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)依次讀取“任務(wù)隊(duì)列”中的任務(wù),將任務(wù)放進(jìn)執(zhí)行棧中執(zhí)行。

執(zhí)行任務(wù)時(shí)可能還會(huì)產(chǎn)生新的異步操作,會(huì)產(chǎn)生新的循環(huán),整個(gè)過程是循環(huán)不斷的。

從事件環(huán)中不難看出當(dāng)我們調(diào)用setTimeout并設(shè)定一個(gè)確定的時(shí)間,而這個(gè)任務(wù)的實(shí)際執(zhí)行時(shí)間可能會(huì)由于主線程中的任務(wù)沒有執(zhí)行完而大于我們?cè)O(shè)定的時(shí)間,導(dǎo)致定時(shí)器不準(zhǔn)確,也是連續(xù)調(diào)用setTimeout與調(diào)用setInterval會(huì)產(chǎn)生不同效果的原因(此處就不再展開,有時(shí)間我會(huì)多帶帶寫一篇文章)。

接下來上代碼:

console.log(1);
console.log(2);
setTimeout(function(){
    console.log(3)
    setTimeout(function(){
        console.log(6);
    })
},0)
setTimeout(function(){
    console.log(4);
    setTimeout(function(){
        console.log(7);
    })
},0)
console.log(5)

代碼中的setTimeout的時(shí)間給得0,相當(dāng)于4ms,也有可能大于4ms(不重要)。我們要注意的是代碼輸出的順序。我們把任務(wù)以其輸出的數(shù)字命名。
先執(zhí)行的一定是同步代碼,先輸出1,2,5,而3任務(wù),4任務(wù)這時(shí)會(huì)依次進(jìn)入“任務(wù)隊(duì)列中”。同步代碼執(zhí)行完畢,隊(duì)列中的3會(huì)進(jìn)入執(zhí)行棧執(zhí)行,4到了隊(duì)列的最前端,3執(zhí)行完后,內(nèi)部的setTimeout將6的任務(wù)放入隊(duì)列尾部。開始執(zhí)行4任務(wù)……

最終我們得到的輸出為1,2,5,3,4,6,7。

宏任務(wù)與微任務(wù)

任務(wù)隊(duì)列中的所有任務(wù)都是會(huì)乖乖排隊(duì)的嗎?答案是否定的,任務(wù)也是有區(qū)別的,總是有任務(wù)會(huì)有一些特權(quán)(比如插隊(duì)),就是任務(wù)中的vip--微任務(wù)(micro-task),那些沒有特權(quán)的--宏任務(wù)(macro-task)。
我們看一段代碼:

console.log(1);
setTimeout(function(){
    console.log(2);
    Promise.resolve(1).then(function(){
        console.log("promise")
    })
})
setTimeout(function(){
    console.log(3);
})

按照“隊(duì)列理論”,結(jié)果應(yīng)該為1,2,3,promise??墒菍?shí)際結(jié)果事與愿違輸出的是1,2,promise,3。

明明是3先進(jìn)入的隊(duì)列 ,為什么promise會(huì)排在前面輸出?這是因?yàn)閜romise有特權(quán)是微任務(wù),當(dāng)主線程任務(wù)執(zhí)行完畢微任務(wù)會(huì)排在宏任務(wù)前面先去執(zhí)行,不管是不是后來的。

換句話說,就是任務(wù)隊(duì)列實(shí)際上有兩個(gè),一個(gè)是宏任務(wù)隊(duì)列,一個(gè)是微任務(wù)隊(duì)列,當(dāng)主線程執(zhí)行完畢,如果微任務(wù)隊(duì)列中有微任務(wù),則會(huì)先進(jìn)入執(zhí)行棧,當(dāng)微任務(wù)隊(duì)列沒有任務(wù)時(shí),才會(huì)執(zhí)行宏任務(wù)的隊(duì)列。

微任務(wù)包括: 原生Promise(有些實(shí)現(xiàn)的promise將then方法放到了宏任務(wù)中),Object.observe(已廢棄), MutationObserver, MessageChannel;

宏任務(wù)包括:setTimeout, setInterval, setImmediate, I/O;

Node環(huán)境下的Event Loop
   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

node中的時(shí)間循環(huán)與瀏覽器的不太一樣,如圖:

timers 階段: 這個(gè)階段執(zhí)行setTimeout(callback) and setInterval(callback)預(yù)定的callback;

I/O callbacks 階段: 執(zhí)行除了close事件的callbacks、被timers(定時(shí)器,setTimeout、setInterval等)設(shè)定的callbacks、setImmediate()設(shè)定的callbacks之外的callbacks;

idle, prepare 階段: 僅node內(nèi)部使用;

poll 階段: 獲取新的I/O事件, 適當(dāng)?shù)臈l件下node將阻塞在這里;

check 階段: 執(zhí)行setImmediate() 設(shè)定的callbacks;

close callbacks 階段: 比如socket.on(‘close’, callback)的callback會(huì)在這個(gè)階段執(zhí)行。

每一個(gè)階段都有一個(gè)裝有callbacks的fifo queue(隊(duì)列),當(dāng)event loop運(yùn)行到一個(gè)指定階段時(shí),
node將執(zhí)行該階段的fifo queue(隊(duì)列),當(dāng)隊(duì)列callback執(zhí)行完或者執(zhí)行callbacks數(shù)量超過該階段的上限時(shí),
event loop會(huì)轉(zhuǎn)入下一下階段。

process.nextTick

process.nextTick方法不在上面的事件環(huán)中,我們可以把它理解為微任務(wù),它的執(zhí)行時(shí)機(jī)是當(dāng)前"執(zhí)行棧"的尾部----下一次Event Loop(主線程讀取"任務(wù)隊(duì)列")之前----觸發(fā)回調(diào)函數(shù)。也就是說,它指定的任務(wù)總是發(fā)生在所有異步任務(wù)之前。setImmediate方法則是在當(dāng)前"任務(wù)隊(duì)列"的尾部添加事件,也就是說,它指定的任務(wù)總是在下一次Event Loop時(shí)執(zhí)行。上代碼:

process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log("TIMEOUT FIRED");
}, 0)
// 1
// 2
// TIMEOUT FIRED

代碼可以看出,不僅函數(shù)A比setTimeout指定的回調(diào)函數(shù)timeout先執(zhí)行,而且函數(shù)B也比timeout先執(zhí)行。這說明,如果有多個(gè)process.nextTick語句(不管它們是否嵌套),將全部在當(dāng)前"執(zhí)行棧"執(zhí)行。

setTimeout 和 setImmediate

二者非常相似,但是二者區(qū)別取決于他們什么時(shí)候被調(diào)用.

setImmediate 設(shè)計(jì)在poll階段完成時(shí)執(zhí)行,即check階段;

setTimeout 設(shè)計(jì)在poll階段為空閑時(shí),且設(shè)定時(shí)間到達(dá)后執(zhí)行;但其在timer階段執(zhí)行

其二者的調(diào)用順序取決于當(dāng)前event loop的上下文,如果他們?cè)诋惒絠/o callback之外調(diào)用,其執(zhí)行先后順序是不確定的。

setTimeout(function timeout () {
  console.log("timeout");
},0);

setImmediate(function immediate () {
  console.log("immediate");
});
$ node timeout_vs_immediate.js
timeout
immediate

$ node timeout_vs_immediate.js
immediate
timeout

這是因?yàn)楹笠粋€(gè)事件進(jìn)入的時(shí)候,事件環(huán)可能處于不同的階段導(dǎo)致結(jié)果的不確定。當(dāng)我們給了事件環(huán)確定的上下文,事件的先后就能確定了。

var fs = require("fs")

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log("timeout")
  }, 0)
  setImmediate(() => {
    console.log("immediate")
  })
})
$ node timeout_vs_immediate.js
immediate
timeout

這是因?yàn)橐驗(yàn)閒s.readFile callback執(zhí)行完后,程序設(shè)定了timer 和 setImmediate,因此poll階段不會(huì)被阻塞進(jìn)而進(jìn)入check階段先執(zhí)行setImmediate,后進(jìn)入timer階段執(zhí)行setTimeout。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93496.html

相關(guān)文章

  • Javascript 運(yùn)行機(jī)制詳解,Event Loop

    摘要:主線程在任務(wù)隊(duì)列中讀取事件,這個(gè)過程是循環(huán)不斷地,所以這種運(yùn)行機(jī)制叫做事件循環(huán)是在執(zhí)行棧同步代碼結(jié)束之后,下一次任務(wù)隊(duì)列執(zhí)行之前。 單線程 javascript為什么是單線程語言,原因在于如果是多線程,當(dāng)一個(gè)線程對(duì)DOM節(jié)點(diǎn)做添加內(nèi)容操作的時(shí)候,另一個(gè)線程要?jiǎng)h除這個(gè)DOM節(jié)點(diǎn),這個(gè)時(shí)候,瀏覽器應(yīng)該怎么選擇,這就造成了混亂,為了解決這類問題,在一開始的時(shí)候,javascript就采用單線...

    Jingbin_ 評(píng)論0 收藏0
  • JavaScript中線程運(yùn)行機(jī)制詳解

    摘要:中線程運(yùn)行機(jī)制詳解對(duì)于我們都知道,他是個(gè)單線程語言,但是準(zhǔn)確來說它是擁有一個(gè)執(zhí)行程序主線程,和消息隊(duì)列輔線程,以及各個(gè)真正處理異步操作的工作線程。 JavaScript中線程運(yùn)行機(jī)制詳解 對(duì)于JavaScript我們都知道,他是個(gè)單線程語言,但是準(zhǔn)確來說它是擁有一個(gè)執(zhí)行程序主線程,和消息隊(duì)列輔線程(Event Loop),以及各個(gè)真正處理異步操作的工作線程。當(dāng)主線程執(zhí)行JS程序的時(shí)候,...

    xiangchaobin 評(píng)論0 收藏0
  • JavaScript Event Loop 機(jī)制詳解與 Vue.js 中實(shí)踐應(yīng)用

    摘要:機(jī)制詳解與中實(shí)踐應(yīng)用歸納于筆者的現(xiàn)代開發(fā)語法基礎(chǔ)與實(shí)踐技巧系列文章。事件循環(huán)機(jī)制詳解與實(shí)踐應(yīng)用是典型的單線程單并發(fā)語言,即表示在同一時(shí)間片內(nèi)其只能執(zhí)行單個(gè)任務(wù)或者部分代碼片。 JavaScript Event Loop 機(jī)制詳解與 Vue.js 中實(shí)踐應(yīng)用歸納于筆者的現(xiàn)代 JavaScript 開發(fā):語法基礎(chǔ)與實(shí)踐技巧系列文章。本文依次介紹了函數(shù)調(diào)用棧、MacroTask 與 Micr...

    livem 評(píng)論0 收藏0
  • JavaScript執(zhí)行機(jī)制、事件循環(huán)

    摘要:曾經(jīng)的理解首先,是單線程語言,也就意味著同一個(gè)時(shí)間只能做一件事,那么為什么不是多線程呢這樣還能提高效率啊假定同時(shí)有兩個(gè)線程,一個(gè)線程在某個(gè)節(jié)點(diǎn)上編輯了內(nèi)容,而另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器就很懵逼了,到底以執(zhí)行哪個(gè)操作呢所以,設(shè)計(jì)者把 Event Loop曾經(jīng)的理解 首先,JS是單線程語言,也就意味著同一個(gè)時(shí)間只能做一件事,那么 為什么JavaScript不是多線程呢?這樣還能提...

    rose 評(píng)論0 收藏0
  • Js 的事件循環(huán)(Event Loop)機(jī)制以及實(shí)例講解

    摘要:主線程要明確的一點(diǎn)是,主線程跟執(zhí)行棧是不同概念,主線程規(guī)定現(xiàn)在執(zhí)行執(zhí)行棧中的哪個(gè)事件。主線程循環(huán)即主線程會(huì)不停的從執(zhí)行棧中讀取事件,會(huì)執(zhí)行完所有棧中的同步代碼。以上參考資料詳解中的事件循環(huán)機(jī)制中的事件循環(huán)運(yùn)行機(jī)制詳解再談 showImg(https://segmentfault.com/img/remote/1460000015317437?w=1920&h=1080); 前言 大家都...

    Anshiii 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<