1. 程式人生 > 其它 >ES6筆記 - Promise物件

ES6筆記 - Promise物件

Promise物件

目錄

1. Promise簡介

  • Promise是非同步程式設計的一種解決方案。從語法上來說,Promise是一個物件,裡面儲存著某個未來才會結束的事件的結果,從這個物件中可以獲取非同步操作的資訊

  • Promise具有兩個特點:

    1. 物件的狀態不受外界影響

      Promise物件只有三種狀態:Pending(進行中)、Fulfilled(已成功)、Rejected(已失敗)

      只有非同步操作的結果可以決定當前是哪一種狀態,其它任何操作都無法改變這個狀態

    2. 一旦狀態改變就不會再變,且任何時候都可以等到這個結果

      Promise物件的狀態只有兩種可能:Pending→Fulfilled或Pending→Rejected。只要這兩種情況發生,狀態就不會再改變,此時就稱為Resolved(已定型)。一旦定型,無論何時添加回調函式都會得到這個結果

  • Promise的優點

    • 將非同步操作以同步操作的流程表達出來,避免回撥函式的層層巢狀
    • 統一了操作介面,使得控制非同步操作更容易
  • Promise的缺點

    • Promise無法被取消,一旦新建就會立即執行,無法中止
    • 如果不設定回撥函式,Promise內部丟擲的錯誤不會反應到外部
    • 當處於Pending狀態時,無法得知目前進展到哪個階段

2. Promise的使用

2.1 建立Promise物件

  • Promise物件是一個建構函式,用來生成Promise例項

    Promise建構函式接受一個函式作為引數,該函式的兩個引數分別是Resolve函式(Pending→Resolved時觸發)和Reject函式(Pending→Rejected時觸發),其中Reject函式可省略

    Promise建構函式中的Resolve和Reject僅作宣告用,後續需要在Promise例項生成後,使用then方法分別制定兩者的回撥函式

  • 一個完整的Promise例項建立流程

    1. 在Promise建構函式中設定觸發條件

      var promise = new Promise(function(resolve,reject){
          //此處無需拘泥於if-else,任何可以是任何用於判斷執行是否成功失敗的函式都可以
          //甚至可以直接書寫resolve,不需要判斷
          if(/*非同步操作成功*/){
              resolve(value)
          }else{
              reject(error);
          }
      })
      
    2. 在Promise例項建立後使用then設定具體回撥函式

      promise.then(
      	function(value){
          	//執行成功後具體操作
      	},
      	function(error){
          	//執行失敗後具體操作
      	}
      )
      

2.2 Promise的執行時間

  • Promise在新建後就會立刻執行,而then方法指定的回撥函式將在當前指令碼所有同步任務執行完畢後才會執行

    let promise = new Promise(function(resolve,reject){
        console.log("Promise");
        resolve();
    })
    
    promise.then(function(){
        console.log('Resolved');
    });
    
    console.log('Hi!');
    
    //Promise
    //Hi!
    //Resolved
    
  • 解析:promise在建立完後立刻執行,所以首先輸出的是Promise,然後按照指令碼執行順序輸出Hi!,then方法中的函式會在指令碼中所有同步任務執行完畢後執行,所以最後才會輸出Resolved

2.3 簡寫形式

  • 由於reject函式可以被省略,所以更常見的是下面的簡寫形式

    function timeout(ms){
        return new Promise((resolve,reject) => {
            setTimeout(resolve,ms,'done');
        });
    }
    
    timeout(100).then((value)=>{
        console.log(value);
    });
    
  • 解析:在呼叫timeout函式的時候就會新建一個promise物件例項,隨後延遲函式setTimeout就會立刻被執行,並在100ms後輸出'done'這一內容。此處在新建Promise物件的時候,並沒有任何判斷,預設setTimeout執行即觸發resolve,同理then方法也並沒有考慮Reject的情況,value直接傳遞給resolve處理

3. then方法

  • then方法是定義在Promise的原型物件Promise.prototype上的。then方法的作用是為Promise例項新增狀態改變時的回撥函式。then方法的第一個引數是Resolved狀態的回撥函式,第二個引數是Rejected狀態的回撥函式,Rejected可省略

  • then方法實際上返回的是一個新的Promise例項,這意味著可以使用鏈式寫法,後一個then的回撥函式將會在前一個then返回的Promise物件執行完畢後再執行

    getJSON("/post/1.json").then(function(post){
        return getJSON(post.commentURL);
    }).then(function funcA(comments){
        console.log("Resolved:", comments);
    },function funcB(err){
        console.log("Rejected: ",err);
    });
    

4. catch方法

  • catch方法是.then(null,rejection)的別名,不難看出catch實際上就是then只考慮了rejected(執行失敗)的情況。catch用於指定發生錯誤的回撥函式

    getJSON("/post/1.json").then(function(post){
        //...
    }).catch(function(error){
        //處理getJSON和前一個回撥函式的錯誤
        console.log('錯誤!',error)
    });
    
  • 注意事項

    1. 之所以要使用catch是因為,如果不使用catch指定錯誤處理的回撥函式,Promise物件丟擲的錯誤不會傳遞到外層程式碼,即不會有任何反應
    2. Promise物件的錯誤具有"冒泡"性質,會一直向後傳遞,直到被捕獲為止。即錯誤總是會被下一個catch所捕獲
    3. 如果Promise狀態已經變為Resolved,再丟擲錯誤是無效的。在resolve語句後面丟擲的錯誤,不會再被捕獲

5. all方法和race方法

  • Promise.all()Promise.race()方法都是用於將多個Promise例項包裝成一個新的Promise例項

    var p = Promise.all([p1,p2,p3]);
    var p = Promise.race([p1,p2,p3])
    
  • Promise.all()有些類似"與"(&&)操作,即包裝的Promise例項中,只有p1、p2、p3的狀態都變為Fullfilled,p的狀態才會變為Fullfilled,而p123中只要有一個變為Rejected,p就會變為Rejected

    Promise.race()中,只要有一個例項率先改變狀態,那麼p的狀態就會跟著改變

  • 注意:如果p包含的Promise例項(如p1)中,有catch函式用於處理其rejected狀態,那麼該例項即便觸發了rejected,在catch過後仍然會返回成resolved狀態,這點需要注意

  • 示例程式碼

    const databasePromise = connectDatabase();
    
    const booksPromise = databasePromise.then(findAllBooks);
    
    const userPromise = databasePromise.then(getCurrentUser);
    
    Promise.all([
       booksPromise,
       userPromise
    ]);
    

6. resolve方法和reject方法

Promise.resolve()

  • resolve方法可以將現有物件轉化為Promise物件
  • 根據引數不同,resolve的行為也有所不同:
    • 當引數是一個Promise例項:resolve方法不會做出任何修改,原封不動地返回該例項
    • 當引數是一個thenable物件:
      • thenable物件是指具有then方法的物件,then方法可以是API自帶也可以是手動定義的
      • resolve方法會自動將這個物件轉化為Promise物件,然後立刻執行其中的then方法將物件變為Resolved狀態
    • 當引數不是具有then方法的物件或根本不是物件:
      • resolve方法會生成一個新的Promise物件,且該物件狀態為Resolved,回撥函式會立刻執行
    • 無引數:直接返回一個Resolve狀態的Promise物件

Promise.reject()

  • 和resolve差不多,區別只是把所有的狀態都變為reject相關而已