ECMAScript 6 之Promise 對象

1. 概述

Promise 是異步編程的一種解決方案。ES6 原生提供了Promise對象。
Promise 對象是一個代理對象,被代理的值在Promise對象創建時可能是未知的。它允許爲異步操作的成功和失敗分別綁定相應的處理方法。

一個Promise有以下幾種狀態:

  • pending:初始狀態
  • fulfilled:操作成功
  • rejected:操作失敗

Promise 對象具有以下特點:

  • 對象的狀態不受外界影響。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。
  • 一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從pending變爲fulfilled和從pending變爲rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱爲 resolved

2. 基本用法

Promise對象是一個構造函數,用來生成Promise實例。

var promise = new Promise(function(resolve, reject){
	...
})

Promise構造函數接受一個函數作爲參數,Promise構造函數執行時立即調用該函數。該函數的兩個參數分別是resolve函數和reject函數。

resolve函數:將Promise對象的狀態從pending變爲fulfilled,在異步操作成功的時候會調用,並且將異步操作的結果作爲參數傳遞。
reject函數:將Promise對象狀態從pending變爲rejected,在異步操作失敗時會調用,並且將異步操作報出的錯誤作爲參數傳遞。

Promise實例生成以後,可以用then方法分別指定fulfilled狀態和rejected狀態的回調函數。

promise.then(onFulfilled[, onRejected]);

then方法可以接受兩個回調函數作爲參數。第一個回調函數是Promise對象的狀態變爲fulfilled時調用,第二個回調函數是Promise對象的狀態變爲rejected時調用(可選)。

下面是一個簡單的例子。

// 創建Promise實例
function promise(second) {
	return new Promise((resolve, reject) => setTimeout(resolve, second, "成功!"))
}

promise(100).then(value = > console.log(value)); // “成功!“”

上面代碼中,promise方法返回一個Promise實例,內部使用定時器模擬了一個異步任務。當過了指定時間後,Promise實例狀態變爲fulfilled,就會觸發then方法綁定的回調函數。

下面是一個異步加載圖片的例子。

function imgLoad(url) {
	// 返回Promise實例
	return new Promise((resolve, reject) => {
		// 創建XMLHttpRequest實例
		var request = new XMLHttpRequest();
		// 設置連接信息
		request.open("GET",url);
		// 返回二進制對象
		request.responseType = 'blob';
		request.onreadystatechange = function() { // 監聽狀態變化
			if(request.readyState === 4 && request.status === 200) { // 通信完成
				return resolve(request.response);
			}else{
				return reject(Error("圖片加載失敗!" + request.statusText))
			}
		}
		request.onerror = function(){ // 請求失敗監聽
			return reject(Error("請求失敗!"))
		} 
		// 發送請求
		request.send();
	})
}

// 執行then方法
imgLoad("url").then(((response => console.info(response)), (error => console.info(error))));

3. Promise 對象實例方法

3.1 Promise.prototype.then()

then()方法返回一個 Promise實例,可以爲Promise實例添加狀態改變的回調函數。由於then()方法返回一個promise實例,所以可以進行鏈式調用。

語法

 // 創建Promise實例
var promise = new Promise(function(resolve, reject){
	...
})

// then方法參數是兩個回調函數
promise.then(onFulfilled[, onRejected]);

參數
以下是傳遞給then()方法的參數:

  • onFulfilled:當Promise實例狀態變爲fulfilled時調用的函數,該參數可選。
  • onRejected:當Promise實例狀態變爲rejected時調用的函數,該參數可選。

返回值
當一個 Promise 狀態發生改變時,返回函數將被異步調用。具體的返回值依據以下規則返回。如果 then 中的回調函數:

  • 返回了一個值,那麼then方法返回的 Promise 將會成爲fulfilled,並且將返回的值作爲fulfilled狀態的回調函數的參數值。
  • 沒有返回任何值,那麼then方法返回的 Promise 將會成爲fulfilled,並且該fulfilled狀態的回調函數的參數值爲 undefined
  • 拋出一個錯誤,那麼then返回的 Promise將會成爲rejected,並且將拋出的錯誤作爲rejected狀態的回調函數的參數值。
  • 返回一個已經是fulfilled狀態的 Promise,那麼then 返回的 Promise 也會成爲fulfilled,並且將那個 Promisefulfilled狀態的回調函數的參數值作爲該被返回的Promisefulfilled狀態回調函數的參數值。
  • 返回一個已經是拒絕狀態的 Promise,那麼 then 返回的 Promise 也會成爲’rejected’,並且將那個 Promiserejected狀態的回調函數的參數值作爲該被返回的Promiserejected狀態回調函數的參數值。
  • 返回一個pending狀態的 Promise,那麼 then 返回 Promise 的狀態也是fulfilled的,並且它的終態與那個Promise 的終態相同;同時,它變爲終態時調用的回調函數參數與那個 Promise 變爲終態時的回調函數的參數是相同的。

