ES6中Promise用法詳解

zero________________發表於2018-08-12

Promise

promise就是個建構函式,引數為一個函式

建構函式一般使用其例項進行操作,如何得到一個建構函式的例項呢?

1、new Promise(引數:函式);

2、引數為一個函式,這個函式同樣也有兩個引數 resolve reject,這兩個引數也是函式

resolve執行後返回promise的成功狀態,

reject執行後返回promise的錯誤狀態

基本使用形式:

    var p = new Promise(function(resolve,reject){
        console.log("結果");
        resolve("結果")
        })
    //以上程式碼將一段同步程式碼封裝成了一個promise例項,promise例項有兩個三個狀態
    //peding   正在請求
    //resolved 成功
    //rejected 失敗
    //這個例項內部呼叫resolve,返回成功狀態,並將結果通過resolve傳遞。
    
    //傳遞出來的結果如何使用呢?
    //下面的程式碼,通過呼叫promise例項上面的then方法來獲得resolve傳遞的資料。
    p.then(function(data){
        console.log(data)
        })

從上面的程式碼可以總結一下promise的特點:

  • 只要一 new ,promise就執行了,並且得到一個promis例項
  • promise例項內部結果通過resolve進行傳遞
  • 通過呼叫promise例項的then方法獲取resolve傳遞的結果;

一般用promise幹什麼呢?

包裝非同步執行程式碼,將其轉化為同步執行的樣式,

比方傳送一個ajax常規方法:

    $.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
        console.log(data);
    })

轉化為Promise如下:

    let p = new Promise((resolve,reject)=>{
        $.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
         resolve(data)
        })
    });
    
    p.then((data)=>{
        //執行程式碼邏輯
        console.log(data)
    })

在實際開發中使用

但是一般在開發中一般不會把new Promise直接暴露在外面,而是封裝成一個函式,上面程式碼修改如下:

    function  fn(){
        let p = new Promise((resolve,reject)=>{
            $.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
                resolve(data)
            })
        });
        return p
    }
    
    fn().then((data)=>{
        console.log(data)
    })

如何學習promise???

根據作用需求/場景來學習,需求如下:

比方一個頁面載入進來,需要傳送5個ajax請求,並需要把結果統一經行處理:

  • 首先大家按照大家已有的知識點來思考一下如何解決呢?

    程式碼如下:
    $(function(){
        var arr = []
        $.get("https://cnodejs.org/api/v1/topics?tab=ask",function(data){
            arr.push(data);
            $.get("https://cnodejs.org/api/v1/topics?tab=job",function(data){
                arr.push(data);
                $.get("https://cnodejs.org/api/v1/topics?tab=good",function(data){
                    arr.push(data);
                    $.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
                        arr.push(data);
                            console.log(arr)
                    })
                })
            })
        })
    })

簡稱 回撥地獄

缺點耗費時間,看圖說話:
這裡寫圖片描述
之前我們可能會用解決方法。

    (function () {
        var count = 0;
        var arr = [];
        function handle() {
            if (count === 4) {
                console.log(arr);
            }
        }
        $.get("https://cnodejs.org/api/v1/topics?tab=good",function(data){
            arr.push(data);
            count++;
            handle()
        })
        $.get("https://cnodejs.org/api/v1/topics?tab=job",function(data){
            arr.push(data);
            count++;
            handle()
        })
        $.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
            arr.push(data);
            count++;
            handle()
        });
        $.get("https://cnodejs.org/api/v1/topics?tab=ask",function(data){
            arr.push(data);
            count++;
            handle()
        });
    
    })();

但是這種依然有個缺點,得寫監控函式,每次回撥都會呼叫監控函式,耗費效能,還有其他方法嗎?

檢視network 中的waterfall;


這裡寫圖片描述

Promise 來了,用promise怎麼實現呢?

程式碼如下:

$(function(){
    // 封裝一個promise;將url提取出來;
    var  p  = function(url){
        return new Promise(function(resolve,reject){
            $.get(url,function(data){
            resolve(data);
            })
        })
    }
    Promise.all([
            p("https://cnodejs.org/api/v1/topics?tab=good"),
            p("https://cnodejs.org/api/v1/topics?tab=share"),
            p("https://cnodejs.org/api/v1/topics?tab=ask"),
            p("https://cnodejs.org/api/v1/topics?tab=job"),
        ]).then(function(result){
            console.log(result);
        })
})

waterfall圖如下:


這裡寫圖片描述

有的時候有這樣的需求,後面的ajax請求依賴前面的ajax請求必須按照順序調動如何實現呢?

前面的回撥是一種解決方案,但是想避免回撥地獄的寫法用promise實現順序呼叫呢?

$(function(){
    // 封裝一個promise;
    var  p  = function(url){
        return new Promise(function(resolve,reject){
            $.get(url,function(data){
            resolve(data);
            })
        })
    }
    var arr = []
    p("https://cnodejs.org/api/v1/topics?tab=ask")
    .then(function(data){
        arr.push(data);
        return p("https://cnodejs.org/api/v1/topics?tab=share")
    }).then(function(data){
        arr.push(data);
        return p("https://cnodejs.org/api/v1/topics?tab=ask")
    }).then(function(data){
        arr.push(data);
        return p("https://cnodejs.org/api/v1/topics?tab=good")
    }).then(function(data){
        arr.push(data);
        console.log(arr);
    })      
})

waterfall執行圖:


這裡寫圖片描述

再看一個應用場景:

請求多個資源,哪個介面先返回,就處理哪個介面的資訊:

一般應用的比較多的是分散式應用,舉例子說明,

四臺 伺服器分別位於 北京 上海 南京 鄭州:畫圖說明

這裡寫圖片描述

這應用到一個race介面,將4.html的js程式碼改成如下:

    $(function(){
        // 封裝一個promise;
        var  p  = function(url){
            return new Promise(function(resolve,reject){
                $.get(url,function(data){
                resolve(data);
                })
            })
        }
        Promise.race([
                p("https://cnodejs.org/api/v1/topics?tab=good"),
                p("https://cnodejs.org/api/v1/topics?tab=share"),
                p("https://cnodejs.org/api/v1/topics?tab=ask"),
                p("https://cnodejs.org/api/v1/topics?tab=job"),
            ]).then(function(result){
                console.log(result);
            })
    })

執行看結果,誰最先返回就列印誰的資料:

再來看一個需求,傳送一個請求,請求超時後返回特定資訊如何實現:

jquery版本:

    $.ajax({
      url:'https://cnodejs.org/api/v1/topics?tab=good',  //請求的URL
      timeout : 1000, //超時時間設定,單位毫秒
      type : 'get',  //請求方式,get或post
      data :{},  //請求所傳引數,json格式
      dataType:'json',//返回的資料格式
      success:function(data){ //請求成功的回撥函式
        alert("成功");
      },
        error:function(){
            console.log("超時了")
        }
    });
    
    //設定timeout的時間,超時後觸發error函式。

Promise版本如何實現呢?

  • 封裝兩個Promise
    • 一個promise裡面封裝ajax請求
    • 一個Promise封裝定時器
  • 呼叫Promise類的race方法
  • 鏈式呼叫then方法獲得執行結果。

注意:Promise.race的引數為一個陣列,陣列每一項都是promise例項物件。

Promise.race的返回結果為一個Prmise例項,只不過這個例項只能是最先執行resolve的那個promise。

    $(function(){
        // 封裝一個promise;
        var  p  = function(url){
            return new Promise(function(resolve,reject){
                $.get(url,function(data){
                resolve(data);
                })
            })
        }
    
    
        var p2 = function(){
            return new Promise(function(resolve,reject){
                setTimeout(function(){
                    resolve('請求超時了')
                },100)
            })
        }
    
        Promise.race([
                p("https://cnodejs.org/api/v1/topics?tab=good"),
                p2()
            ]).then(function(result){
                console.log(result);
            })
        })

promise捕獲錯誤的使用.

如何捕獲promise的錯誤呢?在promise的呼叫鏈的最後呼叫catch函式,一旦promise呼叫鏈中有reject執行,promise就會終止執行直接進入catch函式,否則catch不會執行。

    $(function(){
        // 封裝一個promise;
        var  p  = function(url){
            return new Promise(function(resolve,reject){
                $.get(url,function(data){
                resolve(data);
                })
            })
        }
    
    
    
        // P2的另一種形態:
        // 如果是reject會直接跳到catch裡面去,如果是resolve,會接著執行,不會跳躍
        var p2 = function(){
            return new Promise(function(resolve,reject){
                setTimeout(function(){
                    reject("err");
                },100)
            })
        }
    
        Promise.race([
                p("https://cnodejs.org/api/v1/topics?tab=good"),
                p2()
            ]).then(function(result){
                console.log(result);
            }).then(function(){
                console.log("我執行了")
            }).catch(function(data){
                console.log(data);
            })
        })

javascript的巨集任務和微任務

看一道面試題:


    setTimeout(
    	function(){
    		console.log('1')
    	}
    );
     
    new Promise(
	    function(resolve, reject){
		    resolve()
	        console.log('2');
	    }).then(function(){
	        console.log('3')
	    });
     
    console.log('4');

請問這段程式碼的執行結果是什麼?

回憶javascript的非同步機制,如圖:

這裡寫圖片描述

這張圖不是很完整,在javascript中分為巨集任務(macrotask)和微任務(micro-task);

巨集任務(macrotask)包括 cript(整體程式碼), setTimeout, setInterval, setImmediate, I/O, UI rendering

micro-task: process.nextTick, Promises(這裡指瀏覽器實現的原生 Promise)
;

相關文章