偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

這次徹底了解JavaScript執(zhí)行機(jī)制

開(kāi)發(fā) 前端
這篇文章的目的是為了讓你徹底理解 JavaScript 的執(zhí)行,如果你到本文最后還沒(méi)有理解,你可以揍我一頓。

無(wú)論你是 JavaScript 新手還是老手,無(wú)論你是在面試工作,還是只是做常規(guī)的開(kāi)發(fā)工作,通常會(huì)發(fā)現(xiàn)給定幾行代碼,你需要知道要輸出什么以及以什么順序輸出 . 由于 JavaScript 是一種單線程語(yǔ)言,我們可以得出以下結(jié)論:

let a = '1';
console.log(a);
let b = '2';
console.log(b);

然而,JavaScript 實(shí)際上是這樣的:

setTimeout(function(){
console.log('start')
});
new Promise(function(resolve){
console.log('start for');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('start then')
});
console.log('end');
// Following the idea that JS executes in the order in which the statements appear, I confidently write down the output:
// start
// start for
// start then
// end

在 Chrome 上查看它是完全錯(cuò)誤的??

關(guān)于 JavaScript

JavaScript 是一種單線程語(yǔ)言。Web-worker 是在最新的 HTML5 中提出的,但 JavaScript 是單線程的核心保持不變。所以所有 JavaScript 版本的“多線程”都是用單線程模擬的,所有的 JavaScript 多線程都是紙老虎!

、JavaScript 事件循環(huán)

由于 JavaScript 是單線程的,它就像一個(gè)只有一個(gè)窗口的銀行??蛻粜枰灰慌抨?duì)辦理業(yè)務(wù)。

同樣,JavaScript 任務(wù)也需要一個(gè)一個(gè)地執(zhí)行。如果一項(xiàng)任務(wù)花費(fèi)的時(shí)間太長(zhǎng),則下一項(xiàng)也必須等待。

那么問(wèn)題來(lái)了,如果我們想瀏覽新聞,但新聞中包含加載緩慢的超高清圖像,我們的網(wǎng)頁(yè)是否應(yīng)該一直卡住直到圖像完全顯示?所以聰明的程序員將任務(wù)分為兩類:

  • 同步任務(wù)
  • 異步任務(wù)

當(dāng)我們打開(kāi)一個(gè)網(wǎng)站時(shí),頁(yè)面的渲染過(guò)程是很多同步任務(wù),比如渲染頁(yè)面骨架和頁(yè)面元素。

需要大量時(shí)間的任務(wù),比如加載圖片和音樂(lè),都是異步任務(wù)。這部分有嚴(yán)格的文字定義,但本文的目的是以最小的學(xué)習(xí)成本徹底理解實(shí)現(xiàn)機(jī)制,所以我們用一張圖來(lái)說(shuō)明:

文字要表達(dá)的內(nèi)容:

同步和異步任務(wù)去不同的執(zhí)行“地方”,同步任務(wù)去主線程,異步任務(wù)去事件表和注冊(cè)函數(shù)。

當(dāng)指定的事件完成時(shí),事件表將此函數(shù)移至事件隊(duì)列。

如果執(zhí)行后主線程中的任務(wù)為空,事件隊(duì)列會(huì)讀取相應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行。

這個(gè)過(guò)程一遍又一遍地重復(fù),稱為事件循環(huán)。

我們?cè)趺粗乐骶€程棧是空的?JavaScript 引擎有一個(gè)監(jiān)控進(jìn)程,不斷檢查主線程堆棧是否為空,如果是,則檢查 Event Queue 以查看是否有任何函數(shù)等待調(diào)用。

說(shuō)了這么多,不如直接寫一段代碼:

let data = [];
$.ajax({
url:www.javascript.com,
data:data,
success:() => {
console.log('success!');
}
})
console.log('end');

這是一個(gè)簡(jiǎn)單的ajax請(qǐng)求代碼:

  • ajax 去事件表并注冊(cè)回調(diào)函數(shù)成功。
  • 執(zhí)行 console.log(‘success’)。
  • ajax Event 完成,回調(diào)函數(shù)success 進(jìn)入Event Queue。
  • 主線程從事件隊(duì)列中讀取成功并執(zhí)行。

