一、靜態方法

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

1)Promise.resolve()

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

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

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

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

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

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()中所有類型的參數都會原封不動的傳遞給後續的已拒絕的回調函數,如下代碼所示。

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()只能處理其中的一個。

二、應用

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);
});

推薦閱讀:

相关文章