理解ES6中的Promise


一、Promise的作用

在ajax請求數據的過程中,我們可以異步拿到我們想要的數據,然后在回調中做相應的數據處理。
這樣做看上去並沒有什么麻煩,但是如果這個時候,我們還需要做另外一個ajax請求,這個新的ajax請求的其中一個參數,得從上一個ajax請求中獲取,這個時候我們就需要在回調函數中再寫一個異步請求,然后在這個異步函數的回調函數里在寫相應的數據處理。要是連續嵌套個三四層,往往就很惡心了。
寫起來就像下面這樣:

$.ajax({
    type:'get',
    url:'url_1', 
    data: 'data'
    success : function(res){
        //相應的數據處理
        var data = res.data
        $.ajax({
            type:'get',
            url:'url_2', 
            data: data
            success : function(res){
                //相應的數據處理
                $.ajax({
                    type:'get',
                    url:'url_3', 
                    data: data
                    success : function(res){
                        //相應的數據處理
                    }
                })
            }
        })
    }
})

在這種情況下Promise就能發揮它的威力了;

二、來一個實例

先不談語法,下面先來一個實例,建立感性的認識

<!DOCTYPE html>
<html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
    </head>
    <body>
    </body>
    <script>
        function a(data){
            return new Promise(function(resolve,reject){
                console.log("我是從上一個回調函數里傳過來的數據",data) ;
                $.ajax({
                    type:'post',
                    dataType: 'jsonp',
                    url:'http://api.money.126.net/data/feed/0000001,1399001',   //jsonp跨域調用上證與深證的股票指數
                    data:{
                    },
                    success : function(res){
                        console.log(res) ;
                        resolve(res) ;
                    },
                    error:function(res){
                        console.log("Error:") ;
                        console.log(res) ;
                        reject(res) ;
                    }
                })
            });
        }
        function b(data){
            return new Promise(function(resolve,reject){
                console.log("我是從上一個回調函數里傳過來的數據",data) ;
                $.ajax({
                    type:'post',
                    dataType: 'jsonp',
                    url:'https://api.douban.com/v2/movie/top250',  //跨域調用豆top250的電影
                    success : function(res){
                        console.log(res) ;
                        resolve(res) ;
                    },
                    error:function(res){
                        console.log("Error:") ;
                        console.log(res) ;
                        reject(res)
                    }
                })
            });
        }
        a().then(b).then(a).then(b).catch(function(a){console.log("final Error:",a)}) ;
    </script>
</html>

打印結果如下所示:
Alt text

可以發現,Promise 通過簡單的鏈式調用就能得到之前多層回調才能達成的效果;而且從代碼的結構來看,有效地減小了各個請求之間的耦合;

三、深入Promise

別的不談,先打印一下 Promise , console.dir(Promise) , 看看它究竟是哪號人物:

Alt text

原來 Promise 本身是一個構造函數,自己身上有 allrejectresolve 這幾個的方法,在其 prototype 上有 thencatch 這兩個方法。那么用Promise new出來的對象也會有 thencatch 這兩個方法。

四、注意上面實例中的resolve與reject

1、我們發現,在 new Promise(function(resolve,reject){}) 里傳了兩個方法 resolvereject 作為參數,這兩個方法通常會在函數的回調里被用到。一旦執行到resolve() 或者 reject() ,那么這個函數會停止執行,然后觸發后面的 then() 或者 catch() 方法。准確一點來說,執行到resolve() 會觸發 then() 方法,執行到 reject() 會觸發 catch() 方法。

2、resolvereject 方法里可以傳入參數 ,就像 resolve(data)reject(data) 。 如果這樣做 ,那么在后面的 then() 或者 catch() 里傳入一個帶參數的函數 , 就像 then(function(data){}) 或者 catch(function(data){}) , 就能得到 data 的數據 。

3、說的再專業一些,Promise 對象有三種狀態,他們分別是:

  • pending: 等待中,或者進行中,表示還沒有得到結果
  • resolved(Fulfilled): 已經完成,表示得到了我們想要的結果,可以繼續往下執行
  • rejected: 也表示得到結果,但是由於結果並非我們所願,因此拒絕執行

這三種狀態不受外界影響,而且狀態只能從 pending 改變為 resolved 或者rejected ,並且不可逆。在 Promise 對象的構造函數中,resolvereject 就是用來處理Promise的狀態變化。
一般來說,調用 resolvereject 以后,Promise 的使命就完成了,后繼操作應該放到 then 或者 catch 方法里面,而不應該直接寫在 resolve()reject() 的后面 (事實的情況是,resolve()reject() 的后面的代碼也不會執行)

五、new Promise() 里的函數是立刻執行的

需要注意的的是,new Promise() 里的函數是立刻執行的 ,也就是說 ,當你執行下面這段代碼時,就已經開始執行異步請求了:

<script>
new Promise(function(resolve,reject){
    $.ajax({
        type:'post',
        dataType: 'jsonp',
        url:'http://api.money.126.net/data/feed/0000001,1399001',
        data:{
        },
        success : function(res){
            console.log(res) ;
            resolve(res) ;
        },
        error:function(res){
            reject(res) ;
        }
    })
});
</script>

這也是為什么,在上面第二段的實例中,需要用 a()b() 函數把 new Promise() 給包起來

六、then() 函數的返回值一定是 Promise 對象

還需要注意的的是,then() 函數的返回值一定是 Promise 對象,哪怕手動 return 一個值也無濟於事,如下面的代碼,照樣能運行成功:

a().then(function (){console.log("hello");return 1}).then(b) ;

這也解釋了為什么我們可以鏈式調用 then() 函數。

七、Promise.all()Promise.race()的用法

想要從兩個不同的 ajax 請求里分別獲得信息,這兩個任務是可以並行執行的,就可以用 Promise.all() 實現:

<script>
var p1 = function(){
    return new Promise(function (resolve, reject) {
        setTimeout(resolve, 500, 'P1');
    });
} ;
var p2 = function(){
    return new Promise(function (resolve, reject) {
        setTimeout(resolve, 1000, 'P2');
    });
} ;
// 同時執行p1和p2,並在它們都完成后執行then
var start = function(){
    Promise.all([p1(), p2()]).then(function (results) {
        console.log(results); // 獲得一個Array: ['P1', 'P2']
    });
}
</script>

有些時候,多個異步任務是為了容錯。比如,分別發兩個不同的 ajax 請求讀取用戶的個人信息,只需要獲得先返回的結果即可,這種情況下,就可以用Promise.race() 實現:

<script>
var p1 = function(){
    return new Promise(function (resolve, reject) {
        setTimeout(resolve, 500, 'P1');
    });
} ;
var p2 = function(){
    return new Promise(function (resolve, reject) {
        setTimeout(resolve, 1000, 'P2');
    });
} ;
var start = function(){
    Promise.all([p1(), p2()]).then(function (results) {
        console.log(results); // 'P1'
    });
}
</script>

由於 p1 執行較快,Promisethen() 將獲得結果 'P1'p2 仍在繼續執行,但執行結果將被丟棄。

如果我們組合使用Promise,就可以把很多異步任務以並行和串行的方式組合起來執行。

參考文獻:
阮一峰ES6入門
廖雪峰的官方網站
sitepoint
"呂大豹"的博客園
原文地址:
王玉略的個人網站


免責聲明!

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



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