ES6躬行記(23)——Promise的靜態方法和應用

1、靜態方法

  Promise有四個靜態方法,分別是resolve()、reject()、all()和race(),本節將着重分析這幾個方法的功能和特色。數組

1)Promise.resolve()異步

  此方法有一個可選的參數,參數的類型會影響它的返回值,具體可分爲三種狀況(以下所列),其中有兩種狀況會建立一個新的已處理的Promise實例,還有一種狀況會返回這個參數。async

(1)當參數爲空或非thenable時,返回一個新的狀態爲fulfilled的Promise。函數

(2)當參數爲thenable時,返回一個新的Promise,而它的狀態由自身的then()方法控制,具體細節已在以前的thenable一節作過說明。this

(3)當參數爲Promise時,將不作修改,直接返回這個Promise。spa

  下面用一個例子演示這三種狀況,注意觀察Promise的then()方法的第一個回調函數中接收到的決議結果。code

let tha = {
  then(resolve, reject) {
    resolve("thenable");
  }
};
//參數爲
Promise.resolve().then(function(value) {
  console.log(value);    //undefined
});
//參數爲非thenable
Promise.resolve("string").then(function(value) {
  console.log(value);    //"string"
});
//參數爲thenable
Promise.resolve(tha).then(function(value) {
  console.log(value);    //"thenable"
});
//參數爲Promise
Promise.resolve(new Promise(function(resolve) {
  resolve("Promise");
})).then(function(value) {
  console.log(value);    //"Promise"
});

2)Promise.reject()對象

  此方法能接收一個參數,表示拒絕理由,它的返回值是一個新的已拒絕的Promise實例。與Promise.resolve()不一樣,Promise.reject()中全部類型的參數都會原封不動的傳遞給後續的已拒絕的回調函數,以下代碼所示。blog

Promise.reject("rejected").catch(function (reason) {
  console.log(reason);          //"rejected"
});
var p = Promise.resolve();
Promise.reject(p).catch(function (reason) {
  reason === p;                 //true
});

  第一次調用Promise.reject()的參數是一個字符串,第二次的參數是一個Promise,catch()方法中的回調函數接收到的正是這兩個參數。事件

3)Promise.all()

  此方法和接下來要講解的Promise.race()均可用來監控多個Promise,當它們的狀態發生變化時,這兩個方法會給出不一樣的處理方式。

  Promise.all()能接收一個可迭代對象,其中可迭代對象中的成員必須是Promise,若是是字符串、thenable等非Promise的值,那麼會自動調用Promise.resolve()轉換成Promise。Promise.all()的返回值是一個新的Promise實例,當參數中的成員爲空時,其狀態爲fulfilled;而當參數不爲空時,其狀態由可迭代對象中的成員決定,具體分爲兩種狀況。

  (1)當可迭代對象中的全部成員都是已完成的Promise時,新的Promise的狀態爲fulfilled。而各個成員的決議結果會組成一個數組,傳遞給後續的已完成的回調函數,以下所示。

var p1 = Promise.resolve(200),
  p2 = "fulfilled";
Promise.all([p1, p2]).then(function (value) {
  console.log(value);          //[200, "fulfilled"]
});

  (2)當可迭代對象中的成員有一個是已拒絕的Promise時,新的Promise的狀態爲rejected。而且只會處理到這個已拒絕的成員,接下來的成員都會被忽略,其決議結果會傳遞給後續的已拒絕的回調函數,以下所示。

var p1 = Promise.reject("error"),
  p2 = "fulfilled";
Promise.all([p1, p2]).catch(function (reason) {
  console.log(reason);         //"error"
});

4)Promise.race()

  此方法和Promise.all()有不少類似的地方,以下所列。

(1)能接收一個可迭代對象。

(2)成員必須是Promise,對於非Promise的值要用Promise.resolve()作轉換。

(3)返回值是一個新的Promise實例。

  新的Promise實例的狀態也與方法的參數有關,當參數的成員爲空時,其狀態爲pending;當參數不爲空時,其狀態是最早被處理的成員的狀態,而且此成員的決議結果會傳遞給後續相應的回調函數,以下代碼所示。

var p1 = new Promise(function(resolve) {
  setTimeout(() => {
    resolve("fulfilled");
  }, 200);
});
var p2 = new Promise(function(resolve, reject) {
  setTimeout(() => {
    reject("rejected");
  }, 100);
});
Promise.race([p1, p2]).catch(function (reason) {
  console.log(reason);      //"rejected"
});

  在p1和p2的執行器中都有一個定時器。因爲後者的定時器會先執行,所以經過調用Promise.race([p1, p2])獲得的Promise實例,其狀態和p2的相同,而p2的決議結果會做爲拒絕理由被catch()方法中的回調函數接收。

  根據前面的分析能夠得出,Promise.all()能處理一個或多個受監控的Promise,而Promise.race()只能處理其中的一個。

2、應用

1)Promise和生成器

  用Promise和生成器實現一個運行器,能夠取代冗長的Promise鏈,以一種更直觀的方式控制異步,相似於下面這樣。

function load() {
  return new Promise(function(resolve, reject) {
    resolve("success");
  });
}
run(function* ()    {
  var result = yield load();
  console.log(result);        //"success"
});

  在load()函數內部,執行的是異步操做,因爲處在run運行器中,所以它的決議結果可經過賦值語句直接給到result。這種工做模式徹底顛覆了過去用回調函數接收異步操做結果的模式。爲此,ES8規範特意引入了兩個關鍵字:async和await,支持這種便捷的工做模式,下面改寫上一個示例,用新語法實現相同的功能。

(async function() {
  var result = await load();
  console.log(result);        //"success"
})();

  關於run運行器的工做原理和ES8的新語法,限於篇幅緣由,此處再也不展開說明。

2)與傳統異步操做的組合

  以往須要用回調函數接收異步處理結果的操做,如今都能改爲Promise的模式。以圖像加載爲例,當圖像載入成功時,會觸發load事件;而當失敗時,會觸發error事件。若是後續又有異步操做,那麼就只能在這兩個事件中處理,但這麼一來,就會造成難以控制的回調金字塔。而改用Promise後,就能鏈式的處理後續的操做,以下所示。

function preImg(src) {
  return new Promise(function (resolve, reject) {
    var img = new Image();
    img.src = src;
    img.onload = function(){
      resolve(this);
    };
    img.onerror = function(){
      reject(this);
    };
  });
};
preImg("img/page.png").then(function(value) {
  console.log(value);
});
相關文章
相關標籤/搜索