ES6 - Promise



Promise 語法

與 Promise 相關的語法如下所示,其中,p 是 Promise 物件,resolve 是 Promise 成功執行時應呼叫的函式,reject 是 Promise 遇到錯誤時應呼叫的函式。

let p = new Promise(function(resolve,reject){
   let workDone = true; // some time consuming work
      if(workDone){
      //invoke resolve function passed
      
	  resolve('success promise completed')
   }
   else{
      reject('ERROR , work could not be completed')
   }
})

示例

下面給出的示例展示了一個函式 add_positivenos_async(),該函式非同步地新增兩個數字。如果傳遞正值,則 Promise 被 resolved。如果傳遞負值,則 Promise 被 rejected。

<script>   
   function add_positivenos_async(n1, n2) {
      let p = new Promise(function (resolve, reject) {
         if (n1 >= 0 && n2 >= 0) {
            //do some complex time consuming work
            resolve(n1 + n2)
         }
         else
            reject('NOT_Postive_Number_Passed') 
         })
         return p;
   }

   add_positivenos_async(10, 20)
      .then(successHandler) // if promise resolved
      .catch(errorHandler);// if promise rejected

   add_positivenos_async(-10, -20)
      .then(successHandler) // if promise resolved
      .catch(errorHandler);// if promise rejected

   function errorHandler(err) {
      console.log('Handling error', err)
   }
   function successHandler(result) {
      console.log('Handling success', result)
   }

   console.log('end')
</script> 

以上程式碼的輸出將如下所示:

end
Handling success 30
Handling error NOT_Postive_Number_Passed

Promise 鏈式呼叫

當我們需要依次執行一系列非同步任務時,可以使用Promise 鏈式呼叫。當一個 Promise 依賴於另一個 Promise 的結果時,就會進行鏈式呼叫。這在下面的示例中顯示

示例

在下面的示例中,add_positivenos_async() 函式非同步地新增兩個數字,如果傳遞負值則被拒絕。當前非同步函式呼叫的結果作為引數傳遞給後續的函式呼叫。注意每個then()方法都有一個返回值。

<script>   
   function add_positivenos_async(n1, n2) {
      let p = new Promise(function (resolve, reject) {
         if (n1 >= 0 && n2 >= 0) {
            //do some complex time consuming work
            resolve(n1 + n2)
         }
         else
            reject('NOT_Postive_Number_Passed')
      })
      return p;
   }

   add_positivenos_async(10,20)
   .then(function(result){
      console.log("first result",result)
      return add_positivenos_async(result,result)
   }).then(function(result){
   console.log("second result",result)
      return add_positivenos_async(result,result)
   }).then(function(result){
      console.log("third result",result)
   })

   console.log('end')
</script> 

以上程式碼的輸出將如下所示:

end
first result 30
second result 60
third result 120

下面將詳細討論 Promise 物件的一些常用方法:

promise.all()

此方法可用於聚合多個 Promise 的結果。

語法

promise.all() 方法的語法如下所示,其中,iterable 是一個可迭代物件。例如陣列。

Promise.all(iterable);

示例

下面給出的示例執行了一個非同步運算元組 [add_positivenos_async(10,20),add_positivenos_async(30,40),add_positivenos_async(50,60)]。當所有操作完成後,Promise 將被完全 resolved。

<script>   
   function add_positivenos_async(n1, n2) {
      let p = new Promise(function (resolve, reject) {
         if (n1 >= 0 && n2 >= 0) {
            //do some complex time consuming work
            resolve(n1 + n2)
         }
         else
            reject('NOT_Postive_Number_Passed')
      })

      return p;
   }
   //Promise.all(iterable)

Promise.all([add_positivenos_async(10,20),add_positivenos_async(30,40),add_positivenos_async(50,60)])
   .then(function(resolveValue){
      console.log(resolveValue[0])
      console.log(resolveValue[1])
      console.log(resolveValue[2])
      console.log('all add operations done')
   })
   .catch(function(err){
      console.log('Error',err)
   })
   console.log('end')
</script> 

以上程式碼的輸出如下所示:

end
30
70
110
all add operations done

promise.race()

此函式接受一個 Promise 陣列,並返回第一個已完成的 Promise。

語法

promise.race() 函式的語法如下所示,其中,iterable 是一個可迭代物件。例如陣列。

Promise.race(iterable)

示例

下面給出的示例接受一個非同步運算元組 [add_positivenos_async(10,20),add_positivenos_async(30,40)]

只要任何一個加法操作完成,Promise 就會被 resolved。Promise 不會等待其他非同步操作完成。

<script>   
   function add_positivenos_async(n1, n2) {
      let p = new Promise(function (resolve, reject) {
         if (n1 >= 0 && n2 >= 0) {
            //do some complex time consuming work
            resolve(n1 + n2)
         } else
            reject('NOT_Postive_Number_Passed')
      })

      return p;
   }

   //Promise.race(iterable)
   Promise.race([add_positivenos_async(10,20),add_positivenos_async(30,40)])
   .then(function(resolveValue){
      console.log('one of them is done')
      console.log(resolveValue)
   }).catch(function(err){
      console.log("Error",err)
   })

   console.log('end')
</script> 

以上程式碼的輸出如下所示:

end
one of them is done
30

Promise 是在 JavaScript 中實現非同步程式設計的一種簡潔方法(ES6 新特性)。在 Promise 之前,使用回撥函式來實現非同步程式設計。讓我們首先了解什麼是非同步程式設計及其使用回撥函式的實現。

理解回撥函式

函式可以作為引數傳遞給另一個函式。這種機制稱為回撥函式。回撥函式在事件中很有用。

以下示例將幫助我們更好地理解此概念。

<script>   
   function notifyAll(fnSms, fnEmail) {   
      console.log('starting notification process');   
      fnSms();   
      fnEmail();   
   }   
   notifyAll(function() {   
      console.log("Sms send ..");   
   }, 
   function() {   
      console.log("email send ..");   
   });   
   console.log("End of script"); 
   //executes last or blocked by other methods   
</script> 

在上面顯示的notifyAll()方法中,通知是透過傳送簡訊和傳送電子郵件來實現的。因此,notifyAll 方法的呼叫者必須將兩個函式作為引數傳遞。每個函式承擔一項單一職責,例如傳送簡訊和傳送電子郵件。

成功執行以上程式碼後,將顯示以下輸出。

starting notification process 
Sms send .. 
Email send .. 
End of script 

在上面提到的程式碼中,函式呼叫是同步的。這意味著 UI 執行緒將等待整個通知過程完成。同步呼叫會變成阻塞呼叫。現在讓我們瞭解非阻塞或非同步呼叫。

理解非同步回撥函式

考慮以上示例。

要啟用指令碼,請對 notifyAll() 方法執行非同步或非阻塞呼叫。我們將使用 JavaScript 的setTimeout()方法。此方法預設情況下是非同步的。

setTimeout() 方法接受兩個引數:

  • 一個回撥函式。

  • 方法將在多少秒後被呼叫。

在這種情況下,通知過程已使用超時包裝。因此,它將延遲兩秒,由程式碼設定。將呼叫 notifyAll(),並且主執行緒繼續執行其他方法。因此,通知過程不會阻塞主 JavaScript 執行緒。

<script>   
   function notifyAll(fnSms, fnEmail) {   
      setTimeout(function() {   
         console.log('starting notification process');   
         fnSms();   
         fnEmail();   
      }, 2000);   
   }   
   notifyAll(function() {   
      console.log("Sms send ..");   
   },  
   function() {   
      console.log("email send ..");   
   });   
   console.log("End of script"); //executes first or not blocked by others   
</script>

成功執行以上程式碼後,將顯示以下輸出。

End of script 
starting notification process 
Sms send .. 
Email send .. 

如果有多個回撥函式,程式碼看起來會很嚇人。

<script>   
   setTimeout(function() {   
      console.log("one");   
      setTimeout(function() {   
         console.log("two");   
         setTimeout(function() {   
            console.log("three");   
         }, 1000);   
      }, 1000);   
   }, 1000);   
