Q:你瞭解異步編程、進程、單線程、多線程嗎?

相關定義

Javascript語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。javascript

  1. 同步:一個進程在執行某個請求的時候,若該請求須要一段時間才能返回信息,那麼這個進程將會一直等待下去,直到收到返回信息才繼續執行下去。
  2. 異步:進程不須要一直等下去,而是繼續執行下面的操做,無論其餘進程的狀態。當有消息返回時系統會通知進程進行處理,這樣能夠提升執行的效率。
  3. 進程:狹義上,就是正在運行的程序的實例。廣義上,進程是一個具備必定獨立功能的程序關於某個數據集合的一次運行活動。它是操做系統動態執行的基本單元,在傳統的操做系統中,進程既是基本的分配單元,也是基本的執行單元。
  4. 線程:線程是程序中一個單一的順序控制流程。進程內一個相對獨立的、可調度的執行單元,是系統獨立調度和分派CPU的基本單位。指運行中的程序的調度單位。
  5. 單線程:單線程在程序執行時,所走的程序路徑按照連續順序排下來,前面的必須處理好,後面的纔會執行。單線程就是進程裏只有一個線程。
  6. 多線程:在單個程序中同時運行多個線程完成不一樣的工做,稱爲多線程。

知識點

js是單線程的java

JS運行在瀏覽器中,是單線程的,每一個window一個JS線程,既然是單線程的,在某個特定的時刻只有特定的代碼可以被執行,並阻塞其它的代碼。而瀏覽器是事件驅動的,瀏覽器中不少行爲是異步的,會建立事件並放入執行隊列中,JavaScript引擎是單線程處理它的任務隊列。當異步事件發生時,鼠標點擊事件發生、定時器觸發事件發生、XMLHttpRequest完成回調觸發等,將他們放入執行隊列,等待當前代碼執行完成。編程

瀏覽器不是單線程的promise

雖然JS運行在瀏覽器中,是單線程的,但瀏覽器不是單線程的,例如Web kit引擎,可能有以下線程:瀏覽器

  • JavaScript引擎線程
  • 界面渲染線程
  • 瀏覽器事件觸發線程
  • HTTP請求線程

當一個異步事件發生的時候,它就進入事件隊列。瀏覽器有一個內部大消息循環,Event Loop(事件循環),會輪詢事件隊列並處理事件。好比,瀏覽器當前正在忙於處理onclick事件,這時window onSize事件發生了,這個異步事件就被放入事件隊列等待處理,只有前面的處理完畢了,空閒了纔會執行這個事件。bash

爲何JavaScript是單線程的卻能讓AJAX異步發送和回調請求,爲何setTimeout也看起來像是多線程的?多線程

Ajax請求確實是異步的,這請求是由瀏覽器新開了一個線程請求,事件回調的時候是放入Event loop單線程事件隊列等候處理。當瀏覽器空閒的時候出隊列任務被處理,JavaScript引擎始終是單線程運行回調函數、單線程處理它的任務隊列。異步

setTimeout(func, 0)神奇在哪兒?那就是告訴js引擎,在0ms之後把func放到主事件隊列中,等待當前的代碼執行完畢再執行,注意:重點是改變了代碼流程,把func的執行放到了主事件隊列中。這就是它的神奇之處了。它的用處有三個:模塊化

  1. 讓瀏覽器渲染當前的變化(不少瀏覽器UI render和js執行是放在一個線程中,線程阻塞會致使界面沒法更新渲染)
  2. 從新計算script運行時間,即從新判斷」script is running too long」這個警告
  3. 改變了執行順序

詳細解釋見下一篇文章《巧用setTimeout(func, 0)》。(2017-11-30注:原本想寫的,偶然翻到一篇文章《這一次,完全弄懂 JavaScript 執行機制》以爲已經寫得很好了,就收藏啦(#^.^#))異步編程

異步編程三種方法

一:回調函數

這是異步編程最基本的方法。
假定有兩個函數f1和f2,後者等待前者的執行結果。  

 f1();
 f2();複製代碼

若是f1是一個很耗時的任務,能夠考慮把f2寫成f1的回調函數。

 function f1(callback){
    setTimeout(function () {
      // f1的任務代碼
      callback();
    }, 1000);
  }複製代碼

執行代碼就變成下面這樣:  

 f1(f2);複製代碼

採用這種方式,咱們把同步操做變成了異步操做,f1不會堵塞程序運行。回調函數的優勢是簡單、容易理解和部署,缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合,流程會很混亂,並且每一個任務只能指定一個回調函數

2、事件監聽

另外一種思路是採用事件驅動模式。任務的執行不取決於代碼的順序,而取決於某個事件是否發生。
仍是以f1和f2爲例。首先,爲f1綁定一個事件(這裏採用的jQuery的寫法)。

  f1.on('done', f2);複製代碼

上面這行代碼的意思是,當f1發生done事件,就執行f2。而後,對f1進行改寫:

  function f1(){
    setTimeout(function () {
      // f1的任務代碼
      f1.trigger('done');
    }, 1000);
  }複製代碼

f1.trigger('done')表示,執行完成後,當即觸發done事件,從而開始執行f2。
這種方法的優勢是比較容易理解,能夠綁定多個事件,每一個事件能夠指定多個回調函數,並且能夠"去耦合",有利於實現模塊化。缺點是整個程序都要變成事件驅動型,運行流程會變得很不清晰。

3、Promises對象

Promise 是異步編程的一種解決方案,比傳統的解決方案「回調函數」和「事件」——更合理和更強大。它由社區最先提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。
基本用法以下:

const promise = new Promise(function(resolve, reject) {
      // ... some code
      if (/* 異步操做成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    promise.then(function(value) {
    // success
    }, function(error) {
     // failure
    });複製代碼

下面列出異步操做失敗、抓捕異常的另外一種寫法

const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});複製代碼

這樣寫的優勢在於,回調函數變成了鏈式寫法,程序的流程能夠看得很清楚,能夠實現許多強大的功能。
好比,指定多個回調函數等等。

相關文章
相關標籤/搜索