此次聊聊Promise對象

歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~javascript

本文由 前端林子發表於 雲+社區專欄

Promise是CommonJS提出的一種規範,在ES6中已經原生支持Promise對象,非ES6環境能夠用Bluebird等庫來支持。css

0.引入

在js中任務的執行模型有兩種:同步模式和異步模式。html

同步模式:後一個任務B等待前一個任務A結束後,再執行。任務的執行順序和任務的排序順序是一致的。前端

異步模式:每個任務有一個或多個回調函數,前一個任務A結束後,不是執行後一個任務B,而是執行任務A的回調函數。然後一個任務B是不等任務A結束就執行。任務的執行順序,與任務的排序順序不一致。java

異步模式編程有四種方法:回調函數(最基本的方法,把B寫成A的回調函數)、事件監聽(爲A綁定事件,當A發生某個事件,就執行B)、發佈/訂閱,以及本文要介紹的Promise對象。ajax

Promise是一個用於處理異步操做的對象,能夠將回調函數寫成鏈式調用的寫法,讓代碼更優雅、流程更加清晰,讓咱們能夠更合理、更規範地進行異步處理操做。它的思想是,每個異步任務返回一個Promise對象,該對象有一個then方法,容許指定回調函數。編程

1.Promise的基本知識

1.1 三種狀態

Pending:進行中,剛建立一個Promise實例時,表示初始狀態;數組

resolved(fulfilled):resolve方法調用的時候,表示操做成功,已經完成;promise

Rejected:reject方法調用的時候,表示操做失敗;瀏覽器

1.2 兩個過程

這三種狀態只能從pendeng-->resolved(fulfilled),或者pending-->rejected,不能逆向轉換,也不能在resolved(fulfilled)和rejected之間轉換。而且一旦狀態改變,就不會再改變,會一直保持這個結果。

彙總上述,建立一個Promise的實例是這樣的:

//建立promise的實例
let promise = new Promise((resolve,reject)=>{
    //剛建立實例時的狀態:pending

    if('異步操做成功'){
        //調用resolve方法,狀態從pending變爲fulfilled
        resolve();
    }else{
        //調用reject方法,狀態從pending變爲rejected
        reject();
    }
});

1.3 then()

用於綁定處理操做後的處理程序,分別指定fulfilled狀態和rejected狀態的回調函數,即它的參數是兩個函數,第一個用於處理操做成功後的業務,第二個用於處理操做失敗後的業務。

//then()
promise.then((res)=> {
    //處理操做成功後的業務(即Promise對象的狀態變爲fullfilled時調用)
},(error)=> {
    //處理操做失敗後的業務(即Promise對象的狀態變爲rejected時調用)
});

1.4 catch()

用於處理操做異常的程序,catch()只接受一個參數

//catch()
promise.catch((error)=> {
    //處理操做失敗後的業務
});

通常來講,建議不要在then()裏面定義rejected狀態的回調函數,而是將then()用於處理操做成功,將catch()用於處理操做異常。由於這樣作能夠捕獲then()執行中的錯誤,也更接近同步中try/catch的寫法:

//try-catch
// bad
promise.then((res)=> {
    //處理操做成功後的業務
  }, (error)=> {
    //處理操做失敗後的業務
  });

// good
promise
  .then((res)=> { 
    //處理操做成功後的業務
  })
  .catch((error)=> {
    //處理操做失敗後的業務
  });

1.5 all()

接受一個數組做爲參數,數組的元素是Promise實例對象。只有當參數中的實例對象的狀態都爲fulfilled時,Promise.all( )纔會有返回。

實例代碼(可直接在瀏覽器中打開):

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Promise實例</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        window.onload = () => {
            //建立實例promise1
            let promise1 = new Promise((resolve) => {
                setTimeout(() => {
                    resolve('promise1操做成功');
                    console.log('1')
                }, 3000);
            });

            //建立實例promise1
            let promise2 = new Promise((resolve) => {
                setTimeout(() => {
                    resolve('promise1操做成功');
                    console.log('2')
                }, 1000);
            });


            Promise.all([promise1, promise2]).then((result) => {
                console.log(result);
            });
        }
    </script>
</head>

<body>
    <div></div>
</body>

</html>

結果(注意看時間):

imgPromise.all()

代碼說明:

1s後,promise2進入fulfilled狀態,間隔2s,也就是3s後,promise1也進入fulfilled狀態。這時,因爲兩個實例都進入了fulfilled狀態,因此Promise.all()才進入了then方法。

使用場景:執行某個操做須要依賴多個接口請求回的數據,且這些接口之間不存在互相依賴的關係。這時使用Promise.all(),等到全部接口都請求成功了,它纔會進行操做。

1.6 race()

和all()的參數同樣,參數中的promise實例,只要有一個狀態發生變化(無論是成功fulfilled仍是異常rejected),它就會有返回,其餘實例中再發生變化,它也無論了。

實例代碼(可直接在瀏覽器中打開):

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Promise實例</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        window.onload = () => {
            //建立實例promise1
            let promise1 = new Promise((resolve) => {
                setTimeout(() => {
                    resolve('promise1操做成功');
                    console.log('1')
                }, 3000);
            });

            //建立實例promise1
            let promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    reject('promise1操做失敗');
                    console.log('2')
                }, 1000);
            });


            Promise.race([promise1, promise2])
                .then((result) => {
                    console.log(result);
                })
                .catch((error) => {
                    console.log(error);
                })
        }
    </script>
</head>

<body>
    <div></div>
</body>

</html>

結果(注意看時間):

imgPromise.race()

代碼說明:

1s後,promise2進入rejected狀態,因爲一個實例的狀態發生了變化,因此Promise.race()就馬上執行了。

2 實例

平時開發中可能常常會遇到的問題是,要用ajax進行屢次請求。例如如今有三個請求,請求A、請求B、請求C。請求C要將請求B的請求回來的數據作爲參數,請求B要將請求A的請求回來的數據作爲參數。

按照這個思路,咱們可能會直接寫出這樣的層層嵌套的代碼:

//------請求A 開始---------
    $.ajax({
        success:function(res1){


            //------請求B 開始----
            $.ajax({
                success:function(res2){


                    //----請求C 開始---
                    $.ajax({
                        success:function(res3){
                        }
                    });
                    //---請求C 結束---


                }    
            });
            //------請求B 結束-----


        }
    });
    //------請求A 結束---------

在請求A的success後,請求B發送請求,在請求B 的success後,請求C發送請求。請求C結束後,再向上到請求B結束,請求B結束後,再向上到請求A結束。

這樣雖然能夠完成任務,可是代碼層層嵌套,代碼可讀性差,也不便於調試和後續的代碼維護。而若是用Promise,你能夠這樣寫(示意代碼,無ajax請求):

此處附上完整可執行代碼,可在瀏覽器的控制檯中查看執行結果:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Promise實例</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        window.onload = () => {
            let promise = new Promise((resolve, reject) => {

                if (true) {
                    //調用操做成功方法
                    resolve('操做成功');
                } else {
                    //調用操做異常方法
                    reject('操做異常');
                }
            });

            //then處理操做成功,catch處理操做異常
            promise.then(requestA)
                .then(requestB)
                .then(requestC)
                .catch(requestError);

            function requestA() {
                console.log('請求A成功');
                return '下一個是請求B';
            }
            function requestB(res) {
                console.log('上一步的結果:' + res);
                console.log('請求B成功');
                return '下一個是請求C';
            }
            function requestC(res) {
                console.log('上一步的結果:' + res);
                console.log('請求C成功');
            }
            function requestError() {
                console.log('請求失敗');
            }
        }
    </script>
</head>

<body>
    <div></div>
</body>

</html>

結果以下:

img實例

能夠看出請求C依賴請求B的結果,請求B依賴請求A的結果,在請求A中是使用了return將須要的數據返回,傳遞給下一個then()中的請求B,實現了參數的傳遞。同理,請求B中也是用了return,將參數傳遞給了請求C。

3.小結

本文主要介紹了Promise對象的三個狀態和兩個過程。「三個狀態」是:初始化、操做成功、操做異常,「兩個過程」是初始化狀態到操做成功狀態,和初始化狀態到操做異常狀態。除此以前,還有兩種實例方法:then()、catch()來綁定處理程序。類方法:Promise.all()、Promise.race()。若有問題,歡迎指正。

相關閱讀
【每日課程推薦】機器學習實戰!快速入門在線廣告業務及CTR相應知識

此文已由做者受權騰訊雲+社區發佈,更多原文請點擊

搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區

相關文章
相關標籤/搜索