js执行机制
在寫js代碼的時候我們往往都希望js代碼的執(zhí)行順序是按照自己所想那樣執(zhí)行,但結(jié)果總事與愿違。所以今天就想花時間搞懂js的事件執(zhí)行機制到底是什么樣的。
EventLoop事件循環(huán)機制
js中事件分為宏任務(wù)和微任務(wù)兩類,任務(wù)又分為同步任務(wù)和異步任務(wù)。他們之間的執(zhí)行順序?qū)⒁来芜M行說明
js中事件的同步異步的執(zhí)行順序
js中事件分為兩類
- 同步事件
- 異步事件
- js執(zhí)行順序:先執(zhí)行同步再執(zhí)行異步
那么等到什么時候才能執(zhí)行呢?就是等到主線程的任務(wù)執(zhí)行完畢為空的時候。那我們怎么知道什么時候主線程為空呢?js引擎存在monitoring process進程,會持續(xù)不斷的檢查主線程執(zhí)行棧是否為空,一旦為空,就會去Event Queue那里檢查是否有等待被調(diào)用的函數(shù)。 用流程圖來概括上述步驟:
- 代碼示例
js中事件的宏任務(wù)與微任務(wù)的執(zhí)行順序
js中任務(wù)分為兩類
- 宏任務(wù):包括整體代碼script,setTimeout,setInterval
- 微任務(wù):Promise.then(非new Promise),process.nextTick(node中)
- js執(zhí)行順序:先執(zhí)行宏任務(wù)再執(zhí)行微任務(wù)
用流程圖表示上述流程:
- 代碼示例
現(xiàn)在看一個比較復雜的例子
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')}) }) 輸出: 1 2 4 3 5 復制代碼js中的同步和異步
大家都知道js是是一門單線程語言,所以他的語句肯定是一句一句執(zhí)行的。但是js中又存在同步操作和異步操作,那么就會有疑問,js是如何通過單線程的方式來實現(xiàn)異步操作的呢?
什么是同步操作
當函數(shù)執(zhí)行的時候,按照函數(shù)內(nèi)部的順序依次執(zhí)行,比如:如果此調(diào)用的函數(shù)是很耗時的,但它依然會等待調(diào)用函數(shù)的返回值,直到拿到預期的結(jié)果(即拿到了預期的返回值或者看到了預期的效果)為止才會執(zhí)行后面的操作,那么這個函數(shù)就是同步的。
//在函數(shù)返回時,獲得了預期值,即2的平方根 Math.sqrt(2); //在函數(shù)返回時,獲得了預期的效果,即在控制臺上打印了'hello' console.log('hello');復制代碼什么是異步操作
如果函數(shù)是異步的,發(fā)出調(diào)用之后,馬上返回,但是不會馬上返回預期結(jié)果。調(diào)用者不必主動等待,當被調(diào)用者得到結(jié)果之后會通過回調(diào)函數(shù)主動通知調(diào)用者。
//讀取文件 fs.readFile('hello.txt', 'utf8', function(err, data) {console.log(data); }); //網(wǎng)絡(luò)請求 var xhr = new XMLHttpRequest(); xhr.onreadystatechange = xxx; // 添加回調(diào)函數(shù) xhr.open('GET', url); xhr.send(); // 發(fā)起函數(shù) 復制代碼上述示例中讀取文件函數(shù) readFile和網(wǎng)絡(luò)請求的發(fā)起函數(shù) send都將執(zhí)行耗時操作,雖然函數(shù)會立即返回,但是不能立刻獲取預期的結(jié)果,因為耗時操作交給其他線程執(zhí)行,暫時獲取不到預期結(jié)果(后面介紹)。而在JavaScript中通過回調(diào)函數(shù) function(err, data) { console.log(data); }和 onreadystatechange ,在耗時操作執(zhí)行完成后把相應(yīng)的結(jié)果信息傳遞給回調(diào)函數(shù),通知執(zhí)行JavaScript代碼的線程執(zhí)行回調(diào)。
瀏覽器
前面說到j(luò)s是一門單線程語言,他是怎么實現(xiàn)異步操作的呢。JS的運行通常是在瀏覽器中進行的,具體由JS引擎去解析和運行。下面我們來具體了解一下瀏覽器。 瀏覽器的內(nèi)核是多線程的。 一個瀏覽器通常由以下幾個常駐的線程:
- 渲染引擎線程:顧名思義,該線程負責頁面的渲染
- JS引擎線程:負責JS的解析和執(zhí)行
- 定時觸發(fā)器線程:處理定時事件,比如setTimeout, setInterval
- 事件觸發(fā)線程:處理DOM事件
- 異步http請求線程:處理http請求
需要注意的是,渲染線程和JS引擎線程是不能同時進行的。 渲染線程在執(zhí)行任務(wù)的時候,JS引擎線程會被掛起。因為JS可以操作DOM,若在渲染中JS處理了DOM,瀏覽器可能就不知所措了。
JS引擎可以說是JS虛擬機,負責JS代碼的解析和執(zhí)行。之所以說JavaScript是單線程,就是因為瀏覽器在運行時只開啟了一個JS引擎線程來解析和執(zhí)行JS。那為什么只有一個引擎呢?如果同時有兩個線程去操作DOM,瀏覽器是不是又要不知所措了。 所以,雖然JavaScript是單線程的,可是瀏覽器內(nèi)部不是單線程的。一些I/O操作、定時器的計時和事件監(jiān)聽(click, keydown...)等都是由瀏覽器提供的其他線程來完成的。也就是之前說的Event Queue。
參考文章
JavaScript異步機制詳解
這一次,徹底弄懂 JavaScript 執(zhí)行機制
簡單總結(jié)下JS中EventLoop事件循環(huán)機制
總結(jié)
- 上一篇: 程序员写了段代码,自称完美! 网友: 我
- 下一篇: [转] 前端异常监控解决方案研究