您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關JavaScript中異步編程的示例分析的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
提升開發(fā)效率,編寫易維護的代碼
請求時候為什么頁面卡死??
$.ajax({ url: "www.xx.com/api", async: false, // true success: function(result) { console.log(result); }, });
為什么數(shù)據(jù)更新了,DOM 卻沒有更新??
// 異步批量更新DOM(vue-nextTick) // <p id="app">{{num}}</p> 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 引擎是單線程導致的。
單線程是指在 JS 引擎中負責解釋和執(zhí)行 IavaScript 代碼的線程只有一個,不妨叫它主線程。
所謂單線程,就是指一次只能完成一件任務。如果有多個任務,就必須排隊,前面一個任務完成再執(zhí)行后面一個任務。
先看看一下瀏覽器內(nèi)核的線程圖:
其中,渲染線程和 JS 線程互斥。
假設有兩個函數(shù),一個修改一個刪除,同時操作一個 DOM 節(jié)點,假如有多個線程的話,兩個線程一起執(zhí)行,肯定就死鎖了,就會有問題。
為什么 JS 要設計為單線程,因為瀏覽器的特殊環(huán)境。
單線程的優(yōu)缺點:
這種模式的好處是實現(xiàn)起來比較簡單,執(zhí)行環(huán)境相對單純;壞處是只要有一個任務耗時很長,后面的任務都必須排隊等著,會拖延整個程序的執(zhí)行。常見的瀏覽器無響應(假死),往往就是因為某一段 Javascript 代碼長時間運行(比如死循環(huán)),導致整個頁面卡在這個地方,其他任務無法執(zhí)行。
常見的堵塞(死循環(huán)):
while (true) {}
JS 在設計之初就以運行在瀏覽器中的腳本語言,所以也不想搞得這么復雜,就設計成了單線程,也就是,一個時間點,只能做一件事。
為了解決單線程堵塞這個缺點:產(chǎn)生了異步。
拿吃泡面舉例:
同步:買泡面=》燒水(盯著)=》煮面=》吃泡面
異步:買泡面=》燒水(水開了熱水壺響-回調(diào))=》看電視=》煮面(面好了熱水壺響-回調(diào))=》看電視=》熟了叫我=》吃泡面
看電視就是異步操作,熱水壺響,就是回調(diào)函數(shù)。
JS 中大多的代碼都是同步執(zhí)行的,只有極個別的函數(shù)是異步執(zhí)行的,異步執(zhí)行的代碼,則需要異步編程。
setTimeout(() => { console.log("log2"); }, 0); console.log("log1"); // ?? log1 log2
異步代碼的特點:不是立即執(zhí)行,而是需要等待,在未來的某一個時間點執(zhí)行。
同步代碼 | 異步代碼 |
---|---|
<script> 代碼 | 網(wǎng)絡請求(Ajax) |
I/O 操作 | 定時器(setTimeout、setInterval) |
渲染操作 | Promise(then) |
async/await |
異步代碼最常見的寫法就是使用回調(diào)函數(shù)。
HTTP 網(wǎng)絡請求(請求成功、識別后執(zhí)行 xx 操作)
DOM 事件綁定機制(用戶觸發(fā)事件后執(zhí)行 xx 操作)
定時器(setTimeout、setInterval)(在達到設定時間后執(zhí)行 xx 操作)
// 注意到click方法中是一個函數(shù)而不是一個變量 // 它就是回調(diào)函數(shù) $("#btn_1").click(function() { alert("Btn 1 Clicked"); }); // 或者 function click() { // 它就是回調(diào)函數(shù) alert("Btn 1 Clicked"); } $("#btn_1").click(click);
回調(diào)函數(shù)的缺點也很明顯,容易產(chǎn)生回調(diào)地獄:
callback
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); }, }); }, }); }
promise
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/await
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
如果多個異步代碼同時存在,那么執(zhí)行順序應該是怎樣的?那個先執(zhí)行、那個后執(zhí)行了?
異步代碼的劃分,異步代碼分宏任務和微任務。
宏任務(不著急) | 微任務(著急) |
---|---|
<script> 整體代碼 | Promise |
setTimeout/setInterval |
執(zhí)行順序:
執(zhí)行整體代碼<script>
(宏任務)
執(zhí)行所有微任務
執(zhí)行一個宏任務
執(zhí)行渲染線程
2->3->2->3...依次循環(huán)(在 2、3 步中又創(chuàng)建了新的宏、微任務)
重復從宏任務和微任務隊列里拿出任務去執(zhí)行。
因為瀏覽器設計的原因,JS 線程和渲染線程互斥,所以 JS 線程被設計成了單線程。
因為單線程執(zhí)行一些操作(如網(wǎng)絡請求)時有堵塞的問題,所有產(chǎn)生了異步。
因為有了異步,所以產(chǎn)生了異步編程,從而有了回調(diào)函數(shù)。
因為回調(diào)函數(shù)寫多了會產(chǎn)生回調(diào)地獄,所有又有了解決回調(diào)地獄的 Promise 寫法
自 ES7 標準后有了比 Promise 更加優(yōu)雅的寫法 ———— async/await 寫法,也是異步編程的最終解決方法。
因為 JS 的代碼分為同步和異步代碼,同步代碼的執(zhí)行順序不必多說,自上而下的執(zhí)行。
但是如果有多個異步的代碼,他的執(zhí)行順序又是怎么的呢??
為了解決多個異步代碼的執(zhí)行順序問了,有了事件循環(huán)(EventLoop),將異步任務區(qū)分為宏任務、微任務,依據(jù)規(guī)則依次執(zhí)行。
至此 完!
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 的輸出結果,并說出理由。
感謝各位的閱讀!關于“JavaScript中異步編程的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。