ES6 Promise介紹

遊麥前端發表於2018-01-05
  • 作者:Leo曉華

回撥地獄

由於 JavaScript 的單執行緒性質,我們必須等待上一個請求返回結果後才能處理下一步,如下:

$.ajax({ 
  url: "/step1", 
  success: function(){
      $.ajax({ 
        url: "/step2", 
        success: function(){
            $.ajax({ 
              url: "/step3", 
              success: function(){

              }
            });
        }
      });
  }
});
複製程式碼

這種回撥地獄巢狀層級多了,程式碼結構就容易變得很不直觀,可讀性比較差。
Promise 是 ES6原生支援的,把原來巢狀的回撥改為了級聯的方式。

瞭解Promise

Promise 可以簡單理解為一個事務,這個事務存在三種狀態:

ES6 Promise介紹

  • 已經完成了 resolved(完成態)
  • 因為某種原因被中斷了 rejected(失敗態)
  • 初始狀態 pending(未完成)

注意,這種狀態的改變只會出現從未完成態向完成態或失敗態轉化,不能逆反。完成態和失敗態不能互相轉化,而且,狀態一旦轉化,將不能更改。

只有非同步操作的結果可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思是承諾,表示其他手段無法改變。

例子:

var p = new Promise(function (resolve, reject) {
    if(/* 非同步操作成功 */){
        resolve(ret);
    } else {
        reject(error);
    }
});
複製程式碼

在宣告一個Promise物件例項時,我們傳入的匿名函式引數中:
resolve 就對應著完成態之後的操作
reject 對應著失敗態之後的操作

Promise的then方法

p.then(function (value) {
    // 完成態,value是上面resolve傳入的值
}, function (error) {
    // 失敗態,error是上面reject傳入的值
});
複製程式碼

then()方法傳遞的兩個引數中:

  • 第一個引數(函式)對應著完成態的操作,也就是resolve時呼叫

  • 第二個引數(函式)對應著失敗態的操作,也就是reject時呼叫

  • 第二個引數可以沒有

多個promise鏈式

例子1:

var p1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('p1'), 1000);
});

p1.then( ret => {
    console.log(ret); //p1
    return'then1';
}).then( ret => {
    console.log(ret); //then1
    return'then2';
}).then( ret => {
    console.log(ret); //then2
});
複製程式碼

在 resolve 之前,promise 的每一個 then 都會將回撥函式壓入佇列,resolve 後,將 resolve 的值送給佇列的第一個函式,第一個函式執行完畢後,將執行結果再送入下一個函式,依次執行完佇列。一連串下來,一氣呵成,沒有絲毫間斷。
鏈式中的then方法(第二個開始),它們的resolve中的引數是什麼?答案就是前一個then()中resolve的return語句的返回值。

例子2:

    step1:function(){
        var promise = new Promise(function(resolve,reject){
             $.ajax({
                url:"/step1",
                    success:function(data){
                        resolve(data)//在非同步操作成功時呼叫
                    }
            });
        })
        return promise;
    },
    step2:function(val){
        var promise = new Promise(function(resolve,reject){
            $.ajax({
                url:"/step2",
                    data:val,   //來自step1的引數
                    success:function(data){
                        resolve(data)//在非同步操作成功時呼叫
                    }
                });
            })
            return promise;
        }
    step1()
    .then(data => return step2(data))  //step1的結果傳給step2作為引數
    .then(data => console.log(data))
複製程式碼

錯誤處理

方法1:由then的第二個處理函式處理錯誤

var p = new Promise(function (resolve, reject) {
    // ...
    if(/* 非同步操作成功 */){
        resolve(ret);
    } else {
        reject(error);
    }
});

p.then(function (value) {
    // 完成態
}, function (error) {
    // 失敗態
});

複製程式碼

方法2:鏈式錯誤處理 (catch 方法)

var p = new Promise(function (resolve, reject) {
    // ...
    if(/* 非同步操作成功 */){
        resolve(ret);
    } else {
        reject(error);
    }
});

p.then(function (value) {
    // 完成態
}).then(function (value) {
    // 完成態
}).catch( err => {// 可以捕抓到前面的出現的錯誤。
    console.log(err.toString());
});
複製程式碼

如果第一個then報錯,第二個then不會執行。

一個活動專案的例子:

//查使用者資訊
querymystate(){
    var promise = new Promise(function(resolve,reject){
        utils.get('/querystate', {
          raffle_code:xxx,
        }, (rst) => {
          if (rst.return_code === 0) { //成功獲取使用者資訊
                resolve(rst.data);                        
          } else { //獲取失敗
              reject(rst.error_message);    
          }
        })    
    })    
    return promise;
},
//我要領獎,必須先獲取使用者資訊
getAward(){
    querymystate()
    .then(data => {
      //領獎處理
    }).catch(ErrMsg => {
      //彈出錯誤提示ErrMsg
    })
}
複製程式碼

Promise.all()

Promise.all()方法用於將多個Promise例項,包裝成一個新的Promise例項,例如:

var p = Promise.all([p1, p2, p3]);
複製程式碼

新的Promise例項p的狀態由p1, p2, p3決定:

  • 當p1, p2, p3的狀態都為完成態時,p為完成態。

  • p1, p2, p3中任一一個狀態為失敗態,則p為失敗態。

例子:

let a = new Promise((resolve, reject) => { 
    setTimeout(() => { 
      resolve(2) 
    }, 2000) 
  }) 

let b = new Promise((resolve, reject) => { 
    setTimeout(() => { 
      resolve(3) 
    }, 2000) 
  }) 

Promise.all([a, b])
.then( (ret) => console.log(ret)) //2秒後,注意這裡返回的是陣列 [2,3]
.catch( err => console.log(err.toString()));
複製程式碼

Promise.race()

race意思是賽跑,看誰先到。只要p1, p2, p3中任意一個例項率先改變狀態,則p的狀態就跟著改變,而且狀態由率先改變的例項決定。

let a = new Promise((resolve, reject) => { 
    setTimeout(() => { 
      resolve(2) 
    }, 3000) 
  }) 

let b = new Promise((resolve, reject) => { 
    setTimeout(() => { 
      resolve(3) 
    }, 2000) 
  }) 

Promise.race([a, b])
.then( (ret) => console.log(ret)) //2秒後顯示3
.catch( err => console.log(err.toString()));
複製程式碼

相關文章