摘要:注意如果主邏輯的代碼執行時間已經超過了第二個參數設置的時間,那么等運行到該回調函數時,它會忽略掉這個時間,并立即執行。如果某一個進行大量的計算,那么它就會阻塞在當前的回調函數中,等待該計算完成后,再執行下一個的回調函數。
setTimeout()
? JavaScript是一個單線程的語言,也就是說它同一時間只能執行一段代碼,接下來我們通過兩個例子說明一下單線程語言和多線程語言的區別。
setTimeout 代碼單線程運行機制:
/** * setTimeout 執行是要等主線線程的流程執行完畢之后才會進行,并且按照setTimeout設置的順序進行排隊執行。 * 如果某一個setTimeout進行大量的計算,那么它就會阻塞在當前的setTimeout回調函數中,等待該計算完成后,再執行下一個setTimeout的回調函數。 */ setTimeout(() => { console.log("setTimeout - a"); },0); console.log(1); console.log(2); setTimeout(() => { for (let i = 0; i < 10000022200; i++){} console.log("setTimeout - b"); },0); console.log(3); setTimeout(() => { console.log("setTimeout - c"); },0); console.log(4); setTimeout(() => { console.log("setTimeout - d"); },0); console.log(5); for (let i = 0; i < 10000222200; i++) {} //一直等待它執行完畢后,才會執行setTimeout的回調。
? 從運行結果上可以看出,雖然setTimeout - a 是寫在代碼當最開頭,延時時間也為0,但是,它并沒有立即執行;而是等主邏輯的代碼執行完畢后才進行調用的,當代碼運行到25行的時候,由于這里有一個長長的循環,所以這里會阻塞等待一段時間,才會運行到第一個setTimeout。setTimeout的運行順序是根據你代碼中編寫的順序和延時時間決定的,下面通過一張圖來說明上述代碼的運行機制:
? 從運行結果中,可以看出如果主邏輯代碼沒有執行完畢,setTimeout的回調函數是永遠不會觸發的,這就是單線程,它同一時間只能做一件事。
? **注意:如果主邏輯的代碼執行時間已經超過了setTimeout第二個參數設置的timeout時間,那么等運行到該回調函數時,它會忽略掉這個時間,并立即執行。下面通過一段代碼進行驗證:
/** * setTimeout 執行是要等主線線程的流程執行完畢之后才會進行,并且按照setTimeout設置的順序進行排隊執行。 * 如果某一個setTimeout進行大量的計算,那么它就會阻塞在當前的setTimeout回調函數中,等待該計算完成后,再執行下一個setTimeout的回調函數。 * * 執行順序:即使setTimeout放在最前面執行,它也是等到主線程執行完畢后,才運行,這就是單線程運行機制。 * setTimeout中的第二個參數timeout這個延時時間,是一個相對時間,如果主線程運行的時間,已經超過了這個時間,那么執行到這個setTimeout的時候,會忽略這個時間,直接調用函數。 */ setTimeout(() => { console.log("setTimeout - a"); },0); setTimeout(() => { for (let i = 0; i < 10000022200; i++){} console.log("setTimeout - b"); },0); setTimeout(() => { console.log("setTimeout - c"); },0); setTimeout(() => { console.log("setTimeout - d"); },10000); console.log(1); console.log(2); console.log(3); console.log(4); console.log(5); for (let i = 0; i < 10000222200; i++) {} //一直等待它執行完畢后,才會執行setTimeout的回調。
從上述運行結果可以看出,即使setTimeout放在主邏輯到最前邊,但是它依然是要等到主邏輯到代碼完全執行完畢后才執行。
由于29行有大量的循環邏輯,主邏輯大概會阻塞20秒鐘左右,所以當調用到19行的setTimeout的回調函數時,會忽略掉它設置timeout參數,并立即執行該回調函數。
下面我們通過一段Java的代碼演示一下多線程,以此說明一下單線程與多線程的區別:
Java多線程代碼運行機制:
public class Main { public static void main(String[] args) { // 控制臺打印輸出 System.out.println("1"); // 啟動一個線程 new Thread(new Runnable() { @Override public void run() { System.out.println("線程1 before"); for(int i = 0; i < 2099222220L; i++) {} System.out.println("線程1 after"); } }).start(); // 控制臺打印輸出 System.out.println("2"); // 啟動一個線程 new Thread(new Runnable() { @Override public void run() { System.out.println("線程2"); } }).start(); // 控制臺打印輸出 System.out.println("3"); // 通過一個大的循環阻塞主線程一段時間,看看會不會影響線程的運行 for(int i = 0; i < 2099222220L; i++) {} // 控制臺打印輸出 System.out.println("4"); } }
從運行結果上可以看出,多線程的語言,主線程與子線程之間是完全相互獨立的,即使主線程中存在大量的計算邏輯,也不會阻塞子線程的運行;子線程之間也是相互獨立的,例如:線程1中存在大量計算邏輯并不會影響線程2的正在執行。
Java默認的子線程執行順序是由cpu隨機調度的,上述的線程1啟動后,cpu就沒有馬上調度到它。
process.nextTick()? process.nextTick() 是Node.js提供的一個異步執行函數,它不是setTimeout(fn, 0)的別名,它的效率更高,它的執行順序要早于setTimeout和setInterval,它是在主邏輯的末尾任務隊列調用之前執行。下面通過一段代碼進行驗證:
/** * 執行順序:主線程邏輯 => nextTick => setTimeout * */ console.log(1); setTimeout(() => console.log("setTimeout=> 1"),0); process.nextTick(() => console.log("nextTick=> 1")); console.log(2); setTimeout(() => console.log("setTimeout=> 2"),0); process.nextTick(() => { console.log("nextTick=> 2"); for (let i = 0; i < 10000222200; i++) {} //一直等待它執行完畢后,才會執行下一個nextTick()和之后的任務隊列中的回調函數 }); console.log(3); process.nextTick(() => console.log("nextTick=> 3")); setTimeout(() => console.log("setTimeout=> 3"),0); console.log(4); setTimeout(() => console.log("setTimeout=> 4"),0); process.nextTick(() => console.log("nextTick=> 4")); console.log(5); for (let i = 0; i < 10000222200; i++) {} //一直等待它執行完畢后,才會執行nextTick和setTimeout的回調。
從運行結果中我們可以發現,即使setTimeout設置的時機要早于process.nextTick(),但是process.nextTick()的執行時機還是要早于setTimeout,這就證明是了process.nextTick() 的執行時機是在任務隊列調用之前進行執行的。
setInterval()? setInterval() 是一個定時器函數,可按照指定的周期(以毫秒計)來不斷調用函數或計算表達式。但是由于JavaScript是一個單線程的語言,所以這個定時器的指定的周期回調時間,并不準確;下面通過一段代碼來說明一下:
/** * setInterval 也是要等待主線程執行完畢后,才會進行調用, 如果timeout時間一樣,就按照setInterval設置的順序進行執行。 * 如果有一個setInterval回調函數中有大量的計算,那么線程就阻塞在這個回調函數里,其他的setInterval也會等到這個回調執行完畢后才會調用。 */ console.log("main => 1"); setInterval(() => { console.log("setInterval=> 2 before"); for (let i = 0; i < 10022222220; i++) {} // 此處會阻塞一段時間,等待計算完畢后才會執行下一個setInterval的回調。 console.log("setInterval=> 2 after"); }, 1000); setInterval(() => { console.log("setInterval=> 1"); }, 1000); console.log("main => 2"); for (let i = 0; i < 1002222200; i++) {} // 此處主邏輯會阻塞一段時間進行循環計算,只有主邏輯代碼執行完畢后才會調用setInterval
? 上述代碼中同時啟動了兩個setInterval() 并且它們的回調周期時間都為1000毫秒, 但是從運行結果中我們可以發現這倆setInterval()的回調周期時間遠遠超出了1000毫秒;造成這種情況的主要有兩個地方:
? 第一是在代碼的第17行,這里有一個很大的循環計算,它的循環時間遠遠超過了1000毫秒,所以這倆setInterval只能等主邏輯的代碼執行完畢后才能執行。
? 第二是在代碼的第9行,在第一個setInterval里也有一個很大的循環計算,它的循環時間也超過了1000毫秒,所以第二個setInterval的回調函數也必須要等待前一個setInterval執行完畢后才能進行調用。
? 由此我們可以得出一個結論:setInterval()的執行時機是在主邏輯執行完畢之后,它的執行順序是根據回調周期的時間和設置的順序進行調用,同一時間只會執行一個setInterval的回調函數,只有等待上一個setInterval回調函數執行完畢后,才能執行下一個setInterval的回調函數。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102613.html
摘要:事件完成,回調函數進入。我們來分析一段較復雜的代碼,看看你是否真的掌握了的執行機制第一輪事件循環流程分析如下整體作為第一個宏任務進入主線程,遇到,輸出。宏任務微任務第三輪事件循環宏任務執行結束,執行兩個微任務和。 關于JavaScript 首先js是單線程的,執行任務肯定是一個接著一個。在最新的html5中提出了web-worker,但是JavaScript是單線程這一核心沒有改變,一...
摘要:下面開始分析開頭的代碼第一輪事件循環流程整體作為第一個宏任務進入主線程,遇到,輸出遇到函數聲明,聲明暫時不用管遇到,其回調函數被分發到微任務中。我們記為遇到,其回調函數被分發到宏任務中。 先上一道常見的筆試題 console.log(1); async function async1() { console.log(2); await async2(); con...
摘要:下面開始分析開頭的代碼第一輪事件循環流程整體作為第一個宏任務進入主線程,遇到,輸出遇到函數聲明,聲明暫時不用管遇到,其回調函數被分發到微任務中。我們記為遇到,其回調函數被分發到宏任務中。 先上一道常見的筆試題 console.log(1); async function async1() { console.log(2); await async2(); con...
摘要:關于這部分有嚴格的文字定義,但本文的目的是用最小的學習成本徹底弄懂執行機制,所以同步和異步任務分別進入不同的執行場所,同步的進入主線程,異步的進入并注冊函數。宏任務微任務第三輪事件循環宏任務執行結束,執行兩個微任務和。 不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發工作,我們經常會遇到這樣的情況:給定的幾行代碼,我們需要知道其輸出內容和順序。 因為javascr...
摘要:徹底搞懂執行機制首先我們大家都了解的是,是一門單線程語言,所以我們就可以得出是按照語句順序執行的首先看這個顯然大家都知道結果,依次輸出,然而換一種這個時候再看代碼的順序執行,輸出,,,。不過即使主線程為空,也是達不到的,根據標準,最低是。 徹底搞懂JavaScript執行機制 首先我們大家都了解的是,JavaScript 是一門單線程語言,所以我們就可以得出: JavaScript 是...
閱讀 2528·2021-10-12 10:12
閱讀 1707·2019-08-30 15:52
閱讀 2443·2019-08-30 13:04
閱讀 1734·2019-08-29 18:33
閱讀 961·2019-08-29 16:28
閱讀 451·2019-08-29 12:33
閱讀 2057·2019-08-26 13:33
閱讀 2360·2019-08-26 11:36