欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入了解Javascript的事件循環(huán)機制

 更新時間:2022年09月06日 12:00:50   作者:暢游碼生  
單線程的同步等待極大影響效率,任務(wù)不得不一個一個等待執(zhí)行,對于網(wǎng)頁應(yīng)用是無法接受的。所以Javascript使用事件循環(huán)機制來解決異步任務(wù)的問題。本文就來講講Javascript的事件循環(huán)機制,希望對你有所幫助

單線程的Javascript

JavaScript是一種單線程語言,它主要用來與用戶互動,以及操作DOM。多線程需要共享資源、且有可能修改彼此的運行結(jié)果,且存在上下文切換。

在 JS 運行的時候可能會阻止 UI 渲染,這說明兩個線程是互斥的。這是因為 JS 可以修改 DOM,如果在 JS 執(zhí)行的時候 UI 線程還在工作,就可能導(dǎo)致不能安全的渲染 UI。

JS 是單線程運行的,可以達到節(jié)省內(nèi)存,節(jié)約上下文切換時間。

為了利用多核CPU的計算能力,HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作DOM。

單線程的同步等待極大影響效率,任務(wù)不得不一個一個等待執(zhí)行,對于網(wǎng)頁應(yīng)用是無法接受的。所以Javascript使用事件循環(huán)機制來解決異步任務(wù)的問題。

同步 vs 異步 宏任務(wù) vs 微任務(wù)

首先了解下同步和異步的區(qū)別:

  • 同步:在一個函數(shù)返回的時候,調(diào)用者就能夠得到預(yù)期結(jié)果。
  • 同步任務(wù):在主線程上排隊執(zhí)行的任務(wù),只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù)。
  • 異步:在函數(shù)返回的時候,調(diào)用者還不能夠得到預(yù)期結(jié)果,而是需要在將來通過一定的手段得到。
  • 異步任務(wù):不進入主線程、而放在"任務(wù)隊列"中的任務(wù),若有多個異步任務(wù),則需排隊等待進入主線程執(zhí)行棧中被執(zhí)行。

任務(wù)隊列其實不止一種,根據(jù)任務(wù)種類的不同,可以分為微任務(wù)(micro task)隊列和宏任務(wù)(macro task)隊列。常見的任務(wù)如下:

  • 宏任務(wù):script(整體代碼)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate;需要特定的異步線程去執(zhí)行,有明確的異步任務(wù)去執(zhí)行,有回調(diào)。
  • 微任務(wù):Promise、MutaionObserver、process.nextTick(Node.js 環(huán)境,會先于其他微任務(wù)執(zhí)行);不需要特定的異步線程去執(zhí)行,沒有明確的異步任務(wù)去執(zhí)行,只有回調(diào)。

一次 Eventloop 循環(huán)會處理一個宏任務(wù)和所有這次循環(huán)中產(chǎn)生的微任務(wù)。 執(zhí)行順序如下圖:

第一個例子:

var req = new XMLHttpRequest();
req.open('GET', url);    
req.onload = function (){};    
req.onerror = function (){};    
req.send();
//等同于
var req = new XMLHttpRequest();
req.open('GET', url);
req.send();
req.onload = function (){};    
req.onerror = function (){};

上面代碼中的req.send方法是Ajax操作向服務(wù)器發(fā)送數(shù)據(jù),它是一個異步任務(wù),意味著只有當(dāng)前腳本的所有代碼執(zhí)行完,系統(tǒng)才會去讀取"任務(wù)隊列"。指定回調(diào)函數(shù)的部分(onload和onerror),在send()方法的前面或后面無關(guān)緊要,因為它們屬于執(zhí)行棧的一部分,系統(tǒng)總是執(zhí)行完它們,才會去讀取"任務(wù)隊列"。

第二個例子:

console.log('1 第一次循環(huán) 開始執(zhí)行');
setTimeout(function () {
    console.log('2 第二次循環(huán) 開始執(zhí)行');
    new Promise(function (resolve) {
        console.log('3 第二次循環(huán) 宏任務(wù)結(jié)束');
        resolve();
    }).then(function () {
        console.log('4 第二次循環(huán) 微任務(wù)執(zhí)行')
    })
}, 0)
new Promise(function (resolve) {
    console.log('5 第一次循環(huán) 宏任務(wù)結(jié)束');
    resolve();
}).then(function () {
    console.log('6 第一次循環(huán) 微任務(wù)執(zhí)行')
})

setTimeout(function () {
    console.log('7 第三次循環(huán) 開始執(zhí)行');

    new Promise(function (resolve) {
        console.log('8 第三次循環(huán) 宏任務(wù)結(jié)束');
        resolve();
    }).then(function () {
        console.log('9 第三次循環(huán) 微任務(wù)執(zhí)行')
    })
}, 0)

/*
結(jié)果
1 第一次循環(huán) 開始執(zhí)行
5 第一次循環(huán) 宏任務(wù)結(jié)束
6 第一次循環(huán) 微任務(wù)執(zhí)行
2 第二次循環(huán) 開始執(zhí)行
3 第二次循環(huán) 宏任務(wù)結(jié)束
4 第二次循環(huán) 微任務(wù)執(zhí)行
7 第三次循環(huán) 開始執(zhí)行
8 第三次循環(huán) 宏任務(wù)結(jié)束
9 第三次循環(huán) 微任務(wù)執(zhí)行
*/

定時器

定時器功能主要由setTimeout()和setInterval()這兩個函數(shù)來完成,它們的內(nèi)部運行機制完全一樣,區(qū)別在于前者指定的代碼是一次性執(zhí)行,后者則為反復(fù)執(zhí)行。

