日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

for循环里面有异步操作_JS 线程与异步的那些事

發(fā)布時間:2024/8/23 javascript 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 for循环里面有异步操作_JS 线程与异步的那些事 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

已知,JavaScript 是單線程的,天生異步,適合 IO 密集型,不適合 CPU 密集型,但是,為什么是異步的喃,異步由何而來的喃,我們將在這里逐漸討論實現(xiàn)。

一、進程與線程

1. 瀏覽器是多進程的

它主要包括以下進程:

  • Browser 進程:瀏覽器的主進程,唯一,負責創(chuàng)建和銷毀其它進程、網(wǎng)絡資源的下載與管理、瀏覽器界面的展示、前進后退等。

  • GPU 進程:用于 3D 繪制等,最多一個。

  • 第三方插件進程:每種類型的插件對應一個進程,僅當使用該插件時才創(chuàng)建。

  • 瀏覽器渲染進程(瀏覽器內(nèi)核):內(nèi)部是多線程的,每打開一個新網(wǎng)頁就會創(chuàng)建一個進程,主要用于頁面渲染,腳本執(zhí)行,事件處理等。

2. 渲染進程(瀏覽器內(nèi)核)

瀏覽器的渲染進程是多線程的,頁面的渲染,JavaScript 的執(zhí)行,事件的循環(huán),都在這個進程內(nèi)進行:

  • GUI 渲染線程:負責渲染瀏覽器界面,當界面需要重繪(Repaint)或由于某種操作引發(fā)回流(Reflow)時,該線程就會執(zhí)行。

  • JavaScript 引擎線程:也稱為 JavaScript 內(nèi)核,負責處理 Javascript 腳本程序、解析 Javascript 腳本、運行代碼等。(例如 V8 引擎)

  • 事件觸發(fā)線程:用來控制瀏覽器事件循環(huán),注意這不歸 JavaScript 引擎線程管,當事件被觸發(fā)時,該線程會把事件添加到待處理隊列的隊尾,等待 JavaScript 引擎的處理。

  • 定時觸發(fā)器線程:傳說中的 setInterval 與 setTimeout 所在線程,注意,W3C 在 HTML 標準中規(guī)定,規(guī)定要求 setTimeout 中低于 4ms 的時間間隔算為 4ms 。

  • 異步 http 請求線程:在 XMLHttpRequest 連接后通過瀏覽器新開一個線程請求,將檢測到狀態(tài)變更時,如果設置有回調(diào)函數(shù),異步線程就產(chǎn)生狀態(tài)變更事件,將這個回調(diào)再放入事件隊列中。再由 JavaScript 引擎執(zhí)行。

注意,GUI 渲染線程與 JavaScript 引擎線程是互斥的,當 JavaScript 引擎執(zhí)行時 GUI 線程會被掛起(相當于被凍結(jié)了),GUI 更新會被保存在一個隊列中等到 JavaScript 引擎空閑時立即被執(zhí)行。所以如果 JavaScript 執(zhí)行的時間過長,這樣就會造成頁面的渲染不連貫,導致頁面渲染加載阻塞。

二、單線程的 JavaScript

所謂單線程,是指在 JavaScript 引擎中負責解釋和執(zhí)行 JavaScript 代碼的線程唯一,同一時間上只能執(zhí)行一件任務。

問題:首先為什么要引入單線程喃?

我們知道:

  • 瀏覽器需要渲染 DOM

  • JavaScript 可以修改 DOM 結(jié)構(gòu)

  • JavaScript 執(zhí)行時,瀏覽器 DOM 渲染停止

如果 JavaScript 引擎線程不是單線程的,那么可以同時執(zhí)行多段 JavaScript,如果這多段 JavaScript 都修改 DOM,那么就會出現(xiàn) DOM 沖突。

你可能會說,web worker 就支持多線程,但是 web worker 不能訪問 window 對象,document 對象等。

原因:避免 DOM 渲染的沖突

當然,我們可以為瀏覽器引入 的機制來解決這些沖突,但其大大提高了復雜性,所以 JavaScript從誕生開始就選擇了單線程執(zhí)行。

