天天看點

ES6中Promise用法詳解

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)
                    })
                })
            })
        })
    })
           

簡稱 回調地獄

缺點耗費時間,看圖說話:

ES6中Promise用法詳解

之前我們可能會用解決方法。

(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;

ES6中Promise用法詳解

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圖如下:

ES6中Promise用法詳解

有的時候有這樣的需求,後面的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執行圖:

ES6中Promise用法詳解

再看一個應用場景:

請求多個資源,哪個接口先傳回,就處理哪個接口的資訊:

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

四台 伺服器分别位于 北京 上海 南京 鄭州:畫圖說明

ES6中Promise用法詳解

這應用到一個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的異步機制,如圖:

ES6中Promise用法詳解

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

宏任務(macrotask)包括 cript(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering

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

;

繼續閱讀