js中的同步和異步


  自從讀了研后,走上了學術之路,每天除了看論文就是做實驗,最后發現自己還是喜歡開發呀,於是我又重回前端啦~

  隔了這么久沒學前端,好像很多東西都忘了不少,而且不得不說前端的技術更新是真的快,接下來將會重新拾起前端的一點一滴,首先進入的是js的同步和異步的世界~

 

一、單線程

(1)單線程的概念  

  如果大家熟悉java,應該都知道,java是一門多線程語言,我們常常可以利用java的多線程處理各種各樣的事,比如說文件上傳,下載等,而JavaScript是否也可以支持多線程呢?

  答案是否定的,JavaScript是一門單線程的語言,因此,JavaScript在同一個時間只能做一件事,單線程意味着,如果在同個時間有多個任務的話,這些任務就需要進行排隊,前一個任務執行完,才會執行下一個任務,比如說下面這段代碼

 

// 同步代碼
function fun1() {
  console.log(1);
}
function fun2() {
  console.log(2);
}
fun1();
fun2();

// 輸出
1
2

 

  很容易可以看出,輸出會依次輸入1,2,因為代碼是從上到下依次執行,執行完fun1(),才繼續執行fun2(),但是如果fun1()中的代碼執行的是讀取文件或者ajax操作,文件的讀取和數據的獲取都需要一定時間,難道我們需要完全等到fun1()執行完才能繼續執行fun2()么?為了解決這個問題,后面我們會介紹同步和異步的概念

(2)為什么是單線程

    其實,JavaScript的單線程,與它的用途是有很大關系,我們都知道,JavaScript作為瀏覽器的腳本語言,主要用來實現與用戶的交互,利用JavaScript,我們可以實現對DOM的各種各樣的操作,如果JavaScript是多線程的話,一個線程在一個DOM節點中增加內容,另一個線程要刪除這個DOM節點,那么這個DOM節點究竟是要增加內容還是刪除呢?這會帶來很復雜的同步問題,因此,JavaScript是單線程的

 

二、同步任務和異步任務

(1)為什么會有同步和異步

    因為JavaScript的單線程,因此同個時間只能處理同個任務,所有任務都需要排隊,前一個任務執行完,才能繼續執行下一個任務,但是,如果前一個任務的執行時間很長,比如文件的讀取操作或ajax操作,后一個任務就不得不等着,拿ajax來說,當用戶向后台獲取大量的數據時,不得不等到所有數據都獲取完畢才能進行下一步操作,用戶只能在那里干等着,嚴重影響用戶體驗

    因此,JavaScript在設計的時候,就已經考慮到這個問題,主線程可以完全不用等待文件的讀取完畢或ajax的加載成功,可以先掛起處於等待中的任務,先運行排在后面的任務,等到文件的讀取或ajax有了結果后,再回過頭執行掛起的任務,因此,任務就可以分為同步任務和異步任務

(2)同步任務

    同步任務是指在主線程上排隊執行的任務,只有前一個任務執行完畢,才能繼續執行下一個任務,當我們打開網站時,網站的渲染過程,比如元素的渲染,其實就是一個同步任務

(3)異步任務

    異步任務是指不進入主線程,而進入任務隊列的任務,只有任務隊列通知主線程,某個異步任務可以執行了,該任務才會進入主線程,當我們打開網站時,像圖片的加載,音樂的加載,其實就是一個異步任務

function fun1() {
  console.log(1);
}
function fun2() {
  console.log(2);
}
function fun3() {
  console.log(3);
}
fun1();
setTimeout(function(){
  fun2();
},0);
fun3();

// 輸出
1
3
2

    有了異步,就算fun2()里面是文件的讀取或ajax這種需要耗時的任務,也不怕fun3()要等到fun2()執行完才能執行啦