相信通過(guò)上面的文字和代碼,你對(duì)JS的執(zhí)行順序有了初步的了解。接下來(lái),我們來(lái)看看進(jìn)階話題:setTimeout。

、 愛(ài)恨交加超時(shí)

著名的 setTimeout 無(wú)需進(jìn)一步解釋。setTimeout 的第一印象是異步執(zhí)行可以延遲,我們經(jīng)常這樣實(shí)現(xiàn):

setTimeout(() => {
console.log(‘Delay 3 seconds’);
},3000)

當(dāng) setTimeout 用得越來(lái)越多時(shí),問(wèn)題也出現(xiàn)了。有時(shí)函數(shù)會(huì)在 3 秒的書(shū)面延遲后 5 或 6 秒內(nèi)執(zhí)行。怎么了?

讓我們從一個(gè)例子開(kāi)始:

setTimeout(() => {
task();
},3000)
console.log('console');

按照我們之前的結(jié)論,setTimeout是異步的,應(yīng)該先執(zhí)行console.log。

//console
//task()

去看看吧!這是正確的!然后我們修改之前的代碼:

setTimeout(() => {
task()
},3000)
sleep(10000000)

控制臺(tái)上的 task() 在 Chrome 中執(zhí)行需要超過(guò) 3 秒的時(shí)間。

此時(shí),我們需要重新思考setTimeout的定義。

先說(shuō)上面的代碼是如何執(zhí)行的:

  • task() 進(jìn)入事件表并注冊(cè),定時(shí)器啟動(dòng)。
  • 執(zhí)行sleep,非常慢,非常慢,計(jì)時(shí)繼續(xù)。
  • task()進(jìn)入Event Queue,但是,sleep太慢無(wú)法執(zhí)行。
  • sleep終于結(jié)束了,task()終于從Event Queue執(zhí)行到主線程。

上述過(guò)程完成后,我們知道setTimeout是一個(gè)在指定時(shí)間后將任務(wù)添加到Event Queue(本例中為task())的函數(shù)。

而且,由于是單線程任務(wù),需要一個(gè)一個(gè)執(zhí)行,如果上一個(gè)任務(wù)耗時(shí)過(guò)長(zhǎng),我們只能等待。導(dǎo)致實(shí)際延遲超過(guò) 3 秒。

SetTimeout(fn,0) 是我們經(jīng)常遇到的另一個(gè)代碼??梢粤⒓赐瓿蓡幔?

SetTimeout (fn,0) 指定任務(wù)將在主線程上最早可用的空閑時(shí)間執(zhí)行。這意味著一旦堆棧中的所有同步任務(wù)完成并且堆棧為空,主線程將立即執(zhí)行。例如:

//code1
console.log('one');
setTimeout(() => {
console.log('two')
},0);
// result
// one
// two
//code2
console.log('one');
setTimeout(() => {
console.log('two')
},3000);
// result
// one
// ... 3s later
// two

關(guān)于 setTimeout 要補(bǔ)充的一點(diǎn)是,即使主線程是空的,0 毫秒實(shí)際上也是無(wú)法到達(dá)的。根據(jù) HTML 標(biāo)準(zhǔn),最小值為 4 毫秒。有興趣的同學(xué)可以自行了解。

、 恨與愛(ài)setInterval

說(shuō)了 setTimeout,你不能錯(cuò)過(guò)它的孿生兄弟 setInterval。它們是相似的,只是后者是循環(huán)執(zhí)行。對(duì)于執(zhí)行順序,setInterval 將按指定的時(shí)間間隔將注冊(cè)的函數(shù)放入事件隊(duì)列中。如果上一個(gè)任務(wù)耗時(shí)過(guò)長(zhǎng),也需要等待。

唯一需要注意的是,對(duì)于 setInterval(fn,ms),我們已經(jīng)知道不是每 ms 秒執(zhí)行一次 fn,而是每 ms 秒進(jìn)入 Event Queue。一旦 setInterval 的回調(diào) fn 花費(fèi)的時(shí)間超過(guò)了延遲 ms,時(shí)間間隔就完全不可見(jiàn)了。請(qǐng)讀者細(xì)細(xì)品味這句話。

、 Promise 和 process.nextTick(callback)

我們已經(jīng)看過(guò)傳統(tǒng)的計(jì)時(shí)器,然后,我們將探討 Promise 與 process.Nexttick(回調(diào))的性能。

