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