亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript事件循環(huán)剖析宏任務(wù)與微任務(wù)

 更新時間:2022年10月18日 09:29:48   作者:十七喜歡前端  
這篇文章主要為大家介紹了JavaScript事件循環(huán)剖析宏任務(wù)與微任務(wù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

相信對于剛學(xué)習(xí)JavaScript的新手來說,去理解JS中的事件循環(huán)原理以及異步執(zhí)行過程比較困難,但是這是JS必須要會的基礎(chǔ)知識,逃避不能解決問題,筆者曾經(jīng)也被這個知識點困擾過,現(xiàn)根據(jù)以往的經(jīng)驗編寫此文章,旨在幫助大家徹底搞懂它們以及自我鞏固,話不多說,進入正題。

注意:本篇文章主要是基于瀏覽器環(huán)境,Node環(huán)境沒有研究過暫不討論

引言

我們先來小試牛刀,看看下面這段代碼是怎么執(zhí)行的,例1:

  setTimeout(() => {
    console.log('time')
  });
  new Promise((resolve, reject) => {
    console.log('p1');
    resolve();
  }).then(() => {
    console.log('res')
  });
  console.log(1);
 // 輸出: p1 1 res time

怎么樣?你想的輸出結(jié)果和實際的輸出結(jié)果是一樣的嗎?如果是一樣的說明你對事件循環(huán)有一定的了解,但是你真的已經(jīng)清楚的知道了事件循環(huán)的原理嗎?讓我們繼續(xù)往下看。

為什么會有事件循環(huán)?

JS是單線程的

眾所周知:JavaScript 是一門單線程語言,也就是說,同一個時間只能做一件事。這是因為 Javascript 這門腳 本語言誕生的使命所致——JavaScript 是為處理頁面中用戶的交互,以及操作 DOM 而誕生的。比如我們對 某個 DOM 元素進行添加和刪除操作,不能同時進行。 應(yīng)該先進行添加,之后再刪除。

單線程就意味著,所有任務(wù)需要排隊,前一個任務(wù)結(jié)束,才會執(zhí)行后一個任務(wù)。這樣所導(dǎo)致的問題是: 如果 JS 執(zhí)行的時間過長,這樣就會造成頁面的渲染不連貫,導(dǎo)致頁面渲染加載阻塞的感覺

為了解決這個問題,利用多核 CPU 的計算能力,HTML5 提出 Web Worker 標準,允許JavaScript 腳本創(chuàng)建多個線程,但是子線程完全受主線程控制。于是,JS 中出現(xiàn)了同步任務(wù)和異步任務(wù)。

同步任務(wù)和異步任務(wù) 

  • 同步任務(wù):

同步任務(wù)都在主線程上執(zhí)行,形成一個執(zhí)行棧。在主線程上排隊執(zhí)行的任務(wù),只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù);

  • 異步任務(wù):

不進入主線程、而進入”任務(wù)隊列”的任務(wù),當主線程中的任務(wù)運行完了,才會從”任務(wù)隊列”取出異步任務(wù)放入主線程執(zhí)行。JS 的異步是通過回調(diào)函數(shù)實現(xiàn)的。異步任務(wù)相關(guān)回調(diào)函數(shù)添加到任務(wù)隊列中(任務(wù)隊列也稱為消息隊列)

注意:異步任務(wù)執(zhí)行機制在這里描述的比較籠統(tǒng),主要方便大家理解,具體細節(jié)在后面的“宏任務(wù)與微任務(wù)”中會詳細介紹

JS的事件循環(huán)就是基于同步任務(wù)與異步任務(wù)來展開的,讓我們繼續(xù)往下看:

JS事件循環(huán)

事件循環(huán)是JavaScript實現(xiàn)異步的一種方法,也是JavaScript的執(zhí)行機制

如圖:

當一個腳本第一次執(zhí)行的時候,js引擎會解析這段代碼,并將其中的同步代碼按照執(zhí)行順序加入執(zhí)行棧中,然后從頭開始執(zhí)行。

當遇到異步任務(wù)時不會一直等待事件的返回結(jié)果,而是將事件掛起(即交給其他線程處理,上圖是指Web Worker),繼續(xù)執(zhí)行執(zhí)行棧中的其他任務(wù)。

當異步事件返回結(jié)果時,js將異步事件callback函數(shù)放入隊列中,被放入隊列中的異步事件不會立即回調(diào),等到當前執(zhí)行棧中的任務(wù)都執(zhí)行完成,處于閑置狀態(tài)的主線程按照隊列順序?qū)⑻幱谑孜皇录腸allback函數(shù)放入執(zhí)行棧中,執(zhí)行該函數(shù)的同步代碼,如果遇到了異步事件,同樣也會將其回調(diào)函數(shù)放入事件隊列中…

如此反復(fù),就形成了一個循環(huán),這也是被稱為“事件循環(huán)(EventLoop)”的原因。

js事件循環(huán)的基本原理已經(jīng)描述清楚,但是異步任務(wù)之間也有所不同:

任務(wù)隊列實際上分為兩個:宏任務(wù)隊列和微任務(wù)隊列。上圖只表示了一個是為了便于大家理解事件循環(huán),下面就是事件循環(huán)更細節(jié)的東西了

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

上面講到,js在執(zhí)行異步任務(wù)時,回調(diào)函數(shù)會被放在js的任務(wù)隊列中,實際上,回調(diào)函數(shù)的類別不同,執(zhí)行的優(yōu)先級也不同。

不同的優(yōu)先級被分為兩類,一類是宏任務(wù)(Micro task),一類是微任務(wù)(Macro task)。

回調(diào)函數(shù)是微任務(wù)時,會被放在微任務(wù)隊列,回調(diào)函數(shù)是宏任務(wù)時,會被放在宏任務(wù)隊列。

微任務(wù)的優(yōu)先級高于宏任務(wù),當主線程的任務(wù)執(zhí)行完成時,會首先去執(zhí)行微任務(wù)隊列中首位的回調(diào)函數(shù),當微任務(wù)隊列中為空時,才回去執(zhí)行宏任務(wù)隊列中的回調(diào)函數(shù)。

常見的宏任務(wù)有哪些?

  • 包括整體代碼 script
  • setTimeout()
  • setInterval()
  • setImmediate()(Node獨有)
  • I/O
  • UI 交互事件(瀏覽器獨有)
  • requestAnimationFrame() (瀏覽器獨有)

常見的微任務(wù)有哪些?

  • Promise.then(); Promise.cath()
  • async/await
  • process.nextTick() (Node獨有)
  • MutationObserver() (H5新增,監(jiān)聽DOM樹變化)
  • Object.observe() (異步監(jiān)視對象修改,已廢棄)

注意:new Promise()屬于同步任務(wù),但是Promise.then(); Promise.cath()屬于異步任務(wù)的微任務(wù)

執(zhí)行過程總結(jié)(重點)

現(xiàn)在我們對事件循環(huán)有了深入了解了,但是它們的執(zhí)行過程還不是很清晰,我們再把執(zhí)行過程弄清楚了以后就能游刃有余了。

同步任務(wù) —> 微任務(wù) —> 宏任務(wù)...

  • 先執(zhí)行所有同步任務(wù),碰到異步任務(wù)放到任務(wù)隊列中
  • 同步任務(wù)執(zhí)行完畢,開始執(zhí)行當前所有的異步任務(wù)
  • 先執(zhí)行任務(wù)隊列里面所有的微任務(wù),如果執(zhí)行過程中又產(chǎn)生了微任務(wù)也會在本次執(zhí)行過程中執(zhí)行(即在下一個宏任務(wù)執(zhí)行之前執(zhí)行,可以看看案例1)
  • 然后執(zhí)行一個宏任務(wù)(從宏任務(wù)隊列頭部pop出一個宏任務(wù)進執(zhí)行棧,該任務(wù)中的具體代碼也如步驟1執(zhí)行)
  • 然后再執(zhí)行所有的微任務(wù)(此時的微任務(wù)一般為步驟4中產(chǎn)生出的微任務(wù))
  • 再執(zhí)行一個宏任務(wù),再執(zhí)行所有的微任務(wù)·······依次類推到執(zhí)行結(jié)束。

3-6的這個循環(huán)稱為事件循環(huán)Event Loop

案例挑戰(zhàn)

學(xué)會了嗎?讓我們來做幾個案例鞏固一下吧

案例1:

const promise = new Promise((resolve, reject) => {
    resolve("10")
  }).then(res => {
    console.log("res1:", res)    //res1: hahaha
    return 9
  }).then(res => {
    console.log("res2:", res)    //res2: 9
    return 8
  }).then(res => {
    console.log("res3:", res)    //res3: 8
    let promise2=new Promise((resolve,reject)=>{
        resolve("p2")
      }).then(res=>{
        console.log(res)
        setTimeout(function(){
          console.log("setTimeout2")
        },0)
      })
  })
  console.log('aaa')
  setTimeout(function(){
    console.log("setTimeout1")
  },0)
  const promise1 = new Promise((resolve, reject) => {
    console.log("p1")
    resolve(989)
}).then(res => {
    console.log(res)
    return 990
}).then(res=>{
  console.log(res)
  return 991
}).then(res=>{
  console.log(res)
  return 0
})
/*輸出結(jié)果:
aaa
p1
res1: 10
989
res2: 9
990
res3: 8
991
p2
setTimeout1
setTimeout2
*/

案例2:

console.log('1');
// 定義注解 setTimeout_1 用于下文使用方便
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
// setTimeout_2
setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
// 輸出結(jié)果:  1 7 6 8 2 4 3 5 9 11 10 12

案例3:

console.log('1');    
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
// 輸出結(jié)果:1 7 6 8 2 4 3 5 9 11 10 12

以上就是JavaScript事件循環(huán)剖析宏任務(wù)與微任務(wù)的詳細內(nèi)容,更多關(guān)于JavaScript 事件循環(huán)宏任務(wù)微任務(wù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論