3.2 Promise.prototype.catch()

catch() 方法返回一個Promise實例,並且處理拒絕的情況。它的行爲與調用Promise.prototype.then(undefined, onRejected)相同。

語法

var promise = new Promise(function(resolve, rejected){
	...
})
promise.catch(onRejected);

參數
以下是傳遞給catch()方法的參數:

  • onRejected:當Promiserejected時,被調用的一個函數。

返回值
一個Promise實例。

示例

var p1 = new Promise((resolve, reject) => {
  resolve('Success');
});

p1.then((value) => {
  throw Error("錯誤");
}).catch((error) => {
  console.log(error); // Error: 錯誤
}).then(() =>{
  console.log("sucess!") // sucess!
}, () => {
  console.log("error");
});

3.3 Promise.prototype.finally()

finally方法用於指定不管 Promise對象最後狀態如何,都會執行的操作。

var promise = new Promise(function(resolve, rejected){
	...
})

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

finally方法接受一個回調函數作爲參數,但是該回調函數不接受任何參數。

finally本質上是then方法的特例。

promise
.finally(() => {
 	...
});

// 等價於
promise
.then(
  result => {
    ...
    return result;
  },
  error => {
    ...
    throw error;
  }
);

4. Promise 對象方法

4.1 Promise.all()

Promise.all()方法用於將多個 Promise實例,包裝成一個新的Promise實例。

const promise = Promise.all([p1, p2, p3]);

上面代碼中,Promise.all()接受一個數組作爲參數,數組成員p1p2p3都是Promise實例。如果不是,就會將參數轉爲Promise實例,再進行處理。另外,Promise.all()方法的參數可以不是數組,但必須具有 Iterator 接口。

promise的狀態由p1p2p3決定,分爲兩種情況:

  • p1p2p3的狀態都變成fulfilledpromise的狀態纔會變成fulfilled,此時p1p2p3的返回值組成一個數組,傳遞給promise的回調函數。
  • 只要p1p2p3之中有一個被rejectedpromise的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給promise的回調函數。

示例

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'foo');
}); 

Promise.all([p1, p2, p3]).then(values => { 
  console.log(values); // [3, 1337, "foo"] 
});

上面例子紅,Promise.all 等待所有都完成(或第一個失敗)時纔會調用後面的回調函數。

4.2 Promise.race()

Promise.race()方法同樣是將多個 Promise 實例,包裝成一個新的 Promise實例。

const p = Promise.race([p1, p2, p3]);

上面代碼中,只要p1p2p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。

Promise.race()方法的參數與Promise.all()方法一樣,如果不是 Promise實例,參數會轉爲 Promise 實例,再進一步處理。

示例

var p1 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "one"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "two"); 
});

Promise.race([p1, p2]).then(function(value) {
  console.log(value); // "two"
  // 兩個都完成,但 p2 更快
});

var p3 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "three");
});
var p4 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 500, "four"); 
});

Promise.race([p3, p4]).then(function(value) {
  console.log(value); // "three"
  // p3 更快,所以它完成了              
}, function(reason) {
  // 未被調用
});

var p5 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "five"); 
});
var p6 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 100, "six");
});

Promise.race([p5, p6]).then(function(value) {
  // 未被調用             
}, function(reason) {
  console.log(reason); // "six"
  // p6 更快,所以它失敗了
});

4.3 Promise.resolve()

Promise.resolve()方法將現有對象轉爲一個Promise實例。

const promise = Promise.resolve(12);

Promise.resolve方法的參數分成四種情況:

  • 參數是一個 Promise 實例,那麼Promise.resolve將不做任何修改、原封不動地返回這個實例。
  • 參數是一個thenable對象,即具有then方法的對象,Promise.resolve方法會將這個對象轉爲 Promise 對象,然後就立即執行thenable對象的then方法。
  • 如果參數是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的Promise對象,狀態爲fulfilled
  • 不帶有任何參數,直接返回一個fulfilled狀態的 Promise 對象。

示例

Promise.resolve("Success").then(function(value) {
  console.log(value); // "Success"
}, function(value) {
  // 不會被調用
});

Promise.resolve([1,2,3]).then(function(v) {
 console.log(v[0]); // 1
});

4.4 Promise.reject()

Promise.reject()方法返回一個帶有拒絕原因的Promise對象。

示例

Promise.reject(new Error('fail')).then(function() {
  // not called
}, function(error) {
  console.error(error); // Stacktrace
});

5 參考鏈接

本篇博文是我自己學習筆記,原文請參考:ECMAScript 6 入門MDN網站
如有問題,請及時指出!
歡迎溝通交流,郵箱:[email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章