(84)Wangdao.com第十八天_JavaScript Promise 對象

Promise 對象promise

是 JavaScript 的異步操做解決方案,爲異步操做提供統一接口。app

目前 JavaScript 原生支持 Promise 對象異步

它起到代理做用(proxy),充當異步操做回調函數之間的中介,使得異步操做具有同步操做的接口。函數

Promise 可讓異步操做寫起來,就像在寫同步操做的流程,而沒必要一層層地嵌套回調函數。spa

  • Promise 是一個對象,也是一個構造函數
    function f1(resolve, reject) {
      // 異步代碼...
    }
    
    var p1 = new Promise(f1);
    // 構造函數接受一個回調函數 做爲參數,裏面是異步操做的代碼。而後,返回的就是一個 Promise 實例Promisef1()f1()p1

 

  • Promise 的設計思想
    • 全部異步任務都返回一個 Promise 實例。
      • Promise 實例有一個 then 方法,用來指定下一步的回調函數。
        var p1 = new Promise(f1);
        p1.then(f2);

        f1() 的異步操做執行完成,就會執行 f2()
        prototype

 

    • 不只改善了可讀性,並且對於多層嵌套的回調函數尤爲方便。
      • 傳統的寫法可能須要把 f2() 做爲回調函數傳入 f1(),好比寫成f1(f2),異步操做完成後,在 f1() 內部調用 f2()。
      • 而 Promise 使得 f1() 和 f2() 變成了鏈式寫法。
        // 傳統寫法
        step1(function (value1) {
            step2(value1, function(value2) {
                step3(value2, function(value3) {
                    step4(value3, function(value4) {
                        // ...
                    });
                });
            });
        });
        
        // Promise 的寫法
        (new Promise(step1)).then(step2).then(step3).then(step4);

 

  • 經過自身的狀態,來控制異步操做。
  • Promise 實例具備三種狀態
      • 異步操做未完成(pending)
      • 異步操做成功(fulfilled)
      • 異步操做失敗(rejected)
    • 上面三種狀態裏面,fulfilled 和 rejected合在一塊兒稱爲 resolved(已定型)。
    • 這三種的狀態的變化途徑只有兩種:
      • 從 「未完成」 到 「成功」
      • 從 「未完成」 到 「失敗」
    • 它的英語意思是「承諾」,一旦承諾成效,就不得再改變了。
    • 一旦狀態發生變化,就凝固了,不會再有新的狀態變化。
    • 這也意味着,Promise 實例的狀態變化只可能發生一次。

 

  • Promise 構造函數
    • JavaScript 提供原生的 Promise 構造函數,用來生成 Promise 實例
      • var promise = new Promise(function (resolve, reject) {
            // ...
        
            if (/* 異步操做成功 */){
                resolve(value);
            } else { /* 異步操做失敗 */
                reject(new Error());
            }
        });
        // 該函數的兩個參數分別是 和 。它們是兩個函數,由 JavaScript 引擎提供,不用本身實現
        resolvereject

         

      • resolve() 函數設計

        • 做用是,將Promise實例的狀態從「未完成」變爲「成功」(即從pending變爲fulfilled)代理

        • 在異步操做成功時調用,並將異步操做的結果,做爲參數傳遞出去。code

      • reject函數對象

        • 做用是,將Promise實例的狀態從「未完成」變爲「失敗」(即從pending變爲rejected)

        • 在異步操做失敗時調用,並將異步操做報出的錯誤,做爲參數傳遞出去。

    • 實例
      • function timeout(ms) {
            return new Promise((resolve, reject) => {
                setTimeout(resolve, ms, 'done');
            });
        }
        
        timeout(100)

        上面代碼中,timeout(100) 返回一個 Promise 實例。100毫秒之後,該實例的狀態會變爲 fulfilled

 

  • Promise.prototype.then() 
    • Promise 實例的then方法,用來添加回調函數
    • 能夠接受兩個回調函數,一旦狀態改變,就調用相應的回調函數。
      • 第一個是異步操做成功時(變爲fulfilled狀態)的回調函數
      • 第二個是異步操做失敗(變爲rejected)時的回調函數(該參數能夠省略)。
        • var p1 = new Promise(function (resolve, reject) {
              resolve('成功');
          });
          p1.then(console.log, console.error);    // "成功"
          
          
          var p2 = new Promise(function (resolve, reject) {
              reject(new Error('失敗'));
          });
          p2.then(console.log, console.error);    // Error: 失敗
        • p1 和 p2 都是Promise 實例,它們的 then() 方法綁定兩個回調函數:成功時的回調函數 console.log,失敗時的回調函數 console.error(能夠省略)。

        • p1 的狀態變爲成功,p2 的狀態變爲失敗

        • 對應的回調函數會收到異步操做傳回的值,而後在控制檯輸出

 

  • 實例:圖片加載
    • 使用 Promise 完成圖片的加載
      • var preloadImage = function (path) {
            return new Promise(function (resolve, reject) {
                var image = new Image();
                image.onload  = resolve;
                image.onerror = reject;
                image.src = path;
            });
        };

         

      • 調用

        preloadImage('https://example.com/my.jpg')
            .then(function (e) { document.body.append(e.target) })
            .then(function () { console.log('加載成功') })

         

 

  • 強大之處:
    • 讓回調函數變成了規範的鏈式寫法,程序流程能夠看得很清楚。
    • 它有一整套接口,能夠實現許多強大的功能,
      • 好比同時執行多個異步操做,等到它們的狀態都改變之後,再執行一個回調函數
      • 再好比,爲多個回調函數中拋出的錯誤,統一指定處理方法等等。
    • 它的狀態一旦改變,不管什麼時候查詢,都能獲得這個狀態。
      • 這意味着,不管什麼時候爲 Promise 實例添加回調函數,該函數都能正確執行。
      • 因此,你不用擔憂是否錯過了某個事件或信號。
      • 若是是傳統寫法,經過監聽事件來執行回調函數,一旦錯過了事件,再添加回調函數是不會執行的。

 

  • 微任務(Microtask)
    • Promise 的回調函數屬於異步任務,會在同步任務以後執行。
      • new Promise(function (resolve, reject) {
            resolve(1);
        }).then(console.log);
        
        console.log(2);
        // 2
        // 1
        // 上面代碼會先輸出 2,再輸出 1 。
        // 由於 console.log(2)是同步任務,而 then 的回調函數屬於異步任務,必定晚於同步任務執行

         

    • Promise 的回調函數不是正常的異步任務,而是微任務(microtask)
    • 它們的區別在於
      • 正常任務追加到下一輪事件循環
      • 微任務追加到本輪事件循環
      • 這意味着,微任務的執行時間必定早於正常任務。
    • setTimeout(function() {
          console.log(1);
      }, 0);
      
      new Promise(function (resolve, reject) {
          resolve(2);
      }).then(console.log);
      
      console.log(3);
      // 3
      // 2
      // 1

       

    • 輸出結果是321。這說明 then 的回調函數的執行時間,早於setTimeout(fn, 0)。

    • 由於 then 是本輪事件循環執行,setTimeout(fn, 0)在下一輪事件循環開始時執行

相關文章
相關標籤/搜索