首頁  >  文章  >  web前端  >  js引擎的執行機制詳解

js引擎的執行機制詳解

小云云
小云云原創
2018-03-31 17:11:112979瀏覽


本文主要跟大家分享js引擎的執行機制詳解,希望能幫助大家。

首先,請牢記2 點:

  • js 是單執行緒語言

  • js 中的event loop 是js 的執行機制。深入了解 js 的執行,等於深入了解 js 裡的 event loop

js 為什麼是單執行緒的?

js 最初是設計用在瀏覽器中,那麼想像一下,如果瀏覽器中的 js 是多執行緒的。

場景描述:

那麼現在有2 個進程,process1 process2,由於是多進程的js,所以他們對同一個dom,同時進行操作,process1 刪除了該dom,而process2 編輯了該dom,同時下達2 個矛盾的指令,瀏覽器究竟該如何執行呢?

這樣想,js 為什麼要設計成單一執行緒應該就容易理解了吧。

js 為什麼需要非同步?

場景描述;

如果 js 中不存在非同步,只能自上而下執行,如果上一行解析時間很長,那麼下面的程式碼就會被阻塞。對使用者而言,阻塞就意味著‘卡死’,這樣就導致了很差的用戶體驗。

所以,js 中存在非同步執行。

單執行緒又是如何實現非同步的呢?

既然JS 是單線程的,只能在一條線程上執行,又是如何實現的異步呢?

是通過的事件循環(event loop), 就理解了js 的執行機制。

js 中的event loop(1)

console.log(1);
setTimeout(function() {
  console.log(2);
}, 0);
console.log(3);// 输出 1 3 2

也就是說,setTimeout 裡的函數並沒有立即執行,而是延遲了一段時間,滿足一定條件後,才去執行的,這類程式碼,我們叫非同步程式碼。

所以,這裡我們首先知道了JS 裡的一種分類方式,就是將任務分為: 同步任務和非同步任務

按這種分類方式,js 的執行機制就是:

  • 先判斷js 是同步的還是異步的,同步的就進入主線程,非同步就進入event table

  • 異步任務在event table 中註冊函數,當滿足觸發條件後,被推入event queue

  • #同步任務進入主線程後一直執行,直到主線程空閒,才會去event queue 中查看是否有可執行的非同步任務,若果有就推入主執行緒。

以上三步驟迴圈執行,就是 event loop

所以上述例子,你是否可以描述他們的執行順序了?

console.log(1); // 同步任务,放入主线程中setTimeout(function() {
  // 异步任务,放到event table中,0秒后被推入到event queue中
  console.log(2);
}, 0);
console.log(3); // 同步任务,放到主线程中// 输出 1 3 2// 当1,3被打印后,主线程去event queue(事件队列)里查看是否有可执行的函数,执行setTimeout里面的函数。

js 中的event loop(2)

再看下一個範例:

setTimeout(function() {
  console.log("定时器开始");
}, 0);new promise(function(resolve) {
  console.log("马上执行for循环了");
}).then(function(resolve) {
  console.log("执行then函数拉");
});
console.log("代码执行结束");

用先前總結的js 執行機制去分析:

setTimeout 是异步任务,放在 event table中new promise是同步任务,被放到主线程中,直接打印console.log('马上执行for循环了');

.then里面的是异步任务,被放到event table中

console.log('代码执行结束') 是同步代码,被放在主线程中,直接执行。

所以,結果是【馬上執行for 迴圈— 程式碼執行結束— 計時器開始啦— 執行then 函數啦】嗎?

親自執行後,結果居然不是這樣,而是【馬上執行for 迴圈啦— 程式碼執行結束— 執行then 函數啦— 定時器開始啦】

那麼,難道是異步任務的執行順序,不是前後順序,而是另有規定? 事實上,按照異步和同步的分割方式,不準確

而準確的分割方式是:

  • macro-task(巨集任務): 整體程式碼script, setTimeout,setInterval

  • ##micro-task(微任務): Promise,process,nextTick

js引擎的執行機制詳解

依照這種分類方式,js 的執行機制就是:

  • 執行一個巨集任務,過程中如果遇到微任務,就將其放到微任務的event queue 中

  • 目前巨集任務執行完成後,會常看微任務的event queue ,並將裡面全部的微任務依序執行完。

重複以上 2 步驟,結合 event loop(1) event loop(2) ,就是更準確的 JS 執行機制了。

依照剛才的機制,分析例2:

// 首先执行script下的宏任务,遇到setTimeout,将其放在宏任务队列中setTimeout(function() {
  console.log("定时器开始");
}, 0);// 遇到new promise直接执行new promise(function(resolve) {
  console.log("马上执行for循环了");
}).then(function(resolve) {
  // .then方法,是微任务,放在微任务队列中
  console.log("执行then函数拉");
});
console.log("代码执行结束");// 宏任务执行完毕,查看本轮的微任务,返现有一个then方法。再去执行宏任务队列中的函数。

談談setTimeout

這段setTimeout 程式碼什麼意思? 我們一般說: 3 秒後,會執行setTimeout 裡的那個函數

setTimeout(function() {
  console.log("执行了");
}, 3000);

但是這種說並不嚴謹,準確的解釋是: 3 秒後,setTimeout 裡的函數被會推入event queue,而event queue(事件隊列)裡的任務,只有在主執行緒空閒時才會執行。

所以只有滿足(1)3 秒後(2)主執行緒空閒,同時滿足時,才會3 秒後執行該函數

