首頁  >  文章  >  web前端  >  手把手帶你去理解JavaScript中的非同步編程

手把手帶你去理解JavaScript中的非同步編程

青灯夜游
青灯夜游轉載
2021-06-03 11:02:421362瀏覽

本篇文章帶大家了解JavaScript中的非同步程式設計。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

手把手帶你去理解JavaScript中的非同步編程

異步,就是非同步....

這節內容可能會有點枯燥,但是卻是JavaScript 中非常重要的概念,非常有必要去學習。

目的

  • 提升開發效率,寫出易維護的程式碼

引子問題

  • 請求時候為什麼頁面卡死? ?
$.ajax({
  url: "www.xx.com/api",
  async: false, // true
  success: function(result) {
    console.log(result);
  },
});
  • 為什麼資料更新了,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 引擎中負責解釋和執行 IavaScript 程式碼的執行緒只有一個,不妨叫它主執行緒。

所謂單線程,就是指一次只能完成一件任務。如果有多個任務,就必須排隊,前面一個任務完成再執行後面一個任務。

先來看看瀏覽器核心的執行緒圖:

手把手帶你去理解JavaScript中的非同步編程

#其中,渲染執行緒與JS 執行緒互斥

假設有兩個函數,一個修改一個刪除,同時操作一個 DOM 節點,假如有多個執行緒的話,兩個執行緒一起執行,肯定就死鎖了,就會有問題。

為什麼 JS 要設計為單線程,因為瀏覽器的特殊環境。

單一執行緒的優缺點:

這種模式的好處是實現起來比較簡單,執行環境相對單純;壞處是只要有一個任務耗時很長,後面的任務都必須排隊等著,會拖延整個程式的執行。常見的瀏覽器無回應(假死),往往是因為某一段 Javascript 程式碼長時間運行(例如死循環),導致整個頁面卡在這個地方,其他任務無法執行。

常見的堵塞(死循環):

while (true) {}

JS 在設計之初就以運行在瀏覽器中的腳本語言,所以也不想搞得這麼複雜,就設計成了單線程,也就是,一個時間點,只能做一件事。

為了解決單執行緒阻塞這個缺點:產生了非同步。

拿吃泡麵舉例:

  • 同步:買泡麵=》燒水(盯著)=》煮麵=》吃泡麵
  • 異步:買泡麵=》燒水(水開了熱水壺響-回調)=》看電視=》煮麵(面好了熱水壺響-回調)=》看電視=》熟了叫我=》吃泡面

看電視就是非同步操作,熱水壺響,就是回呼函數。

非同步程式設計

JS 中大多的程式碼都是同步執行的,只有極個別的函數是非同步執行的,非同步執行的程式碼,則需要非同步程式設計。

非同步程式碼

setTimeout(() => {
  console.log("log2");
}, 0);
console.log("log1");
// ?? log1 log2

非同步程式碼的特點:不是立即執行,而是需要等待,在未來的某一個時間點執行。

同步程式碼 #非同步程式碼
<script></script>程式碼 網路請求(Ajax)
I/O 操作 計時器(setTimeout、setInterval)
渲染操作 Promise(then)

  • #async/await


手把手帶你去理解JavaScript中的非同步編程回呼函數

非同步程式碼最常見的寫法就是使用回呼函數。

    HTTP 網路請求(請求成功、識別後執行xx 操作)
  • DOM 事件綁定機制(使用者觸發事件後執行xx 操作)
    計時器(setTimeout、setInterval)(在達到設定時間後執行xx 操作)
  • // 注意到click方法中是一个函数而不是一个变量
    // 它就是回调函数
    $("#btn_1").click(function() {
      alert("Btn 1 Clicked");
    });
    // 或者
    function click() {
      // 它就是回调函数
      alert("Btn 1 Clicked");
    }
    $("#btn_1").click(click);
回呼函數的缺點也很明顯,容易產生回呼地獄:

非同步程式設計的三種方式

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 如果多個非同步程式碼同時存在,那麼執行順序應該是怎麼樣的?那個先執行、那個後執行了?
非同步程式碼的劃分,非同步程式碼分宏任務和微任務。
#微任務(急)
#########<script>###整體程式碼#######Promise############setTimeout/setInterval##### ##################<h2><strong>事件循环(Event loop)<p><img src="https://img.php.cn/upload/image/559/429/380/1622689034979881.png" title="1622689034979881.png" alt="手把手帶你去理解JavaScript中的非同步編程"/><p>执行顺序:<ul style="max-width:90%"><li><p>执行整体代码<code><script>(宏任务)<li><p>执行所有微任务<li><p>执行一个宏任务<li><p>执行渲染线程<li><p>2->3->2->3...依次循环(在 2、3 步中又创建了新的宏、微任务)<p>重复从宏任务和微任务队列里拿出任务去执行。<h2><strong>总结<p>因为浏览器设计的原因,JS 线程和渲染线程互斥,所以 JS 线程被设计成了单线程。<p>因为单线程执行一些操作(如网络请求)时有堵塞的问题,所有产生了异步。<p>因为有了异步,所以产生了异步编程,从而有了回调函数。<p>因为回调函数写多了会产生回调地狱,所有又有了解决回调地狱的 Promise 写法<p>自 ES7 标准后有了比 Promise 更加优雅的写法 ———— async/await 写法,也是异步编程的最终解决方法。<p>因为 JS 的代码分为同步和异步代码,同步代码的执行顺序不必多说,自上而下的执行。<p>但是如果有多个异步的代码,他的执行顺序又是怎么的呢??<p>为了解决多个异步代码的执行顺序问了,有了事件循环(EventLoop),将异步任务区分为宏任务、微任务,依据规则依次执行。<p>至此 完!<h2><strong>练习<pre class="brush:js;toolbar:false;">console.log(&quot;script start&quot;); setTimeout(function() { console.log(&quot;timeout1&quot;); }, 10); new Promise((resolve) =&gt; { console.log(&quot;promise1&quot;); resolve(); setTimeout(() =&gt; console.log(&quot;timeout2&quot;), 10); }).then(function() { console.log(&quot;then1&quot;); }); console.log(&quot;script end&quot;);</pre><p>写出 log 的输出结果,并说出理由。<p>更多编程相关知识,请访问:<a href="https://www.php.cn/course.html" target="_blank" textvalue="编程视频">编程视频!!</script>

以上是手把手帶你去理解JavaScript中的非同步編程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除