如果將setTimeout()的第二個參數(shù)設(shè)為0,就表示當(dāng)前代碼執(zhí)行完(執(zhí)行棧清空)以后,立即執(zhí)行(0毫秒間隔)指定的回調(diào)函數(shù)。主線程盡可能早得執(zhí)行,但是沒有辦法保證回調(diào)函數(shù)一定會在setTimeout()指定的時間執(zhí)行,因為必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)。所以單線程無法實現(xiàn)真正的異步,因為還是存在阻塞。

HTML5標(biāo)準(zhǔn)規(guī)定了setTimeout()的第二個參數(shù)的最小值(最短間隔),不得低于4毫秒,如果低于這個值,就會自動增加。

在此之前,老版本的瀏覽器都將最短間隔設(shè)為10毫秒。

另外,對于那些DOM的變動(尤其是涉及頁面重新渲染的部分),通常不會立即執(zhí)行,而是每16毫秒執(zhí)行一次。

這時使用requestAnimationFrame()的效果要好于setTimeout()。

在Node.js環(huán)境下,還提供了另外兩個方法:

  • process.nextTick方法可以在當(dāng)前"執(zhí)行棧"的尾部,下一次Event Loop之前,觸發(fā)回調(diào)函數(shù)。也就是說,它指定的任務(wù)總是在本次"事件循環(huán)"觸發(fā),發(fā)生在所有異步任務(wù)之前,同時也是在所有微任務(wù)之前執(zhí)行。
  • setImmediate方法則是在當(dāng)前"任務(wù)隊列"的尾部添加事件,也就是說,它指定的任務(wù)總是在之后的Event Loop執(zhí)行,這與setTimeout(fn, 0)很像。

多個process.nextTick?語句總是在當(dāng)前"執(zhí)行棧"一次執(zhí)行完,多個setImmediate則可能需要多次loop才能執(zhí)行完。

To Be Continued

Node.js使用V8作為js的解析引擎,而I/O處理方面使用了自己設(shè)計的libuv,libuv是一個基于事件驅(qū)動的跨平臺抽象層,封裝了不同操作系統(tǒng)一些底層特性,對外提供統(tǒng)一的API,事件循環(huán)機制也是它里面的實現(xiàn)的。(在Python中,uvloop,一個完整的asyncio事件循環(huán)的替代品,也是建立在libuv基礎(chǔ)之上,是由Cython編寫而成。)這個機制和瀏覽器中Javascript的事件循環(huán)機制是不太一樣的。

以上就是深入了解Javascript的事件循環(huán)機制的詳細內(nèi)容,更多關(guān)于Javascript事件循環(huán)機制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • javascript之AJAX框架使用說明

    javascript之AJAX框架使用說明

    javascript之AJAX框架使用說明,需要的朋友可以參考下.`
    2010-04-04
  • 詳解JS中如何終止foreach循環(huán)

    詳解JS中如何終止foreach循環(huán)

    forEach專門用來循環(huán)數(shù)組,可以直接取到元素,同時也可以取到index值,終止forEach存在局限性,不能continue跳過或者break終止循環(huán),所以本文給大家詳細介紹了在JS中如何終止foreach循環(huán),需要的朋友可以參考下
    2024-01-01
  • BootStrap 動態(tài)添加驗證項和取消驗證項的實現(xiàn)方法

    BootStrap 動態(tài)添加驗證項和取消驗證項的實現(xiàn)方法

    這篇文章主要介紹了BootStrap 動態(tài)添加驗證項和取消驗證項的實現(xiàn)方法的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • 微信小程序自定義組件實現(xiàn)tabs選項卡功能

    微信小程序自定義組件實現(xiàn)tabs選項卡功能

    這篇文章主要為大家詳細介紹了微信小程序自定義組件實現(xiàn)tabs選項卡功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • js Dialog 去掉右上角的X關(guān)閉功能

    js Dialog 去掉右上角的X關(guān)閉功能

    用到 dialog彈出框時,不想要右上角的X 關(guān)閉功能,只是做個提示信息顯示,下面是具體的去掉方法,大家可以參考下
    2014-04-04
  • JavaScript實現(xiàn)簡易計算器功能的兩種方法

    JavaScript實現(xiàn)簡易計算器功能的兩種方法

    這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)簡易計算器功能的兩種方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • 微信小程序中做用戶登錄與登錄態(tài)維護的實現(xiàn)詳解

    微信小程序中做用戶登錄與登錄態(tài)維護的實現(xiàn)詳解

    微信小程序的運行環(huán)境不是在瀏覽器下運行的。所以不能以cookie來維護登錄態(tài)。下面這篇文章主要給大家介紹了微信小程序中如何做用戶登錄與登錄態(tài)維護的相關(guān)資料,文中介紹的非常詳細,需要的朋友可以參考學(xué)習(xí)。
    2017-05-05
  • javascript實現(xiàn)自定義滾動條效果

    javascript實現(xiàn)自定義滾動條效果

    這篇文章主要為大家詳細介紹了javascript實現(xiàn)自定義滾動條效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 原生JS實現(xiàn)拖拽效果

    原生JS實現(xiàn)拖拽效果

    這篇文章主要為大家詳細介紹了原生JS實現(xiàn)拖拽效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • javascript實現(xiàn)簡易數(shù)碼時鐘

    javascript實現(xiàn)簡易數(shù)碼時鐘

    這篇文章主要為大家詳細介紹了javascript實現(xiàn)簡易數(shù)碼時鐘,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03

最新評論