JS進階 | 分析JS中的異步操作


 寫在前面

  JS因為是單線程的,所以在執行事務的時候,往往會因為某個事務的延遲,而導致服務器假死,這時候異步編程就顯的格外重要,但是異步編程一般理解為回調函數callback,典型的就是node,回調函數的層層嵌套又導致程序過於冗余,因為閉包的存在,導致了內存的泄露或者誤改上一層回調函數的參數,於是又有一個疑問,能不能用同步的方式去寫異步,ES6的promise就是以同步流程的方式寫出異步操做,但是piomise原生操作寫起來比較長,能不能簡介操作promise,ES7就又出現async/await的概念,與async/await同等語法糖作用的有依賴co庫的generator函數,generator函數也是用來解決異步操作,不過得依賴co庫,co 函數庫是著名程序員 TJ Holowaychuk 於2013年6月發布的一個小工具,用於generator函數的自動執行。

  好了,那就羅列出js的異步操作

  1、回調函數

    ajax典型的異步操作,利用XMLHttpRequest,回調函數獲取服務器的數據傳給前台  

    之前也分析過ajax(飛機票

    回調函數的含義就是耗時任務f1中執行f2,f1不會堵塞住,而是先執行f2,再延遲執行f1

  2、事件監聽

    addEventListener

    當監聽事件發生時,先執行回調函數,再對監聽事件進行改寫

  3、觀察者模式,也叫訂閱發布模式

    多個觀察者可以訂閱同一個主題,主題對象改變時,主題對象就會通知這個觀察者

    其中步驟包括,訂閱、發布、退訂;先訂閱(subscribe)一個主題對象,根據主題對象發布(publish)內容,期間也退訂(unsubscribe)主題對象,一旦退訂就無法再次發布

    可以把訂閱一個主題對象理解成監聽一個事件

    觀察者模式的一個特點就是一旦主題事件一改變,就會通知整個觀察者;觀察者模式還可以計算出訂閱事件的個數

  4、promise

    上面也提到了,promise是異步編程的解決方案,是一種容器,保存着異步操作的結果,可以把異步函數以同步函數的形式寫出來

    promise第一個特點:對象狀態不受外界影響,有三個狀態pending(),fulfilled(),rejected(),只有異步操作才會更改這個狀態,其他操作無法改變這個狀態

    promise第二個特點:一旦狀態改變,pending->fulfilled或pending->rejected,狀態就會凝固住,稱為resolve,通過promise的回調函數可以立即得到這個結果,與事件監聽不同,一旦事件錯誤,就無法再次監聽

    promise第三個特點:避免了回調函數的層層嵌套,實際上寫promise時,雖然沒有回到函數的層層嵌套,但是又有then的嵌套,這個又有新的解決方法

    promise第一個缺點:一旦promise建立,就會立即執行,無法中途停止

      舉個例子,假如去淘寶買東西,去看上一個東西,想要買,已經點擊確定購買了,但是你又cancle了,突然你發現工資發了,你又發送一個請求說又要買了,服務器接收到了你的第一個異步請求,你又有異步請求,異步請求又不能停止,求服務器陰影面積?

    promise的第二個缺點:promise不設置回調函數,拋出的錯誤無法在外部捕獲

    piomise的第三個缺點:處於pending狀態,無法知道進展到哪個狀態

    下面一個經典的案例:用promise寫出ajax,就體現了promise的特點

             function getJSON(url){
                var promise = new Promise(function(resolve,reject){
                var xhr =  new XMLHttpRequest();
                xhr.open("get",url);
                xhr.onreadystatechange=ajax;
                xhr.responseType = "json";
                xhr.setRequestHeader("Accept", "application/json");
                xhr.send();
                function ajax(response){
                    if (this.readyState !=4) {
                        return;
                    }
                    if (this.status ==200) {
                            resolve(this.response)
                    }else{
                        reject(new Error(this.status.responseText))
                    }
                    
                }
            })
            return promise;
        }
        getJSON("/new2").then(function(value){
            console.log(value);
        },function(error){
            console.log(error);
        })

 

   5、es7語法糖async/await

     async異步函數是promise的完成狀態,async函數直接then去獲取狀態改變值,catch來獲取錯誤

     await只允許在async內部使用,就是async異步函數內部想要繼續then,就可以采用await異步函數,await異步函數是內部的async異步函數

       async極大精簡了promise的操作   

async  function asyncFn(){
        return 123;
    }
    asyncFn()
        .then(x => {console.log(x)})
        .catch(err => console.log(error))

    這是未精簡的promise操作,功能是把最終結果return最外層的promise,但是 用到了多層嵌套,比較復雜

    const fn = () => {
        return getJSON()
            .then(data =>{
                if(data.params){
                    return otherGetJSON(data).then(otherdata =>{
                        console.log(otherdata);
                        return otherdata;
                    })
                }else{
                    console.log(data)
                }
            })
    }

    而async異步函數,直接去return出結果就行,這就是await是async內部async異步函數

    const fn = async () => {
        const data = await getJSON();
        if(data.params){
            const otherdata = await otherGetJSON(data);
            console.log(otherdata);
            return otherdata;
        }else{
            console.log(data)
            return data;
        }
    }
    6、co庫的generator函數

      generator函數是一個異步函數,只有異步操作有結果才會交還執行權

      generator用到了ES6的遍歷Iterator的概念,創建一個指針對象,指向數據結構的起始位置,每次next指向下一個指針結構成員,直至指向的下一個結構成員為undefined

      generator概念就是,每次遍歷讀用next方法,內部指針從結構頭部指向下一個結構成員,直至下一個結構成員為undefined,遇到yield或return時會返回value和done參數,value表示yield或return的值,done表示是否結束 

function * gen(x){
      var y = yield x +2;      
      return y;  
}
var g = gen(1);
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: undefined, done: true }

      yield是遍歷的停止標志

      而generator中的yield*表示 yield* 后面跟着一個可遍歷的結構

 寫在后面

    最近的review,讓我對js又有新的認識,js異步操作是js的核心所在,也是js迷人的地方,async/await和generator都是promise的語法糖,我覺得沒有優劣之分,可能道行太淺,還需刷道,希望業界前輩多多指責我,讓我進步更快。

    好了,今天的交流就結束了,下次復習js定時器的內存分配等。


免責聲明!

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



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