Promise
Promise
前言
Promise通常是一種非同步式的語法結構,在ES6將其納入了語言標準,除了解決前一章提到的回調地獄的問題之外,也提供了更強大的功能。
Promise代理了一個非同步操作的Object,或者說是一個容器,裡面保存著未來最終返回的結果。
Promise state
一個Promise物件可處於以下幾種狀態:
- pending:初始擱置狀態
- fulfilled:表示操作成功完成
- rejected:表示操作失敗
一旦狀態變化了,就不會再改變,類似於一種承諾。
以下是常被提到的一種狀態,他不處於pending
- settled/resolved:可能是fulfilled或rejected
建立Promise
以下為一個Promise物件的建構方式,首先建立一個執行器函式(executor function),在此函式帶入兩個帶有callback性質的參數分別為resolve
、reject
,被調用時即會將pending狀態轉換至fulfilled或rejected,調用方式如下:
- 當操作成功時
主動調用resolve()並傳入合法的value,此時Promise狀態會轉移至fulfilled
- 當操作失敗或例外時
主動調用reject()並傳入失敗的reason,此時Promise狀態會轉移至rejected
const myFirstPromise = new Promise((resolve, reject) => {
// 執行一些非同步作業,最終呼叫:
resolve(someValue); // 實現
// 或
reject("failure reason"); // 拒絕
});
以下是一個HTTP Request用Promise建構的範例
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
};
Promise.prototype.then方法
then
方法是Promise主要的精神,其作用是為Promise instance添加狀態改變時的callback function,他會return一個promise
,then
function的第一個參數是resolved
狀態的callback function,第二個參數(optional)是rejected
狀態的callback function 。
promise.then(onFulfilled, onRejected)
使用範例
var p1 = new Promise( (resolve, reject) => {
resolve('Success!');
// or
// reject ("Error!");
} );
p1.then( value => {
console.log(value); // Success!
}, reason => {
console.log(reason); // Error!
} );
錯誤處理
當函式拋出一個error或是回傳一個resolved
狀態的Promise,then
都會return一個resolved
的Promise。
const promise = new Promise((resolve, reject) => {
reject ("Error!");
// or
// throw new Error('test');
})
promise.then(null, reason => {
console.log(reason.toString()); // Error!
});
以上的方式也可用Promise.prototype.catch()
達到,此方法會return一個resolved
狀態的新Promise,catch
在處理 promise 組合的錯誤時很有幫助,一般較推薦此寫法。
.catch(reason => {
console.log(reason); // Error!
});
鏈式調用(Chaining)
一個常見的需求是連續執行兩個或多個非同步操作,這樣的情形下每個操作會將其返回的結果帶到下一個操作執行,透過promise chain即可完成這種需求。
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
也可以箭頭函式(arrow functions)來縮減此寫法,如下所示
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
在一個失敗操作catch
之後可以繼續使用鏈式操作,以幫助後續動作繼續完成。
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this');
})
.catch(() => {
console.log('Do that');
})
.then(() => {
console.log('Do this whatever happened before');
});
- 創建的Promise為fulfilled狀態,印出log
- 第一個then拋出一個error,回傳新的Promise為rejected狀態,跳過輸出
- catch接到rejected狀態,並回傳新的Promise為fulfilled狀態,印出log
- 印出log
“Initial”
“Do that”
“Do this whatever happened before”
在error傳播在鏈式操作中時,一但前一個操作有拋出error,若之後沒有實作rejected
狀態的callback function,則Promise就會不斷往後傳遞error,直到實作catch
才會返回rejected
的Promise,後續也能在接續其他操作。
doSomething()
.then(result => doSomethingElse(value))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);
範例
new Promise((resolve, reject) => {
reject();
})
.then(() => async2(), undefined)
.then(() => async3(), undefined)
.then(undefined, (err) => errorHandler1())
.then(() => async4(), (err) => errorHandler2())
.then(undefined, (err) => console.log('Don\'t worry about it'))
.then(() => console.log('All done!'), undefined)
- 創建的promise物件的狀態是rejected
- async2不會被執行,新的Promise物件直接是rejected狀態
- async3不會被執行,新的Promise物件直接是rejected狀態
- errorHandler1()被執行,新的Promise物件為fulfilled狀態
- async4()被執行,新的Promise物件為fulfilled狀態
- 跳過輸出字串,新的Promise物件為fulfilled狀態,回傳值繼續傳遞
- 輸出字串
“All done!”
靜態方法 (Static Methods)
Promise中共有四個 static method:
Promise.resolve(value)
Promise.reject(reason)
Promise.all(iterable)
Promise.race(iterable)
resolve & reject
Promise.resolve
與 Promise.reject
會直接產生一個fulfilled或rejected的promise。
Promise.resolve()
.then(value => console.log('Immediate resolved!'))
Promise.reject(new Error('Error!!!')).
.then(() => { /* will not go here */ })
.catch(err => console.log(`Error shows up here ${err}`))
all & race
Promise.all
與 Promise.race
皆可傳入一個iterable
的參數,將多個傳入的Promise instance包裝成一個新的Promise回傳,其狀態改變有不同的結果。
Promise.all
- fulfill:所有傳入的Promises皆 fulfill。Resolved value為與傳入的Promises相同順序value array
- reject:任一個Promise被reject即reject。Error reason為與第一個被reject的Promise相同
Promise.race
- 任何一個Promise被fulfill或reject,皆回傳此狀態的promise
const promise = Promise.all([p1, p2, p3]);
// or
const promise = Promise.race([p1, p2, p3]);
Ref
- https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises
- https://legacy.gitbook.com/book/eyesofkids/javascript-start-es6-promise/details
- http://es6.ruanyifeng.com/#docs/promise
- https://www.udemy.com/the-complete-javascript-course/learn/v4/t/lecture/9939774?start=780
- https://weihanglo.tw/posts/2017/javascript-concurrency-promise/
Author Yujay
LastMod 2018-07-01