進程是指程序的一次執行,它佔有一片獨有的內存空間,能夠經過windows任務管理器查看進程(以下圖)。同一個時間裏,同一個計算機系統中容許兩個或兩個以上的進程處於並行狀態,這是多進程。好比電腦同時運行微信,QQ,以及各類瀏覽器等。瀏覽器運行是有些是單進程,如firefox和老版IE,有些是多進程,如chrome和新版IE。javascript
有些進程還不止同時幹一件事,好比Word,它能夠同時進行打字、拼寫檢查、打印等事情。在一個進程內部,要同時幹多件事,就須要同時運行多個「子任務」,咱們把進程內的這些「子任務」稱爲線程(Thread)。 線程是指CPU的基本調度單位,是程序執行的一個完整流程,是進程內的一個獨立執行單元。多線程是指在一個進程內, 同時有多個線程運行。瀏覽器運行是多線程。好比用瀏覽器一邊下載,一邊聽歌,一邊看視頻。另外咱們須要知道JavaScript語言的一大特色就是單線程,爲了利用多核CPU的計算能力,HTML5提出Web Worker標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。因此,這個新標準並無改變JavaScript單線程的本質。 css
因爲每一個進程至少要幹一件事,因此,一個進程至少有一個線程。固然,像Word這種複雜的進程能夠有多個線程,多個線程能夠同時執行,多線程的執行方式和多進程是同樣的,也是由操做系統在多個線程之間快速切換,讓每一個線程都短暫地交替運行,看起來就像同時執行同樣。固然,真正地同時執行多線程須要多核CPU纔可能實現。html
應用程序必須運行在某個進程的某個線程上java
一個進程中至少有一個運行的線程: 主線程, 進程啓動後自動建立git
一個進程中若是同時運行多個線程, 那這個程序是多線程運行的github
一個進程的內存空間是共享的,每一個線程均可以使用這些共享內存。web
多個進程之間的數據是不能直接共享的chrome
單線程的優勢:順序編程簡單易懂編程
單線程的缺點:效率低windows
多線程的優勢:能有效提高CPU的利用率
多線程的缺點:
瀏覽器的內核是指支持瀏覽器運行的最核心的程序,分爲兩個部分的,一是渲染引擎,另外一個是JS引擎。如今JS引擎比較獨立,內核更加傾向於說渲染引擎。
咱們先來看個例子,試問定時器會保證200ms後執行嗎?
document.getElementById('btn').onclick = function () {
var start = Date.now()
console.log('啓動定時器前...')
setTimeout(function () {
console.log('定時器執行了', Date.now() - start)
}, 200)
console.log('啓動定時器後...')
// 作一個長時間的工做
for (var i = 0; i < 1000000000; i++) {
}
}
複製代碼
定時器回調函數在主線程執行的, 具體實現方式下文會介紹。
JavaScript語言的一大特色就是單線程,也就是說,同一個時間只能作一件事。那麼,爲何JavaScript不能有多個線程呢?這樣能提升效率啊。
JavaScript的單線程,與它的用途有關。做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。好比,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另外一個線程刪除了這個節點,這時瀏覽器應該以哪一個線程爲準?
因此,爲了不復雜性,從一誕生,JavaScript就是單線程,這已經成了這門語言的核心特徵,未來也不會改變。 爲了利用多核CPU的計算能力,HTML5提出Web Worker標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。因此,這個新標準並無改變JavaScript單線程的本質。
JavaScript中全部任務能夠分紅兩種,一種是同步任務,另外一種是異步任務(如各類瀏覽器事件、定時器和Ajax等)。同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。
具體來講,異步執行的運行機制以下。(同步執行也是如此,由於它能夠被視爲沒有異步任務的異步執行。)
(1)全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。
(2)主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
(3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步
主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)
setTimeout(function () {
console.log('timeout 2222')
alert('22222222')
}, 2000)
setTimeout(function () {
console.log('timeout 1111')
alert('1111111')
}, 1000)
setTimeout(function () {
console.log('timeout() 00000')
}, 0)//當指定的值小於 4 毫秒,則增長到 4ms(4ms 是 HTML5 標準指定的,對於 2010 年及以前的瀏覽器則是 10ms)
function fn() {
console.log('fn()')
}
fn()
console.log('alert()以前')
alert('------') //暫停當前主線程的執行, 同時暫停計時, 點擊肯定後, 恢復程序執行和計時
console.log('alert()以後')
複製代碼
有兩點咱們須要注意下:
總結:異步任務(各類瀏覽器事件、定時器和Ajax等)都是先添加到「任務隊列」(定時器則到達其指定參數時)。當 Stack 棧(JavaScript 主線程)爲空時,就會讀取 Queue 隊列(任務隊列)的第一個任務(隊首),最後執行。
正如上面所提到,JavaScript是單線程。當一個頁面加載一個複雜運算的 js 文件時,用戶界面可能會短暫地「凍結」,不能再作其餘操做。好比下面這個例子:
<input type="text" placeholder="數值" id="number">
<button id="btn">計算</button>
<script type="text/javascript">
// 1 1 2 3 5 8 f(n) = f(n-1) + f(n-2)
function fibonacci(n) {
return n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2) //遞歸調用
}
var input = document.getElementById('number')
document.getElementById('btn').onclick = function () {
var number = input.value
var result = fibonacci(number)
alert(result)
}
</script>
複製代碼
Web Worker 的做用,就是爲 JavaScript 創造多線程環境,容許主線程建立 Worker 線程,將一些任務分配給後者運行。在主線程運行的同時,Worker 線程在後臺運行,二者互不干擾。等到 Worker 線程完成計算任務,再把結果返回給主線程。這樣的好處是,一些計算密集型或高延遲的任務,被 Worker 線程負擔了,主線程(一般負責 UI 交互)就會很流暢,不會被阻塞或拖慢。其原理圖以下:
主線程
var worker = new Worker('work.js');
複製代碼
var input = document.getElementById('number')
document.getElementById('btn').onclick = function () {
var number = input.value
//建立一個Worker對象
var worker = new Worker('worker.js')
// 綁定接收消息的監聽
worker.onmessage = function (event) {
console.log('主線程接收分線程返回的數據: '+event.data)
alert(event.data)
}
// 向分線程發送消息
worker.postMessage(number)
console.log('主線程向分線程發送數據: '+number)
}
console.log(this) // window
複製代碼
Worker 線程
//worker.js文件
function fibonacci(n) {
return n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2) //遞歸調用
}
console.log(this)//[object DedicatedWorkerGlobalScope]
this.onmessage = function (event) {
var number = event.data
console.log('分線程接收到主線程發送的數據: '+number)
//計算
var result = fibonacci(number)
postMessage(result)
console.log('分線程向主線程返回數據: '+result)
// alert(result) alert是window的方法, 在分線程不能調用
// 分線程中的全局對象再也不是window, 因此在分線程中不可能更新界面
}
複製代碼
這樣當分線程在計算時,用戶界面還能夠操做,並且更早拿到計算後數據,響應速度更快了。
若是須要源代碼,請猛戳Web Workers
若是以爲文章對你有些許幫助,歡迎在個人GitHub博客點贊和關注,感激涕零!