如果主執行緒執行內容很多,執行時間超過3 秒,比如執行了10 秒,那麼這個函數只能10 秒後執行了

#深入理解js 引擎的執行機制

首先,請牢記2 點:

  • js 是單執行緒語言

  • #js 中的event loop 是js 的執行機制。深入了解 js 的執行,就等於深入了解 js 裡的 event loop

js 為什麼是單執行緒的?

js 最初是設計用在瀏覽器中,那麼想像一下,如果瀏覽器中的 js 是多執行緒的。

場景描述:

那么现在有 2 个进程,process1 process2,由于是多进程的 js,所以他们对同一个 dom,同时进行操作,process1 删除了该 dom,而 process2 编辑了该 dom,同时下达 2 个矛盾的命令,浏览器究竟该如何执行呢?

这样想,js 为什么被设计成单线程应该就容易理解了吧。

js 为什么需要异步?

场景描述;

如果 js 中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。对于用户而言,阻塞就意味着‘卡死’,这样就导致了很差的用户体验。

所以,js 中存在异步执行。

单线程又是如何实现异步的呢?

既然 JS 是单线程的,只能在一条线程上执行,又是如何实现的异步呢?

是通过的事件循环(event loop), 就理解了 js 的执行机制。

js 中的 event loop(1)

console.log(1);
setTimeout(function() {
  console.log(2);
}, 0);
console.log(3);// 输出 1 3 2

也就是说,setTimeout 里的函数并没有立即执行,而是延迟了一段时间,满足一定条件后,才去执行的,这类代码,我们叫异步代码。

所以,这里我们首先知道了 JS 里的一种分类方式,就是将任务分为: 同步任务和异步任务

按这种分类方式,js 的执行机制就是:

  • 首先判断 js 是同步的还是异步的,同步的就进入主线程,异步就进入 event table

  • 异步任务在 event table 中注册函数,当满足触发条件后,被推入 event queue

  • 同步任务进入主线程后一直执行,直到主线程空闲,才会去 event queue 中查看是否有可执行的异步任务,若果有就推入到主线程中。

以上三步循环执行,就是 event loop

所以上述例子,你是否可以描述他们的执行顺序了?

console.log(1); // 同步任务,放入主线程中setTimeout(function() {
  // 异步任务,放到event table中,0秒后被推入到event queue中
  console.log(2);
}, 0);
console.log(3); // 同步任务,放到主线程中// 输出 1 3 2// 当1,3被打印后,主线程去event queue(事件队列)里查看是否有可执行的函数,执行setTimeout里面的函数。

js 中的 event loop(2)

再看下一个例子:

setTimeout(function() {
  console.log("定时器开始");
}, 0);new promise(function(resolve) {
  console.log("马上执行for循环了");
}).then(function(resolve) {
  console.log("执行then函数拉");
});
console.log("代码执行结束");

用之前总结的 js 执行机制去分析:

setTimeout 是异步任务,放在 event table中new promise是同步任务,被放到主线程中,直接打印console.log('马上执行for循环了');

.then里面的是异步任务,被放到event table中

console.log('代码执行结束') 是同步代码,被放在主线程中,直接执行。

所以,结果是 【马上执行 for 循环啦 — 代码执行结束 — 定时器开始啦 — 执行 then 函数啦】吗?

亲自执行后,结果居然不是这样,而是【马上执行 for 循环啦 — 代码执行结束 — 执行 then 函数啦 — 定时器开始啦】

那么,难道是异步任务的执行顺序,不是前后顺序,而是另有规定? 事实上,按照异步和同步的划分方式,并不准确

而准确的划分方式是:

  • macro-task(宏任务): 整体代码 script, setTimeout,setInterval

  • micro-task(微任务): Promise,process,nextTick

js引擎的執行機制詳解

按照这种分类方式,js 的执行机制就是:

  • 执行一个宏任务,过程中如果遇到微任务,就将其放到微任务的 event queue 中

  • 当前宏任务执行完成后,会常看微任务的 event queue ,并将里面全部的微任务依次执行完。

重复以上 2 步骤,结合 event loop(1) event loop(2) ,就是更为准确的 JS 执行机制了。

按照刚才的机制,分析例 2:

// 首先执行script下的宏任务,遇到setTimeout,将其放在宏任务队列中setTimeout(function() {
  console.log("定时器开始");
}, 0);// 遇到new promise直接执行new promise(function(resolve) {
  console.log("马上执行for循环了");
}).then(function(resolve) {
  // .then方法,是微任务,放在微任务队列中
  console.log("执行then函数拉");
});
console.log("代码执行结束");// 宏任务执行完毕,查看本轮的微任务,返现有一个then方法。再去执行宏任务队列中的函数。

谈谈 setTimeout

这段 setTimeout 代码什么意思? 我们一般说: 3 秒后,会执行 setTimeout 里的那个函数

setTimeout(function() {
  console.log("执行了");
}, 3000);

但是这种说并不严谨,准确的解释是: 3 秒后,setTimeout 里的函数被会推入 event queue,而 event queue(事件队列)里的任务,只有在主线程空闲时才会执行。

所以只有满足 (1)3 秒后 (2)主线程空闲,同时满足时,才会 3 秒后执行该函数

如果主线程执行内容很多,执行时间超过 3 秒,比如执行了 10 秒,那么这个函数只能 10 秒后执行了

以上是js引擎的執行機制詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn