ES6 中 Promise 詳解

博客地址:https://ainyi.com/16ajax

 

Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。從語法上說,Promise 是一個對象,從它能夠獲取異步操做的消息。json

Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。api

Promise對象有如下兩個特色:數組

(1)對象的狀態不受外界影響。Promise對象表明一個異步操做,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是「承諾」,表示其餘手段沒法改變。promise

(2)一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種可能:從Pending變爲Resolved和從Pending變爲Rejected。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會當即獲得這個結果。這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的。異步

有了Promise對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操做更加容易。函數

簡單來講,Promise 就是用同步的方式寫異步的代碼,用來解決回調問題

 

then()方法

then 方法就是把原來的回調寫法分離出來,在異步操做執行完後,用鏈式調用的方式執行回調函數。工具

而 Promise 的優點就在於這個鏈式調用。咱們能夠在 then 方法中繼續寫 Promise 對象並返回,而後繼續調用 then 來進行回調操做。url

可有兩個參數,第一個是成功 resolve 調用的方法,第二個是失敗 reject 調用的方法

下面作一個買筆寫做業上交的演示,它們是層層依賴的關係,下一步的的操做須要使用上一部操做的結果。(這裏使用 setTimeout 模擬異步操做),正式開發能夠用 ajax 異步spa

 1         //買筆
 2         function buy(){
 3             console.log("開始買筆");
 4             var p = new Promise(function(resolve,reject){
 5                 setTimeout(function(){
 6                     console.log("買了筆芯");
 7                     resolve("數學做業");
 8                 },1000);
 9             });
10             return p;
11         }
12         //寫做業
13         function work(data){
14             console.log("開始寫做業:"+data);
15             var p = new Promise(function(resolve,reject){
16                 setTimeout(function(){
17                     console.log("寫完做業");
18                     resolve("做業本");
19                 },1000);
20             });
21             return p;
22         }
23 
24         function out(data){
25             console.log("開始上交:"+data);
26             var p = new Promise(function(resolve,reject){
27                 setTimeout(function(){
28                     console.log("上交完畢");
29                     resolve("得分:A");
30                 },1000);
31             });
32             return p;
33         }
34         /* 不建議使用這種方式
35         buy().then(function(data){
36             return work(data);
37         }).then(function(data){
38             return out(data);
39         }).then(function(data){
40             console.log(data);
41         });*/
42 
43         //推薦這種簡化的寫法
44         buy().then(work).then(out).then(function(data){
45             console.log(data);
46         });

正式開發用ajax異步:

 1         var promise = new Promise(function(resolve,reject){
 2             $.ajax({
 3                 url:'/api/poisearch.json',
 4                 method:'get',
 5                 datatype:'json',
 6                 success:(res) =>{
 7                     resolve(res)
 8                 },
 9                 error:(err)=>{
10                     reject(err)
11                 }
12             });
13         });
14 
15         promise.then(function(res){
16             return res.data
17         }).then(function(data){
18             return data.result;
19         }).then(function(result){
20             console.log(result)
21         });
22 
23         //推薦使用箭頭函數簡寫成,極大提高了代碼的簡潔性和可讀性
24         promise.then(res => res.data).then(data => data.result).then(result => console.log(result));

 

reject()方法:

上面樣例咱們經過 resolve 方法把 Promise 的狀態置爲完成態(Resolved),這時 then 方法就能捕捉到變化,並執行「成功」狀況的回調。

而 reject 方法就是把 Promise 的狀態置爲已失敗(Rejected),這時 then 方法執行「失敗」狀況的回調(then 方法的第二參數)

 1         function rebuy(){
 2             console.log("開始買筆");
 3             var p = new Promise(function(resolve,reject){
 4                 setTimeout(function(){
 5                     console.log("買筆失敗");
 6                     reject("沒帶夠錢");
 7                 },1000);
 8             });
 9             return p;
10         }
11 
12         function rework(data){
13             console.log("開始寫做業:"+data);
14             var p = new Promise(function(resolve,reject){
15                 setTimeout(function(){
16                     console.log("寫完做業");
17                     resolve("做業本");
18                 },1000);
19             });
20             return p;
21         }
22         
23         rebuy().then(rework,function(data){
24             console.log(data);
25         });

catch()方法:

1. 它能夠和 then 的第二個參數同樣,用來指定 reject 的回調

 1        function rebuy(){
 2             console.log("開始買筆");
 3             var p = new Promise(function(resolve,reject){
 4                 setTimeout(function(){
 5                     console.log("買筆失敗");
 6                     reject("沒帶夠錢");
 7                 },1000);
 8             });
 9             return p;
10         }
11 
12         function rework(data){
13             console.log("開始寫做業:"+data);
14             var p = new Promise(function(resolve,reject){
15                 setTimeout(function(){
16                     console.log("寫完做業");
17                     resolve("做業本");
18                 },1000);
19             });
20             return p;
21         }
22 
23         rebuy().then(rework).catch(function(data){
24             console.log(data);
25         });    

2. 它的另外一個做用是,當執行 resolve 的回調(也就是上面 then 中的第一個參數)時,若是拋出異常了(代碼出錯了),那麼也不會報錯卡死 js,而是會進到這個 catch 方法中。

 1         function buy(){
 2             console.log("開始買筆");
 3             var p = new Promise(function(resolve,reject){
 4                 setTimeout(function(){
 5                     console.log("買了筆芯");
 6                     resolve("數學做業");
 7                 },1000);
 8             });
 9             return p;
10         }
11 
12         function work(data){
13             console.log("開始寫做業:"+data);
14             var p = new Promise(function(resolve,reject){
15                 setTimeout(function(){
16                     console.log("寫完做業");
17                     resolve("做業本");
18                 },1000);
19             });
20             return p;
21         }
22 
23         buy().then(function(data){
24             throw new Error("買了壞的筆芯");
25             work(data);
26         }).catch(function(data){
27             console.log(data);
28         });

all()方法:

Promise 的 all 方法提供了並行執行異步操做的能力,而且在全部異步操做執行完後才執行回調。

好比下面代碼,兩個個異步操做是並行執行的,等到它們都執行完後纔會進到 then 裏面。同時 all 會把全部異步操做的結果放進一個數組中傳給 then。

 1         //買做業本
 2         function cutUp(){
 3             console.log('挑做業本');
 4             var p = new Promise(function(resolve, reject){ //作一些異步操做
 5                 setTimeout(function(){
 6                     console.log('挑好購買做業本');
 7                     resolve('新的做業本');
 8                 }, 1000);
 9             });
10             return p;
11         }
12          
13         //買筆
14         function boil(){
15             console.log('挑筆芯');
16             var p = new Promise(function(resolve, reject){ //作一些異步操做
17                 setTimeout(function(){
18                     console.log('挑好購買筆芯');
19                     resolve('新的筆芯');
20                 }, 1000);
21             });
22             return p;
23         }
24 
25         Promise.all([cutUp(),boil()]).then(function(results){
26             console.log("寫做業的工具都買好了");
27             console.log(results);
28         });

 

race()方法:

race 按字面解釋,就是賽跑的意思。race 的用法與 all 同樣,只不過 all 是等全部異步操做都執行完畢後才執行 then 回調。而 race 的話只要有一個異步操做執行完畢,就馬上執行 then 回調。

注意:其它沒有執行完畢的異步操做仍然會繼續執行,而不是中止。

這裏咱們將上面樣例的 all 改爲 race

1         Promise.race([cutUp(), boil()]).then(function(results){
2             console.log("哈哈,我先買好啦");
3 console.log(results); 4 });

 

race 使用場景不少。好比咱們能夠用 race 給某個異步請求設置超時時間,而且在超時後執行相應的操做。

請求某個圖片資源

 1        function requestImg(){
 2             var p = new Promise(function(resolve, reject){
 3             var img = new Image();
 4             img.onload = function(){
 5                resolve(img);
 6             }
 7             img.src = 'xxxxxx';
 8             });
 9             return p;
10         }
11          
12         //延時函數,用於給請求計時
13         function timeout(){
14             var p = new Promise(function(resolve, reject){
15                 setTimeout(function(){
16                     reject('圖片請求超時');
17                 }, 5000);
18             });
19             return p;
20         }
21          
22         Promise.race([requestImg(), timeout()]).then(function(results){
23             console.log(results);
24         }).catch(function(reason){
25             console.log(reason);
26         });
27         //上面代碼 requestImg 函數異步請求一張圖片,timeout 函數是一個延時 5 秒的異步操做。咱們將它們一塊兒放在 race 中賽跑。
28         //若是 5 秒內圖片請求成功那麼便進入 then 方法,執行正常的流程。
29         //若是 5 秒鐘圖片還未成功返回,那麼則進入 catch,報「圖片請求超時」的信息。

 博客地址:https://ainyi.com/16

相關文章
相關標籤/搜索