引入單線程就意味著,所有任務需要排隊,前一個任務結(jié)束,才會執(zhí)行后一個任務。這同時又導致了一個問題:如果前一個任務耗時很長,后一個任務就不得不一直等著。

//?實例1
let?i,?sum?=?0
for(i?=?0;?i?1000000000;?i?++)?{
????sum?+=?i
}
console.log(sum)

在實例1中,sum 并不能立刻打印出來,必須在 for 循環(huán)執(zhí)行完成之后才能執(zhí)行 console.log(sum) 。

//?實例2
console.log(1)
alert('hello')
console.log(2)

在實例2中,瀏覽器先打印 1 ,然后彈出彈框,點擊確定后才執(zhí)行 console.log(2) 。

總結(jié):

  • 優(yōu)點:實現(xiàn)比較簡單,執(zhí)行環(huán)境相對單純

  • 缺點:只要有一個任務耗時很長,后面的任務都必須排隊等著,會拖延整個程序的執(zhí)行。常見的瀏覽器無響應(假死),往往就是因為某一段 Javascript 代碼長時間運行(比如死循環(huán)),導致整個頁面卡在這個地方,其他任務無法執(zhí)行。

為了解決這個問題,JavaScript 語言將任務的執(zhí)行模式分為兩種:同步和異步

三、同步與異步

1. 同步

func(args...)

如果在函數(shù) func 返回的時候,調(diào)用者就能夠得到預期結(jié)果(即拿到了預期的返回值或者看到了預期的效果),那么這個函數(shù)就是同步的。

let?a?=?1
Math.floor(a)
console.log(a)?//?1

2. 異步

如果在函數(shù) func 返回的時候,調(diào)用者還不能夠得到預期結(jié)果,而是需要在將來通過一定的手段得到,那么這個函數(shù)就是異步的。

fs.readFile('foo.txt',?'utf8',?function(err,?data)?{
????console.log(data);
});

總結(jié):

JavaScript 采用異步編程原因有兩點,

  • 一是 JavaScript 是單線程;

  • 二是為了提高 CPU 的利用率。

四、異步過程

fs.readFile('data.json',?'utf8',?function(err,?data)?{
????console.log(data)
})

在執(zhí)行這段代碼時,fs.readFile 函數(shù)返回時,并不會立刻打印 data ,只有 data.json 讀取完成時才打印。也就是異步函數(shù) fs.readFile 執(zhí)行很快,但后面還有工作線程執(zhí)行異步任務、通知主線程、主線程回調(diào)等操作,這個過程就叫做異步過程。

主線程發(fā)起一個異步操作,相應的工作線程接受請求并告知主線程已收到(異步函數(shù)返回);主線程繼續(xù)執(zhí)行后面的任務,同時工作線程執(zhí)行異步任務;工作線程完成任務后,通知主線程;主線程收到通知后,執(zhí)行一定的動作(調(diào)用回調(diào)函數(shù))。

工作線程在異步操作完成后通知主線程,那么這個通知機制又是如何顯現(xiàn)喃?答案就是就是消息隊列與事件循環(huán)。

五、消息隊列與事件循環(huán)

工作線程將消息放在消息隊列,主線程通過事件循環(huán)過程去取消息。

  • 消息隊列:消息隊列是一個先進先出的隊列,它里面存放著各種消息。

  • 事件循環(huán):事件循環(huán)是指主線程重復從消息隊列中取消息、執(zhí)行的過程。

1. 事件循環(huán)(eventloop)

主線程不斷的從消息隊列中取消息,執(zhí)行消息,這個過程稱為事件循環(huán),這種機制叫事件循環(huán)機制,取一次消息并執(zhí)行的過程叫一次循環(huán)。

大致實現(xiàn)過程如下:

while(true)?{
????var?message?=?queue.get()
????execute(message)
}

例如:

$.ajax({
????url:?'xxxx',
????success:?function(result)?{
????????console.log(1)
????}
})
setTimeout(function()?{
????console.log(2)
},?100)
setTimeout(function()?{
????console.log(3)
})
console.log(4)
// output:4321 或 4312

其中,主線程:

//?主線程
console.log(4)

異步隊列:

//?異步隊列
function?()?{
????console.log(3)
}
function?()?{?//?100ms后
????console.log(2)
}
function()?{?//?ajax加載完成之后
????console.log(1)
}

事件循環(huán)是JavaScript實現(xiàn)異步的具體解決方案,其中同步代碼,直接執(zhí)行;異步函數(shù)先放在異步隊列中,待同步函數(shù)執(zhí)行完畢后,輪詢執(zhí)行 異步隊列 的回調(diào)函數(shù)。

2. 消息隊列

其中,消息就是注冊異步任務時添加的回調(diào)函數(shù)。

$.ajax('XXX',?function(res)?{
????console.log(res)
})
...

主線程在發(fā)起 AJAX 請求后,會繼續(xù)執(zhí)行其他代碼,AJAX 線程負責請求 XXX,拿到請求后,會封裝成 JavaScript 對象,然后構(gòu)造一條消息:

//?消息隊列里的消息
var?message?=?function?()?{
????callback(response)
}

其中 callback 是 AJAX 網(wǎng)絡請求成功響應時的回調(diào)函數(shù)。

主線程在執(zhí)行完當前循環(huán)中的所有代碼后,就會到消息隊列取出這條消息(也就是 message 函數(shù)),并執(zhí)行它。到此為止,就完成了工作線程對主線程的 通知 ,回調(diào)函數(shù)也就得到了執(zhí)行。如果一開始主線程就沒有提供回調(diào)函數(shù),AJAX 線程在收到 HTTP 響應后,也就沒必要通知主線程,從而也沒必要往消息隊列放消息。

異步過程中的回調(diào)函數(shù),一定不在當前這一輪事件循環(huán)中執(zhí)行。

六、異步與事件

消息隊列中的每條消息實際上都對應著一個事件。

其中一個重要的異步過程就是:DOM事件

var?button?=?document.getElementById('button')
button.addEventLister('click',?function(e)?{
????console.log('事件')
})

從異步的角度看,addEventLister 函數(shù)就是異步過程的發(fā)起函數(shù),事件監(jiān)聽器函數(shù)就是異步過程的回調(diào)函數(shù)。事件觸發(fā)時,表示異步任務完成,會將事件監(jiān)聽器函數(shù)封裝成一條消息放在消息隊列中,等待主線程執(zhí)行。

事件的概念實際上并不是必須的,事件機制實際上就是異步過程的通知機制。

另外,所有的異步過程也都可以用事件來描述。例如:

setTimeout(func,?1000)
//?可以看成:
timer.addEventLister('timeout',?1000,?func)

其中關于事件的詳細描述,可以看這篇文章:事件綁定、事件監(jiān)聽、事件委托,這里不再深入介紹。

七、生產(chǎn)者與消費者

生產(chǎn)者和消費者問題是線程模型中的經(jīng)典問題:生產(chǎn)者和消費者在同一時間段內(nèi)共用同一個存儲空間,生產(chǎn)者往存儲空間中添加數(shù)據(jù),消費者從存儲空間中取走數(shù)據(jù),當存儲空間為空時,消費者阻塞,當存儲空間滿時,生產(chǎn)者阻塞。

從生產(chǎn)者與消費者的角度看,異步過程是這樣的:

工作線程是生產(chǎn)者,主線程是消費者(只有一個消費者)。工作線程執(zhí)行異步任務,執(zhí)行完成后把對應的回調(diào)函數(shù)封裝成一條消息放到消息隊列中;主線程不斷地從消息隊列中取消息并執(zhí)行,當消息隊列空時主線程阻塞,直到消息隊列再次非空。

那么異步的實現(xiàn)方式有哪些喃?

  • ES6之前:callback、eventloop、Promise

  • ES6:Generator

  • ES7:Async/Await

八、走在最后

1.???玩得開心,不斷學習,并始終保持編程。?

2.??點擊原文,查看更多精彩文章!?

3. 如有任何問題或更獨特的見解,歡迎聯(lián)系瓶子君!(掃碼關注公眾號,回復 123 即可)??

我知道你 “在看

總結(jié)

以上是生活随笔為你收集整理的for循环里面有异步操作_JS 线程与异步的那些事的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。