近年來,在許多JavaScript程式碼中,紛紛使用了Promise物件來處理非同步工作。而其最為人所知的好處,就是在語法上避免了callback hell的問題。
Promise基本使用
1. 建立Promise物件,以執行非同步工作:
Promise的建構式接受一個function型別的參數,表示你將要執行的非同步"工作";此外,它本身有兩個function型別的參數,先後分別代表工作成功或失敗時,需要被呼叫的callback函式(如下例中,分別命名為resolve與reject)。
var myWork = new Promise(function(resolve, reject) {
// 做些事情 ...
// 此處假設我們的工作是取一個亂數,
// 若大於0.5則表示工作成功;反之則表示工作失敗
var randomNumber = Math.random();
var workResult = randomNumber > 0.5;
//使用Timeout來模擬這個工作需要一段時間的情境
setTimeout(function(){
if (workResult) {
//此處可以傳一個參數,作為稍後成功處理時使用
resolve({data:randomNumber});
}
else {
//此處可以傳一個參數,作為稍後失敗處理時使用
reject({error:"The random number is less than 0.5!", randomNumber:randomNumber});
}
}, 3000);
});
你可以傳一個參數給resolve函式或reject函式,在稍後提到的then方法中,都可以取得。
2. 使用then方法,執行成功或失敗處理:
then方法則接受兩個function型別的參數,第一個是工作成功時的處理;第二個是工作失敗時的處理。
myWork.then(function(data){
//成功時的處理
console.log("My work is successfully done! data:", data);
}, function(error){
//失敗時的處理
console.log("Some errors occurred! error:", error);
});
使用catch處理失敗
在上例使用then方法時,也可以省略第二個參數,也就是工作失敗時的處理,而改用catch方法處理:
myWork.then(function(){
//成功時的處理
console.log("My work is successfully done!");
}).catch(function(){
//失敗時的處理,使用catch方法
console.log("Some errors occurred! Catch it!");
});
注意,如果呼叫then方法時,有使用第二個參數處理錯誤,那麼接在後面的catch就不會被執行了。
Promise的串接
在then方法傳入的function型別的參數中,可以回傳一個新的Promise物件,執行下一個工作:
myWork.then(function(){
//成功時的處理
console.log("My work is successfully done!");
//第二個工作
return new Promise(/* ... */);
}).then(function(){
//第二個工作成功時的處理
console.log("My second work is successfully done!");
}).catch(function(){
//失敗時的處理,無論是第一個還是第二個工作失敗,都會落入這裡。
console.log("Some errors occurred! Catch it!");
});
Promise的應用
以下以XMLHttpRequest為例,示範使用Promise與沒有使用Promise的差異。
沒有使用 Promise
function makeRequest(path, onsuccess, onerror) {
var req = new XMLHttpRequest();
req.addEventListener("load", function(){
if(this.status == "200") {
//request OK
onsuccess({data:this.responseText, url:this.responseURL});
}
else {
//request failed
onerror({error:"The status code is not 200!", status:this.status, url:this.responseURL});
}
});
req.open("GET", path);
req.send();
}
makeRequest("/path_a", function(data){
//成功時的處理
//下一個Request
makeRequest("/path_b", function(data){
//成功時的處理
/*
//再下一個請求...?!
makeRequest("/path_c", ...
//成功時的處理...
//看來要陷入了callback hell了,還是先就此打住吧!
});
*/
},function(error){
//失敗時的處理
});
},function(error){
//失敗時的處理
});
使用Promise
function makePromiseRequest(path) {
//使用Promise將原本的工作包裝起來
new Promise(function(resolve, reject) {
//以下內容如同原本的makeRequest,
//惟注意成功與失敗的處理函式,改用resolve與reject
var req = new XMLHttpRequest();
req.addEventListener("load", function(){
if(this.status == "200") {
//request OK
resolve({data:this.responseText, url:this.responseURL});
}
else {
//request failed
reject({error:"The status code is not 200!", status:this.status, url:this.responseURL});
}
});
req.open("GET", path);
req.send();
});
}
makePromiseRequest("/path_a").then(function(data){
//成功時的處理
//下一個Request
return makePromiseRequest("/path_b");
}).then(function(data){
//成功時的處理
//繼續下一個Request!? Easy!
return makePromiseRequest("/path_c");
}).then(function(data){
//成功時的處理
//...
}).catch(function(error){
//失敗時的處理,只要某次Request失敗,即會若入這裡;
//你可以由當初你自行傳入的error物件,來判別發生了什麼錯誤,以及該如何處理!
console.log("Some errors occurred! Catch it!", error);
});
沒有留言:
張貼留言