</script>

ES6 透過引入 Promise 的概念來解決這個問題。Promise 是“延續事件”,它們可以幫助您以更簡潔的程式碼風格一起執行多個非同步操作。

示例

讓我們用一個例子來理解這一點。以下是相同的語法。

var promise = new Promise(function(resolve , reject) {    
   // do a thing, possibly async , then..  
   if(/*everthing turned out fine */)    resolve("stuff worked");  
   else     
   reject(Error("It broke"));  
});  
return promise;
// Give this to someone

實現 Promise 的第一步是建立一個將使用 Promise 的方法。假設在這個例子中,getSum()方法是非同步的,即它的操作不應該阻塞其他方法的執行。一旦此操作完成,它稍後將通知呼叫者。

以下示例(步驟 1)聲明瞭一個 Promise 物件“var promise”。Promise 建構函式將兩個函式作為引數,第一個用於工作成功完成,另一個用於發生錯誤時。

Promise 透過使用 resolve 回撥並傳入結果(即 n1+n2)來返回計算結果。

步驟 1 - resolve(n1 + n2);

如果 getSum() 遇到錯誤或意外情況,它將在 Promise 中呼叫 reject 回撥方法並將錯誤資訊傳遞給呼叫者。

步驟 2 - reject(Error("Negatives not supported"));

方法實現如下面的程式碼所示(步驟 1)。

function getSum(n1, n2) {   
   varisAnyNegative = function() {   
      return n1 < 0 || n2 < 0;   
   }   
   var promise = new Promise(function(resolve, reject) {   
      if (isAnyNegative()) {   
         reject(Error("Negatives not supported"));   
      }   
      resolve(n1 + n2)
   });   
   return promise;   
} 

第二步詳細介紹了呼叫者的實現(步驟 2)。

呼叫者應該使用“then”方法,該方法接受兩個回撥方法——第一個用於成功,第二個用於失敗。每個方法都接受一個引數,如下面的程式碼所示。

getSum(5, 6)   
.then(function (result) {   
   console.log(result);   
},   
function (error) {   
   console.log(error);   
});

成功執行以上程式碼後,將顯示以下輸出。

11 

由於 getSum() 的返回型別是 Promise,因此我們實際上可以有多個“then”語句。第一個 'then' 將有一個返回值。

getSum(5, 6)   
.then(function(result) {   
   console.log(result);   
   returngetSum(10, 20); 
   // this returns another promise   
},   
function(error) {   
   console.log(error);   
})   
.then(function(result) {   
   console.log(result);   
}, 
function(error) {   
   console.log(error);
});    

成功執行以上程式碼後,將顯示以下輸出。

11
30

以下示例使用 getSum() 方法發出三個 then() 呼叫。

<script>   
   function getSum(n1, n2) {   
      varisAnyNegative = function() {   
         return n1 < 0 || n2 < 0;   
      }   
      var promise = new Promise(function(resolve, reject) {   
         if (isAnyNegative()) {   
            reject(Error("Negatives not supported"));   
         }   
         resolve(n1 + n2);   
      });   
      return promise;   
   }   
   getSum(5, 6)   
   .then(function(result) {   
      console.log(result);   
      returngetSum(10, 20); 
      //this returns another Promise   
   },   
   function(error) {   
      console.log(error);   
   })
   .then(function(result) {   
      console.log(result);   
      returngetSum(30, 40); 
      //this returns another Promise   
   }, 
   function(error) {   
      console.log(error);   
   })   
   .then(function(result) {   
      console.log(result);   
   }, 
   function(error) {         
      console.log(error);   
   });   
   console.log("End of script ");   
</script> 

成功執行以上程式碼後,將顯示以下輸出。

程式首先顯示“end of script”,然後依次顯示呼叫 getSum() 方法的結果。

End of script  
11 
30 
70

這表明 getSum() 是以非同步方式或非阻塞方式呼叫的。Promise 提供了一種很好且簡潔的方式來處理回撥函式。

廣告