異步,就是非同步....javascript
這節內容可能會有點枯燥,可是倒是 JavaScript 中很是重要的概念,很是有必要去學習。html
$.ajax({ url: "www.xx.com/api", async: false, // true success: function(result) { console.log(result); }, });
// 異步批量更新DOM(vue-nextTick) // <div id="app">{{num}}</div> new Vue({ el: "#app", data: { num: 0, }, mounted() { let dom = document.getElementById("app"); while (this.num !== 100) { this.num++; } console.log("Vue num=" + this.num, "DOM num=" + dom.innerHTML); // Vue num=100,DOM num=0 // nextTick or setTimeout }, });
緣由:單線程(一個時間點,只作一件事),瀏覽器的 JS 引擎是單線程致使的。vue
單線程是指在 JS 引擎中負責解釋和執行 IavaScript 代碼的線程只有一個,不妨叫它主線程。html5
所謂單線程,就是指一次只能完成一件任務。若是有多個任務,就必須排隊,前面一個任務完成再執行後面一個任務。java
先看看一下瀏覽器內核的線程圖:
ios
其中,渲染線程和 JS 線程互斥。ajax
假設有兩個函數,一個修改一個刪除,同時操做一個 DOM 節點,假若有多個線程的話,兩個線程一塊兒執行,確定就死鎖了,就會有問題。編程
爲何 JS 要設計爲單線程,由於瀏覽器的特殊環境。axios
單線程的優缺點:segmentfault
這種模式的好處是實現起來比較簡單,執行環境相對單純; 壞處是隻要有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程序的執行。常見的瀏覽器無響應(假死),每每就是由於某一段 Javascript 代碼長時間運行(好比死循環),致使整個頁面卡在這個地方,其餘任務沒法執行。
常見的堵塞(死循環):
while (true) {}
JS 在設計之初就以運行在瀏覽器中的腳本語言,因此也不想搞得這麼複雜,就設計成了單線程,也就是,一個時間點,只能作一件事。
爲了解決單線程堵塞這個缺點:產生了異步。
拿吃泡麪舉例:
看電視就是異步操做,熱水壺響,就是回調函數。
JS 中大多的代碼都是同步執行的,只有極個別的函數是異步執行的,異步執行的代碼,則須要異步編程。
setTimeout(() => { console.log("log2"); }, 0); console.log("log1"); // ?? log1 log2
異步代碼的特色:不是當即執行,而是須要等待,在將來的某一個時間點執行。
同步代碼 | 異步代碼 |
---|---|
<script> 代碼 |
網絡請求(Ajax) |
I/O 操做 | 定時器(setTimeout、setInterval) |
渲染操做 | Promise(then) |
async/await |
異步代碼最多見的寫法就是使用回調函數。
// 注意到click方法中是一個函數而不是一個變量 // 它就是回調函數 $("#btn_1").click(function() { alert("Btn 1 Clicked"); }); // 或者 function click() { // 它就是回調函數 alert("Btn 1 Clicked"); } $("#btn_1").click(click);
回調函數的缺點也很明顯,容易產生回調地獄:
function getOneNews() { $.ajax({ url: topicsUrl, success: function(res) { let id = res.data[0].id; $.ajax({ url: topicOneUrl + id, success: function(ress) { console.log(ress); render(ress.data); }, }); }, }); }
function getOneNews() { axios .get(topicsUrl) .then(function(response) { let id = response.data.data[0].id; return axios.get(topicOneUrl + id); }) .then((res) => { render(res.data.data); }) .catch(function(error) { console.log(error); }); }
async function getOneNews() { let listData = await axios.get(topicsUrl); let id = listData.data.data[0].id; let data = await axios.get(topicOneUrl + id); render(data.data.data); }
預覽地址:http://jsrun.net/s43Kp/embedded/all/light
若是多個異步代碼同時存在,那麼執行順序應該是怎樣的?那個先執行、那個後執行了?
異步代碼的劃分,異步代碼分宏任務和微任務。
宏任務(不着急) | 微任務(着急) |
---|---|
<script> 總體代碼 |
Promise |
setTimeout/setInterval |
執行順序:
<script>
(宏任務)重複從宏任務和微任務隊列裏拿出任務去執行。
由於瀏覽器設計的緣由,JS 線程和渲染線程互斥,因此 JS 線程被設計成了單線程。
由於單線程執行一些操做(如網絡請求)時有堵塞的問題,全部產生了異步。
由於有了異步,因此產生了異步編程,從而有了回調函數。
由於回調函數寫多了會產生回調地獄,全部又有了解決回調地獄的 Promise 寫法
自 ES7 標準後有了比 Promise 更加優雅的寫法 ———— async/await 寫法,也是異步編程的最終解決方法。
由於 JS 的代碼分爲同步和異步代碼,同步代碼的執行順序沒必要多說,自上而下的執行。
可是若是有多個異步的代碼,他的執行順序又是怎麼的呢??
爲了解決多個異步代碼的執行順序問了,有了事件循環(EventLoop),將異步任務區分爲宏任務、微任務,依據規則依次執行。
至此 完!
console.log("script start"); setTimeout(function() { console.log("timeout1"); }, 10); new Promise((resolve) => { console.log("promise1"); resolve(); setTimeout(() => console.log("timeout2"), 10); }).then(function() { console.log("then1"); }); console.log("script end");
寫出 log 的輸出結果,並說出理由。
來自九旬的 原創文章