Promise 的定義和功能這里就不介紹了,process.nexttick(回調(diào))類似于node.js 版本的“setTimeout”,在事件循環(huán)的下一次迭代中調(diào)用回調(diào)函數(shù)。

我們開(kāi)始談?wù)掳?。除了廣義的同步和異步任務(wù),我們對(duì)任務(wù)有更詳細(xì)的定義:

  • 宏任務(wù):包括整個(gè)代碼腳本、setTimeout 和 setInterval
  •  微任務(wù):Promise、process.nexttick

不同類型的任務(wù)會(huì)進(jìn)入對(duì)應(yīng)的Event Queue。例如,setTimeout 和 setInterval 將進(jìn)入同一個(gè)事件隊(duì)列。

事件循環(huán)的順序決定了 JS 代碼的執(zhí)行順序。輸入整體代碼(宏任務(wù))后,第一個(gè)循環(huán)開(kāi)始。然后,執(zhí)行所有微任務(wù)。然后再?gòu)暮耆蝿?wù)開(kāi)始,找一個(gè)任務(wù)隊(duì)列完成,然后,執(zhí)行所有的微任務(wù)。如果聽(tīng)起來(lái)有點(diǎn)繞,我們用本文開(kāi)頭的代碼來(lái)說(shuō)明:

setTimeout(function() {
console.log('setTimeout');
})
new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then');
})
console.log('console');

  • 此代碼作為宏任務(wù)進(jìn)入主線程。
  • 當(dāng)遇到 setTimeout 時(shí),將其回調(diào)函數(shù)注冊(cè)并分發(fā)到宏任務(wù) Event Queue。(注冊(cè)過(guò)程同上,下面不再贅述)。
  • 然后,遇到一個(gè) Promise,立即執(zhí)行 New Promise,然后將 then 函數(shù)分派到微任務(wù)事件隊(duì)列中。如果遇到console.log(),立即執(zhí)行。
  • 好的,整個(gè)腳本作為第一個(gè)宏任務(wù)執(zhí)行。什么是微任務(wù)?我們發(fā)現(xiàn) then 是在 microtask Event Queue 中執(zhí)行的。
  • 好了,第一輪的 Event loop 已經(jīng)結(jié)束了,讓我們開(kāi)始第二輪,當(dāng)然是從宏任務(wù) Event Queue 開(kāi)始。我們?cè)诤耆蝿?wù)Event Queue中找到setTimeout對(duì)應(yīng)的回調(diào)函數(shù),立即執(zhí)行。
  •  結(jié)束。

事件循環(huán)、宏任務(wù)和微任務(wù)的關(guān)系如下圖所示:

讓我們看一些更復(fù)雜的代碼,看看你是否真的了解 JS 的工作原理:

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')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})

第一輪事件循環(huán)流程分析如下:

整個(gè)腳本作為第一個(gè)宏任務(wù)進(jìn)入主線程,遇到console.log,打印1。

  • 當(dāng)遇到 setTimeout 時(shí),它的回調(diào)函數(shù)被調(diào)度到宏任務(wù)事件隊(duì)列。我們稱之為 setTimeout1。
  • 當(dāng)遇到 process.nexttick() 時(shí),將其回調(diào)函數(shù)調(diào)度到微任務(wù)事件隊(duì)列中。我們稱它為 process1。
  • 如果遇到 Promise,直接執(zhí)行新的 Promise,打印 7,然后,分發(fā)到微任務(wù) Event Queue,讓我們稱之為then1。
  • 再次遇到setTimeout,它的回調(diào)函數(shù)被分發(fā)到宏任務(wù)Event Queue中,我們稱之為setTimeout2。

  • 上表展示了第一輪Event loop的宏任務(wù)結(jié)束時(shí)各個(gè)Event Queue的情況。這時(shí)候已經(jīng)輸出了1和7。
  • 我們找到了兩個(gè)微任務(wù) process1 和 then1。
  • 執(zhí)行process1,輸出6。
  • 執(zhí)行 then1 print 8。

好了,第一輪事件循環(huán)正式結(jié)束,本輪結(jié)果輸出1,7,6,8。所以第二個(gè)時(shí)間循環(huán)從 setTimeout1 宏任務(wù)開(kāi)始:

首先,print2。接下來(lái)是process.nexttick(),它也被分派到微任務(wù)事件隊(duì)列中,稱為process2。新的 Promise 立即執(zhí)行輸出 4,然后也被分發(fā)到微任務(wù)事件隊(duì)列中,記為 then2。

  • 第二輪事件循環(huán)宏任務(wù)完成,我們發(fā)現(xiàn) process2 和 then2 微任務(wù)可以執(zhí)行。
  •  3的輸出。
  • 5的輸出。
  • 第二個(gè)事件循環(huán)結(jié)束,第二輪輸出2,4,3,5。
  • 第三個(gè)事件循環(huán)開(kāi)始,此時(shí)只剩下setTimeout2,執(zhí)行。
  • 所以它只print 9。
  • 將 process.nexttick() 分發(fā)到微任務(wù)事件隊(duì)列。記得process 3。
  • 只需執(zhí)行 new Promise,print 11。
  • then 分配 microtask Event Queue,記為 then3。

  • 第三輪事件循環(huán)宏任務(wù)執(zhí)行完成,執(zhí)行兩個(gè)微任務(wù)process3和then3。
  • 10 的輸出。
  • 12的輸出。
  • 第三輪事件循環(huán)結(jié)束。第三輪輸出9,11,10,12。

整個(gè)代碼,一共經(jīng)過(guò)了3次事件循環(huán),完整的輸出為1,7,6,8,2,4,3,5,9,11,10,12。

六、寫在最后

1、異步 JavaScript

我們從一開(kāi)始就說(shuō)過(guò) JavaScript 是單線程語(yǔ)言,無(wú)論什么新的框架和語(yǔ)法實(shí)現(xiàn)被稱為異步,實(shí)際上都是以同步的方式模擬的,所以牢牢掌握單線程很重要。

2、事件循環(huán)

事件循環(huán)是實(shí)現(xiàn)異步 JavaScript 的一種方法,也是 JavaScript 的執(zhí)行機(jī)制。

3、JavaScript的執(zhí)行和運(yùn)行

在 Node.js、瀏覽器、Ringo 等不同的環(huán)境中執(zhí)行和運(yùn)行 JavaScript 是有很大區(qū)別的。雖然運(yùn)行多指 JavaScript 解析引擎,但它是統(tǒng)一的。

4、立即設(shè)置

還有許多其他類型的微任務(wù)和宏任務(wù),例如 setImmediate,它們不進(jìn)行中介。

5、在一天結(jié)束時(shí)

JavaScript 是一種單線程語(yǔ)言。事件循環(huán)是 JavaScript 的執(zhí)行機(jī)制。

牢牢把握兩個(gè)基本點(diǎn),以認(rèn)真學(xué)習(xí)JavaScript為中心,早日實(shí)現(xiàn)成為前端高手的偉大夢(mèng)想!??

責(zé)任編輯:龐桂玉 來(lái)源: WEB前端開(kāi)發(fā)社區(qū)
相關(guān)推薦

2024-06-21 08:32:24

2018-08-07 14:45:52

編程語(yǔ)言JavaScripthtml

2025-03-17 00:21:00

2019-06-26 06:31:56

緩沖緩沖池查詢數(shù)據(jù)

2019-06-24 05:05:40

緩沖池查詢數(shù)據(jù)InnoDB

2019-06-26 09:41:44

分布式事務(wù)微服務(wù)

2021-09-14 07:06:12

Android磁盤緩存

2020-10-23 10:10:59

Promise前端代碼

2023-04-26 01:17:16

惡意注冊(cè)Java驗(yàn)證

2020-10-26 07:02:11

ConcurrentH存儲(chǔ)

2020-09-29 06:44:28

Redis延時(shí)隊(duì)列

2020-06-05 18:57:41

BiLSTMCRF算法

2023-10-27 08:15:45

2012-06-12 09:21:53

JavaScript

2021-11-02 06:58:55

FlinkWindow機(jī)制

2020-07-08 08:07:23

高并發(fā)系統(tǒng)消息隊(duì)列

2019-11-06 09:52:01

JavaScript單線程非阻塞

2020-10-14 09:11:44

IO 多路復(fù)用實(shí)現(xiàn)機(jī)

2019-02-25 09:20:53

2018-10-22 08:14:04

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)