本篇文章帶大家了解JavaScript中的非同步程式設計。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
異步,就是非同步....
這節內容可能會有點枯燥,但是卻是JavaScript 中非常重要的概念,非常有必要去學習。
$.ajax({ url: "www.xx.com/api", async: false, // true success: function(result) { console.log(result); }, });
// 异步批量更新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 引擎中負責解釋和執行 IavaScript 程式碼的執行緒只有一個,不妨叫它主執行緒。
所謂單線程,就是指一次只能完成一件任務。如果有多個任務,就必須排隊,前面一個任務完成再執行後面一個任務。
先來看看瀏覽器核心的執行緒圖:
#其中,渲染執行緒與JS 執行緒互斥 。
假設有兩個函數,一個修改一個刪除,同時操作一個 DOM 節點,假如有多個執行緒的話,兩個執行緒一起執行,肯定就死鎖了,就會有問題。
為什麼 JS 要設計為單線程,因為瀏覽器的特殊環境。
單一執行緒的優缺點:
這種模式的好處是實現起來比較簡單,執行環境相對單純;壞處是只要有一個任務耗時很長,後面的任務都必須排隊等著,會拖延整個程式的執行。常見的瀏覽器無回應(假死),往往是因為某一段 Javascript 程式碼長時間運行(例如死循環),導致整個頁面卡在這個地方,其他任務無法執行。
常見的堵塞(死循環):
while (true) {}
JS 在設計之初就以運行在瀏覽器中的腳本語言,所以也不想搞得這麼複雜,就設計成了單線程,也就是,一個時間點,只能做一件事。
為了解決單執行緒阻塞這個缺點:產生了非同步。
拿吃泡麵舉例:
看電視就是非同步操作,熱水壺響,就是回呼函數。
JS 中大多的程式碼都是同步執行的,只有極個別的函數是非同步執行的,非同步執行的程式碼,則需要非同步程式設計。
setTimeout(() => { console.log("log2"); }, 0); console.log("log1"); // ?? log1 log2
非同步程式碼的特點:不是立即執行,而是需要等待,在未來的某一個時間點執行。
同步程式碼 | #非同步程式碼 |
---|---|
<script> 程式碼 | 網路請求(Ajax) |
I/O 操作 | 計時器(setTimeout、setInterval) |
渲染操作 | Promise(then) |
回呼函數
// 注意到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); }, }); }, }); }
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 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>
(宏任务)
执行所有微任务
执行一个宏任务
执行渲染线程
2->3->2->3...依次循环(在 2、3 步中又创建了新的宏、微任务)
重复从宏任务和微任务队列里拿出任务去执行。
因为浏览器设计的原因,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 的输出结果,并说出理由。
更多编程相关知识,请访问:编程视频!!
以上是手把手帶你去理解JavaScript中的非同步編程的詳細內容。更多資訊請關注PHP中文網其他相關文章!