JavaScript ES6 Promise Object 物件
Promise 是一種非同步 (asynchronous) 編程的解決方案,所謂的 Promise,簡單來說它是一個等待非同步操作完成的物件,當事件完成時,Promise 根據操作結果是成功、或者失敗,做相對應的處理動作。
一個 Promise 物件 (只) 會處於下面三種狀態之一:
- pending - 初始狀態 (進行中)
- fulfilled - 事件已完成
- rejected - 事件已失敗
Promise 狀態的改變只有兩種可能:
- 從 pending 變成 fulfilled
- 從 pending 變成 rejected
而一但狀態改變就會固定,永遠不會再改變狀態了。
非同步最常見的例子像是 AJAX,傳統在處理非同步事件時會用一堆 nested callbacks,Promise 則是提供另外一種解決方案,讓你更直觀地控制非同步操作。
來看看建立一個 Promise 物件的語法:
var promise = new Promise(function(resolve, reject) {
// ...
if (異步操作成功) {
resolve(value);
} else {
reject(error);
}
});
Promise 構造函數 (constructor) 接受一個函數作為參數,這個函數會在建立 Promise 物件的同時立刻被執行,該函數有兩個參數分別是 resolve 函數和 reject 函數,resolve/reject 這兩個函數會由 JavaScript interpreter 自動傳入。
- resolve(value) 函數的用途是用來將 Promise 物件的狀態變為 fulfilled (已完成),在非同步操作成功時調用,你可以將非同步操作的結果當作參數一起傳入
- reject(error) 函數的用途是用來將 Promise 物件的狀態變為 rejected (已失敗),在非同步操作失敗時調用,你可以將非同步操作的錯誤當作參數一起傳入
此外,當 Promise constructor 的函數參數執行時,如果內部發生錯誤 throw error,Promise 物件的狀態會自動變成 rejected。
Promise.prototype.then(onFulfilled, onRejected)
Promise 物件生成後,可以用 then() 方法來綁定當 fulfilled 或 rejected 狀態時,分別要執行的函數。
promise.then(function(value) {
// 當狀態是 fulfilled (成功) 時,執行這個函數
// value 是透過 resolve() 傳進來的參數
}, function(error) {
// 當狀態是 rejected (失敗) 時,執行這個函數
// error 是透過 reject() 傳進來的參數
});
then() 方法接受兩個函數作為參數:
- 第一個函數是當 Promise 狀態變為成功時會被調用
- 第二個函數是當 Promise 狀態變為失敗時會被調用,這個參數是選擇性的不一定需要
舉一個 Promise 實際使用的例子:
let promise = new Promise(function(resolve, reject) {
// 執行非同步的 setTimeout
setTimeout(function(){
// 250ms 過後,將 Promise 物件狀態改為成功
resolve('Success!');
}, 250);
});
promise.then(function(successMessage) {
// 當 Promise 物件狀態變成功後執行這個函數
console.log('Yay! ' + successMessage);
});
// 250ms 後你會看到輸出 "Yay! Success!"
Promise.prototype.catch(onRejected)
Promise 物件生成後,可以用 catch() 方法來綁定當 rejected 狀態時,要執行的函數。
catch() 的用途就像是 then(undefined, onRejected)。
var promise = new Promise(function(resolve, reject) {
throw 'Uh-oh!';
});
promise.catch(function(e) {
console.log(e);
});
// 顯示 "Uh-oh!"
Chaining 串接
then() 和 catch() 方法執行後都會返回一個新的 Promise 物件,讓你可以使用 chaining 的語法。
而後面的 then() 會接收前一個 then() 的 return value 當作參數。
例如:
var hello = new Promise(function(resolve, reject) {
resolve('Hello');
});
hello.then(function(str) {
return str + ' World';
}).then(function(str) {
return str;
}).then(function(str) {
console.log(str);
});
// 最後輸出 "Hello World"
Promise.all(iterable)
Promise.all() 函數用來將多個 Promise 物件包裝成一個 Promise 物件,他接受的參數可以是一個陣列,陣列中放不同的 Promise 物件。
而新的 Promise 物件的狀態會怎麼改變?
- 狀態變為 fulfilled: 如果它包含的所有 Promise 物件狀態都變為 fulfilled。而所有 Promise 物件個別的返回值,會被組成一個陣列傳進 all Promise 物件的 callback
- 狀態變為 rejected: 如果它包含的其中一個 Promise 物件狀態變為 rejected。而第一個被 reject 的值會被傳進回 all Promise 物件的 callback
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3]).then(function(values) {
console.log(values);
});
// 會顯示 [3, 1337, "foo"]
Promise.race(iterable)
Promise.race() 函數和 Promise.all() 一樣用來將多個 Promise 物件包裝成一個 Promise 物件。
不同的地方在於,只要它包含的所有 Promise 物件其中任何一個的狀態先改變,race Promise 物件的狀態就會跟著改變,率先改變狀態的 Promise 物件參數會直接傳給 race Promise 物件的 callback。
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 比較快被 resolve
Promise.resolve(value)
Promise.resolve() 函數用來將一個物件轉型為 Promise (如果它不是一個 Promise 物件),然後立刻 resolve 它。
Promise.resolve('Success').then(function(value) {
console.log(value);
}, function(value) {
console.log('Fail');
});
// 輸出 "Success"
Promise.reject(reason)
Promise.reject() 函數用來將一個物件轉型為 Promise (如果它不是一個 Promise 物件),然後立刻 reject 它。
Promise.reject(new Error('Fail')).then(function(error) {
console.log('Success');
}, function(error) {
console.log('Fail');
});
// 輸出 "Fail"