單線程的JS 就是一個傻子,腦子一根筋,做著當前的這件事情,沒有完成之前,絕對不會做下一件事情,那麼通過什麼方法可以讓JS變“聰明”? ...
同步和非同步
JS是單線程
JavaScript語言的一大特點是單線程,同一時間只能做一件事
(單線程的JS 就是一個傻子,腦子一根筋,做著當前的這件事情,沒有完成之前,絕對不會做下一件事情)
當然,這是由其誕生的初衷所決定的——處理頁面中用戶的交互,以及操作DOM
用戶不可能同時進行兩個操作,邊添加邊刪除
當然會出現一個問題:所有的任務需要排隊,前一個結束,才會執行下一個(要是前面有人很磨蹭,後面的人需要等很久),造成頁面渲染的不連貫
console.log(1)
setTimeout(function(){
console.log(3)
},100000000);
console.log(2)//快點吧,等的我花都謝了
同步和非同步
問題總有解決方案,利用多核CPU的計算能力,HTML5提出了Web Worker標準,允許JS腳本創建多個線程,於是JS出現了——同步和非同步
- 同步:前一個任務結束執行下一個任務,任務的執行順序和任務的排列順序是一致的
- 非同步:在執行某一任務(要花費很長時間)的同時,可以執行其他任務
所以上面那個代碼結果是什麼呢?
JS沒有那麼傻,不會一直等待,所以列印了:1 2 3
//那這個呢?
console.log(1)
setTimeout(function(){
console.log(3)
},0);
console.log(2)
知道同步和非同步是什麼之後,我們要學習——同步任務和非同步任務:
- 同步任務(synchronous)(非耗時任務):同步任務都在主線程上執行,形成一個執行棧
- 非同步任務(asynchronous)(耗時任務):JS的非同步任務都是通過回調函數實現的,如:
- 普通事件:click、resize等
- 資源載入:load、error等
- 定時器:setInterval、setTimeout等
JS執行機制
從記憶體角度(上圖)理解不難發現,同步任務和非同步任務根本身處兩個區域,當執行任務時:
- 先執行執行棧中的同步任務
- 非同步任務(回調函數)放入任務隊列中
- 執行完所有的同步任務,就會一次讀取任務隊列中的非同步任務,結束等待,進入執行棧開始執行
//通過底下的;例子練習一下吧(結果不唯一哦)
console.log(1);
document.onclick=function(){
console.log('click');
}
console.log(2);
setTimeout(function(){
console.log(3);
},3000)
你會發現,當點擊滑鼠時,click不斷被列印,說明主線程不斷獲取著任務隊列中的非同步任務,這種主線程不斷
的重覆獲得任務、執行任務,再獲得任務、執行任務的這種機制就是——事件迴圈(Event Loop)!!!!
巨集任務和微任務
JavaScript把非同步任務又做了進一步劃分——巨集任務和微任務
巨集任務(macrotask):
- 非同步Ajax請求
- setTimeout、setInterval
- 文件操作
- 其他巨集任務
微任務(microtask):
- Promise.then、.catch和.finally
- process.nextTick
- 其他微任務
既然細分,勢必涉及到二者的執行順序:
每一個巨集任務執行完成之後,都會檢查是否存在待執行的微任務,如果有,則執行完所有的微任務,再繼續執行下一個巨集任務
哈哈,到這裡你是不是以為已經學了很多,課件到此就結束了
其實才剛剛開始,使用回調函數只是JavaScript 的非同步編程發展的第一個階段,也只是非同步解決方案的其中一種
可以說JavaScript 的非同步編程發展經過了四個階段:
- 回調函數、發佈訂閱
- Promise
- co 自執行的 Generator 函數
- async / await
接下來,我們主要講一下Promise和async / await
Promise
Promise本意是承諾,在程式中的意思就是承諾我過一段時間後會給你一個結果。 什麼時候會用到過一段時間?
答案是非同步操作,非同步是指可能比較長時間才有結果的才做,例如網路請求、讀取本地文件等
回調地獄
之所以誕生Promise,是因為回調地獄的問題,廢話不多說,先看下麵代碼:
function demo(num) {
setTimeout(function () {
console.log("1");
if (num > 5) {
setTimeout(function () {
console.log(num);
}, 500);
} else {
setTimeout(function () {
console.log("3");
setTimeout(function () {
console.log("4");
setTimeout(function () {
console.log("5");
}, 3000);
}, 500);
}, 500);
}
}, 500);
}
demo(6);
是不是看到想把電腦砸了?