(4)異步機制

    那么,JavaScript中的異步是怎么實現的呢?那要需要說下回調和事件循環這兩個概念啦

    首先要先說下任務隊列,我們在前面也介紹了,異步任務是不會進入主線程,而是會先進入任務隊列,任務隊列其實是一個先進先出的數據結構,也是一個事件隊列,比如說文件讀取操作,因為這是一個異步任務,因此該任務會被添加到任務隊列中,等到IO完成后,就會在任務隊列中添加一個事件,表示異步任務完成啦,可以進入執行棧啦~但是這時候呀,主線程不一定有空,當主線程處理完其它任務有空時,就會讀取任務隊列,讀取里面有哪些事件,排在前面的事件會被優先進行處理,如果該任務指定了回調函數,那么主線程在處理該事件時,就會執行回調函數中的代碼,也就是執行異步任務啦

    單線程從從任務隊列中讀取任務是不斷循環的,每次棧被清空后,都會在任務隊列中讀取新的任務,如果沒有任務,就會等到,直到有新的任務,這就叫做任務循環,因為每個任務都是由一個事件觸發的,因此也叫作事件循環

    總的來說,JavaScript的異步機制包括以下幾個步驟

 

(1)所有同步任務都在主線程上執行,行成一個執行棧
(2)主線程之外,還存在一個任務隊列,只要異步任務有了結果,就會在任務隊列中放置一個事件
(3)一旦執行棧中的所有同步任務執行完畢,系統就會讀取任務隊列,看看里面還有哪些事件,那些對應的異步任務,於是結束等待狀態,進入執行棧,開始執行
(4)主線程不斷的重復上面的第三步

 

 

三、異步編程

    那么,怎么才能實現異步編程,寫出性能更好的代碼呢,下面有幾種方式

(1)回調函數

    回調函數是實現異步編程最簡單的方法啦,回調函數我們在使用ajax時應該用的很多啦,其實在使用ajax時,我們就用到了異步

 

var req = new XMLHttpRequest();
req.open("GET",url);
req.send(null);
req.onreadystatechange=function(){}

 

    req.send()方法是 AJAX  向服務器發生數據,它是一個異步任務,而 req.onreadystatechange()屬於事件回調,借由瀏覽器的HTTP請求線程發起對服務器的請求,在請求得到響應之后觸發請求完成事件,將回調函數推入事件隊列等待執行

    其實像setTimeout,還有我們平時為元素綁定監聽事件,和上面說的道理也是一樣的

    回調函數的優點是簡單、容易理解和部署,缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合(Coupling),流程會很混亂,而且每個任務只能指定一個回調函數

(2)Promise

    一直以來,JavaScript處理異步都是以callback的方式,在前端開發領域callback機制幾乎深入人心,近幾年隨着JavaScript開發模式的逐漸成熟,CommonJS規范順勢而生,其中就包括提出了Promise規范,Promise完全改變了js異步編程的寫法,讓異步編程變得十分的易於理解,同時Promise也已經納入了ES6,而且高版本的chrome、firefox瀏覽器都已經原生實現了Promise,只不過和現如今流行的類Promise類庫相比少些API

    Promise包括以下幾個規范

  • 一個promise可能有三種狀態:等待(pending)、已完成(fulfilled)、已拒絕(rejected)
  • 一個promise的狀態只可能從“等待”轉到“完成”態或者“拒絕”態,不能逆向轉換,同時“完成”態和“拒絕”態不能相互轉換
  • promise必須實現then方法(可以說,then就是promise的核心),而且then必須返回一個promise,同一個promise的then可以調用多次,並且回調的執行順序跟它們被定義時的順序一致
  • then方法接受兩個參數,第一個參數是成功時的回調,在promise由“等待”態轉換到“完成”態時調用,另一個是失敗時的回調,在promise由“等待”態轉換到“拒絕”態時調用,同時,then可以接受另一個promise傳入,也接受一個“類then”的對象或方法,即thenable對象

    在使用Promise時,我們需要檢測一些瀏覽器是否支持Promise

 

if(typeof(Promise)==="function") {
    console.log("支持");
}
else {
    console.log("不支持");
}

 

    我們可以使用new Promise進行Promise的創建

function wait(time) {
    return new Promise(function(resolve,reject) {
        setTimeout(resolve,time);
    });
}

    這個時候我們就可以使用Promise處理異步任務啦

wait(1000).then(function(){
    console.log(1);
})

    上面這個例子表示1秒后輸出1,同樣的道理,我們可以使用Promise進行更加復雜的操作,關於更多的操作,就不繼續說啦,關於異步的實現,其實還有其它的一些方法,但是因為上面說的這兩種方法用的比較多,所以就只說上面這兩種了

 

 

    

 

 

 

 

 

 

 

 

 

 

  

 

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM