1. 程式人生 > 實用技巧 >Promises in JavaScript(譯)

Promises in JavaScript(譯)

原文地址:Promises in JavaScript

現實生活中的承諾只是對“一些事情”的保證。因此,當有人向您承諾時會發生什麼? 他們為您提供保證,您可以以此為基礎進行提規劃。 現在,諾言可以兌現也可以違背。 因此,當您信守諾言時,您會期望從諾言中得到一些東西。 您可以將promise的輸出用於您的進一步操作或計劃。 但是,當承諾違背時,您想知道為什麼做出承諾的人不能跟上交易,而是會採取相應的下一個行動。JavaScript也提供了一種類似的“承諾”概念來處理非同步呼叫。 本文涵蓋以下主題,讓我們瞭解“ JavaScript中的Promises”的詳細資訊:

  • JavaScript中的Promises是什麼?
    • 何時在JavaScript中使用Promises?
    • Promises如何在JavaScript中工作?
  • 在JavaScript中建立Promise的過程。
  • 如何在JavaScript中使用Promise?
    • 如何使用.then()函式?
    • 如何使用.catch()函式?
    • 如何使用.finally()函式?
    • 如何將Promise.all()函式?
    • 如何使用Promise.race()函式?

JavaScript中的Promises是什麼?

JavaScript中的Promise是持有非同步操作未來值的物件。 例如,如果我們要從伺服器請求一些資料,則promise承諾我們將獲得將來可以使用的資料。

一個promise物件擁有如下狀態:

  • Pending:這是初始狀態,結果尚未準備好,正在等待非同步操作完成。
  • Resolved/Fulfilled:表示執行的操作已成功完成。 即,函式返回了承諾值。
  • Rejected:表示執行的操作失敗或發生錯誤。 即函式未返回承諾值。

現在,讓我們理解下在JavaScript中需要promise做些什麼,因為通過回撥它本身來處理非同步操作。

何時在JavaScript中使用Promises?

正如我們在JavaScript中的回撥文章中所討論的那樣,回撥函式用於處理非同步執行。 回撥函式指示非同步操作完成後JavaScript應該執行的操作。

這是執行中的回撥函式的最簡單示例:

<html>

	<body> Demonstrating callback in javascript:</br>
    	<script type="text/javascript">
    		function i_take_1_sec() {
                return setTimeout(() => {
                    document.write('I was no: 2 and I take 1 second</br>')
                }, 1000);
            }
            function i_take_10_sec(callback) {
                return setTimeout(() => {
                    document.write('I was no: 1 and I take 10 seconds</br>')
                    callback()
                }, 10000);
            }
            function run (){
                i_take_10_sec(i_take_1_sec);
            }
            run();
        </script>
    </body>

</html>

儲存名稱為callbackOne.html的檔案,然後在任何瀏覽器(Chrome,Firefox或IE)中開啟它。 它應該將輸出顯示為:

Promises如何在JavaScript中工作?

為了理解promise到底是如何工作的,讓我們舉個例子:考慮到您正在向母親作出諾言,說您將在一天之後收拾完房間。 因此,可能發生兩種可能的情況,要麼您要打掃房間,要麼不打掃房間並違背諾言。 讓我們用JavaScript來寫那個承諾。

let promiseCleanRoom = new Promise(function(resolve,reject){
    cleanRoom = true;
    if(cleanRoom){
      resolve('Cleaned the Room');
    }else{
      reject('Broke the promise');
    }
});

上面的程式碼塊顯示瞭如何建立承諾。 即,當執行此承諾時,它將基於cleanRoom值給出解決狀態或拒絕狀態。

我們可以將以上建立的promise稱為或使用:

 promiseCleanRoom.then((result)=>{
      console.log(result)
  })

在上面的程式碼片段中,它顯示瞭如何使用已建立的承諾,並且有“ .then”方法,該方法僅在滿足或解決給定的承諾時才會執行某些操作。 在這種情況下,它將在解決時打印出諾言傳送的結果,即"Cleaned the Room"

如果承諾被破壞或在這種情況下發生了一些錯誤,還會發生另外一種情況。 然後,我們可以使用“ .catch”塊,該塊將允許處理損壞的或錯誤的例項。

promiseCleanRoom.then((result)=>{
      console.log(result)
}).catch((result)=>{
   console.log(result) //will execute if promise is rejected or errored out
})

假設,如果諾言被破壞,那麼它將通過跳過then塊來執行catch塊,並且輸出將是“Broke the promise”。

注意:我們將在以下各節中詳細介紹“ .then”和“ .catch”方法。

在JavaScript中如何建立一個Promise?

如上所述,JavaScript中的Promise是一個物件,代表非同步操作的最終完成或失敗。 其語法如下所示:

語法:

const promise = new Promise(function(resolve,reject){

//do something

});

注意:我們使用Promise建構函式建立一個新的Promise,該建構函式使用一個引數,一個回撥函式(也稱為executor函式),該函式依次使用兩個回撥函式,resolve和reject。

promise建立後,executor函式立即執行。 promise通過呼叫resolve()方法進行解析,並通過呼叫reject()方法被拒絕。

在以下程式碼段的幫助下,讓我們嘗試瞭解Promise在JavaScript中的用法和工作方式。 在這裡,我們將使用promise物件修改回撥地獄程式(在回撥文章中有所介紹)。

<html>

<body> Demonstrating promise in javascript:</br>
    <script type="text/javascript">

    	function i_take_10_sec()
    	{
    	 	return new Promise((resolve, reject)=>{
    	 	 	setTimeout(() => { 
    	 			resolve('I was no: 1 and I take 10 seconds');
    	 		}, 10000);
			})
    	}

    	function i_take_1_sec()
    	{ 
    		return new Promise((resolve, reject)=>{
    			setTimeout(() => { 
    				resolve('I was no: 2 and I take 1 second');
    			}, 1000);
    		}) 
    	}

    	function i_take_5_sec(callback) 
    	{ 
    		return new Promise((resolve, reject)=>{
    		 	setTimeout(() => { 
    				resolve('I was no: 3 and I take 5 second')
    			}, 5000); 
    		})
    	}

    	function i_take_7_sec(callback) 
    	{ 
    		return new Promise((resolve, reject)=>{
    			setTimeout(() => { 
    				resolve('I was no: 4 and I take 7 second')
    			}, 7000);
    		}) 
    	}

    
    	function run()
    	{
    		i_take_10_sec()
    		.then((result) => {
            console.log(result);
        		return i_take_1_sec()
   		})
    		.then((result) => {
            console.log(result);
        		return i_take_5_sec()
    		})
    		.then((result) => {
            console.log(result);
        		return i_take_7_sec()
    		})
    		.then((result)=>{
            console.log(result);
          })
    	}

    	run();

    </script>
</body>

</html>

使用promise.html名稱儲存檔案,然後在任何瀏覽器(Chrome,Firefox或IE)中開啟它。 它應該顯示為

如上例所示,每個函式都返回一個使用“ .then()”連結到下一個的promise。 在這種情況下,程式碼流顯得更漂亮和易於理解。

現在考慮一種promise被拒絕的情況。 因此,在那種情況下,它將引發錯誤,並且不會呼叫其他連結的函式呼叫。

讓我們修改上面編寫的程式,並且拒絕來自函式“ i_take_5_sec()”的promise,如下所示:

<html>
 
<body> Demonstrating promise in javascript:</br>
    <script type="text/javascript">
 
    	function i_take_10_sec()
    	{
     return new Promise((resolve, reject)=>{
     setTimeout(() => { 
     resolve('I was no: 1 and I take 10 seconds');
     }, 10000);
 })
    	}
 
    	function i_take_1_sec()
    	{ 
     return new Promise((resolve, reject)=>{
     setTimeout(() => { 
     resolve('I was no: 2 and I take 1 second');
     }, 1000);
     }) 
    	}
 
    	function i_take_5_sec(callback) 
    	{ 
     return new Promise((resolve, reject)=>{
     setTimeout(() => { 
     reject('I was no: 3 and I take 5 second')
     }, 5000); 
     })
    	}
 
    	function i_take_7_sec(callback) 
    	{ 
     return new Promise((resolve, reject)=>{
     setTimeout(() => { 
     resolve('I was no: 4 and I take 7 second')
     }, 7000);
     }) 
    	}
 
    
    	function run()
    	{
     i_take_10_sec()
     .then((result) => {
            console.log(result);
         return i_take_1_sec()
   })
     .then((result) => {
            console.log(result);
         return i_take_5_sec()
     })
     .then((result) => {
            console.log(result);
         return i_take_7_sec()
     })
     .then((result)=>{
            console.log(result);
          })
    	}
 
    	run();
 
    </script>
</body>
 
</html>

命名為promise.html並儲存檔案,然後在任何瀏覽器(Chrome,Firefox或IE)中開啟它。 它應該顯示為

從上面的螢幕截圖可以看出,JavaScript在呼叫方法“ i_take_5_sec”後引發了“Uncaught”錯誤,並且沒有其他程式碼語句執行。 因此,這證實了,promise一旦“rejected”了,它將導致所有進一步連結的命令失敗。

如何在JavaScript中使用promise?

通過使用.then,.catch和.finally方法註冊函式來消費promise。 Promise物件用作執行程式和消費函式之間的連結,執行器和接收函式將接收結果或錯誤,並且使用函式可以使用.then,.catch或.finally方法中的任何一種。 讓我們在以下各節中檢視所有這些方法的用法:

如何使用.then()作為消費者函式?

.then()方法在一個promise明確resolved或rejected時呼叫。 其語法如下所示:

語法:

.then(function(result){
        //statements when the promise is resolved successfully
    }, function(error){
        //statements when the prmise was rejected or raised an error
    })

通過下圖,我們可以瞭解“ .then()”方法的主要用法:

讓我們以這種方式理解,假設一條if-else語句,其中當給定條件為true時,if塊中的程式碼將執行; 否則,它將執行else塊。 同樣,“ then”方法將使用兩個函式作為引數,第一個函式將在promise成功解決後執行,或者在promise拒絕或引發錯誤時執行第二個方法。

通過修改上面編寫的程式,讓我們瞭解“ .then()消費者方法”的用法和實現:

<html>

<body> Demonstrating promise consumer using .then()  method in javascript:</br>
    <script type="text/javascript">

    	function i_take_10_sec()
    	{
    	 	return new Promise((resolve, reject)=>{
    	 	 	setTimeout(() => { 
    	 			resolve('I was no: 1 and I take 10 seconds');
    	 		}, 10000);
			})
    	}

    	function i_take_5_sec(callback) 
    	{ 
    		return new Promise((resolve, reject)=>{
    		 	setTimeout(() => { 
    				reject('I was no: 3 and I take 5 second')
    			}, 5000); 
    		})
    	}
    
    	function run()
    	{
    		i_take_10_sec()
    		.then((result) => {
                        console.log(result);
        		return i_take_5_sec()
   		})
    		.then((result)=>{
                   console.log(result);
                 },()=>{
                    console.log('Error Raised')
                 })
    	}

    	run();

    </script>
</body>

</html>

儲存名稱為promiseThen.html的檔案,然後在任何瀏覽器(Chrome,Firefox或IE)中開啟它。 它應該將輸出顯示為:

從上面的螢幕截圖中我們可以看到,由於“ i_take_5_sec()”方法導致承諾被拒絕,因此run方法中的呼叫流呼叫了第二個函式,而不是第一個函式。 因此,以這種方式,使用者可以根據promise的預期輸出,使用“ .then()”方法控制程式的執行流程。

但是,傳遞兩個函式作為then方法的引數看起來有些混亂。 因此,為克服此問題,引入了“ .catch”塊或函式,當出現拒絕或發生某些錯誤時,catch塊將顯式處理。

如何使用.catch()作為消費者函式?

當承諾被拒絕或執行中發生錯誤時,.catch()方法將呼叫。 其語法如下所示:

語法:

.catch(function(error){
        //Statements to handle error raised
    })

下圖可以幫助您理解“ .catch()”方法的基本用法:

從上圖可以明顯看出,在指定.catch()方法的情況下,當承諾被拒絕時,它將呼叫“ .catch”塊。

讓我們通過如下修改上述編寫的程式來了解“ .catch()消費者函式”的用法和實現:

<html>

<body> Demonstrating promise consumer using .catch() method in javascript:</br>
    <script type="text/javascript">

    	function i_take_10_sec()
    	{
    	 	return new Promise((resolve, reject)=>{
    	 	 	setTimeout(() => { 
    	 			resolve('I was no: 1 and I take 10 seconds');
    	 		}, 10000);
			})
    	}

    	function i_take_5_sec(callback) 
    	{ 
    		return new Promise((resolve, reject)=>{
    		 	setTimeout(() => { 
    				reject('I was no: 3 and I take 5 second')
    			}, 5000); 
    		})
    	}
    
    	function run()
    	{
    		i_take_10_sec()
    		.then((result) => {
                        console.log(result);
        		return i_take_5_sec()
   		})
    		.then((result)=>{
                    console.log(result);
                }).catch(()=>{
                   console.log('Error Raised')
               })
    	}

    	run();

    </script>
</body>

</html>

使用名稱promiseCatch.html儲存檔案,然後在任何瀏覽器(Chrome,Firefox或IE)中開啟檔案。 它應該將輸出顯示為:

從上面的螢幕截圖中我們可以看到,由於“ i_take_5_sec()”方法導致承諾被拒絕,因此呼叫流程引發了異常,該異常發生在.catch()塊中。 另外,由於異常發生得當,它不會阻止呼叫流程,並且仍然會執行“ Done”列印所產生的.then()部分中的下一部分。 很明顯,即使中間呼叫之一導致錯誤或異常,使用.catch()塊也可以確保繼續執行進一步的連結呼叫。

如何使用.finally()作為消費者者函式?

我們過去經常使用在try{...}catch{...}語句中使用finally塊,在promises也有finally。當promise完成後這個消費者函式總是會執行,無論是resolve或者reject。finally塊是執行清理操作的良好處理程式,我們一直希望執行該處理程式。其語法如下所示:

.finally(() => {
 // Statements which are expected to be executed always
})

下圖可以幫助您理解“ .finally()”方法的基本用法:

從上圖可以看出,無論promise是否resolved或者rejected,“ finally”塊始終都會執行。

通過修改上面編寫的程式,讓我們在“ .finally()消費者方法”的用法和實現下進行以下操作:

<html>

<body> Demonstrating promise consumer using .finally() method in javascript:</br>
    <script type="text/javascript">

    	function i_take_10_sec()
    	{
    	 	return new Promise((resolve, reject)=>{
    	 	 	setTimeout(() => { 
    	 			resolve('I was no: 1 and I take 10 seconds');
    	 		}, 10000);
			})
    	}

    	function i_take_5_sec(callback) 
    	{ 
    		return new Promise((resolve, reject)=>{
    		 	setTimeout(() => { 
    				reject('I was no: 3 and I take 5 second')
    			}, 5000); 
    		})
    	}
    
    	function run()
    	{
    		i_take_10_sec()
    		.then((result) => {
            console.log(result);
        		return i_take_5_sec()
   		})
    		.then((result)=>{
            console.log(result);
          }).catch(()=>{
             console.log('Error Raised')
          }).finally(()=>{
             console.log('Completed Execution')
          })
    	}

    	run();

    </script>
</body>

</html>

儲存名稱為promiseFinally.html的檔案,然後在任何瀏覽器(Chrome,Firefox或IE)中開啟它。 它應該將輸出顯示為:

從上面的螢幕截圖中我們可以看到,由於“ i_take_5_sec()”方法導致承諾被拒絕,因此引發了異常,並且未執行“ Done”塊的下一個.then()方法。 但是,仍然可以從輸出中看到執行了finally()塊。 因此,很清楚,無論promise返回已解決,已拒絕還是錯誤狀態,都將始終執行“ finally()”塊。

如何使用Promise.all()作為消費者函式?

如果你需要並行執行多個promise,並且想要在所有promise完成之後繼續執行,則可以使用JavaScript中Promises提供的“ .all”函式。 它接受一個promises函式陣列,並同時/並行執行所有函式,然後等到所有promise都被rejecte或resolve為止。 其語法如下所示:

Promise.all([array of promises]);

通過以下示例,讓我們瞭解“ Promise.all”的用法:

示例:

<html>

<body> Demonstrating promise all() method in javascript:</br>
    <script type="text/javascript">

    	function i_take_10_sec()
    	{
    	 	return new Promise((resolve, reject)=>{
    	 	 	setTimeout(() => { 
               console.log('I was no: 1 and I take 10 second')
    	 			resolve('I was no: 1 and I take 10 seconds');
    	 		}, 10000);
			})
    	}

    	function i_take_5_sec(callback) 
    	{ 
    		return new Promise((resolve, reject)=>{
    		 	setTimeout(() => { 
               console.log('I was no: 2 and I take 5 second')
    				resolve('I was no: 2 and I take 5 second')
    			}, 5000); 
    		})
    	}

      function i_take_7_sec(callback) 
    	{ 
    		return new Promise((resolve, reject)=>{
    		 	setTimeout(() => { 
               console.log('I was no: 3 and I take 7 second')
    				resolve('I was no: 3 and I take 7 second')
    			}, 7000); 
    		})
    	}
    
    	function run()
    	{
    		Promise.all([i_take_10_sec(),i_take_5_sec(),i_take_7_sec()]).then(()=>{
             console.log("All finished");
          })
    	}

    	run();
    </script>
</body>

</html>

儲存名稱為promiseAll.html的檔案,然後在任何瀏覽器(Chrome,Firefox或IE)中開啟它。 它應該將輸出顯示為:

我們可以看到,所有的promise都在同時執行,並且會等到所有promise已經resolve或reject為止。

注意:Promise.all()不能確保promise的任何順序。 它只是並行執行所有promise。

如何使用Promise.race()作為消費者函式?

如果您想並行執行多個promise,但又不想等待所有promise的完成再繼續進行,則可以使用JavaScript中Promises提供的“ .race”函式。 它只是等待任何一個promise的完成,然後首先返回。 它將使用一組promise函式並同時執行所有函式,並等待直到任何一個promise已經resolve或reject為止。 其語法如下所示:

Promise.race([array of promises])

在以下示例的幫助下,我們瞭解“ Promise.race”的用法:

示例:

<html>

<body> Demonstrating promise race() method in javascript:</br>
    <script type="text/javascript">

    	function i_take_10_sec()
    	{
    	 	return new Promise((resolve, reject)=>{
    	 	 	setTimeout(() => { 
               console.log('I was no: 1 and I take 10 second')
    	 			resolve('I was no: 1 and I take 10 seconds');
    	 		}, 10000);
			})
    	}

    	function i_take_5_sec(callback) 
    	{ 
    		return new Promise((resolve, reject)=>{
    		 	setTimeout(() => { 
               console.log('I was no: 2 and I take 5 second')
    				resolve('I was no: 2 and I take 5 second')
    			}, 5000); 
    		})
    	}

      function i_take_7_sec(callback) 
    	{ 
    		return new Promise((resolve, reject)=>{
    		 	setTimeout(() => { 
               console.log('I was no: 3 and I take 7 second')
    				resolve('I was no: 3 and I take 7 second')
    			}, 7000); 
    		})
    	}
    
    	function run()
    	{
    		Promise.race([i_take_10_sec(),i_take_5_sec(),i_take_7_sec()]).then(()=>{
             console.log("All finished");
          })
    	}

    	run();
    </script>
</body>

</html>

儲存名稱為promiseRace.html的檔案,然後在任何瀏覽器(Chrome,Firefox或IE)中開啟它。 它應該將輸出顯示為:

在上面的示例中,我們可以看到,一個promise(i_take_5 sec)一旦resolve,它就會從then函式中退出。

重點

  • promise可以處理JavaScript中的非同步呼叫。
  • 根據非同步呼叫的響應,promise在執行時將處於“pending”狀態,並導致“resolved”或“rejected”。
  • promise避免由於巢狀的回撥函式而發生的“回撥地獄”問題。

現在,讓我們轉到下一篇文章“ DOM Manipulation”,那裡我們將介紹JavaScript提供的各種概念/功能,DOM將使用這些概念/功能 可以在執行時動態地進行操作