到目前為止我們已經學習了如何通過?[`.then`](http://liubin.github.io/promises-book/#promise.then)?和?[`.catch`](http://liubin.github.io/promises-book/#promise.catch)?來注冊回調函數,這些回調函數會在promise對象變為 FulFilled 或 Rejected 狀態之后被調用。
如果只有一個promise對象的話我們可以像前面介紹的那樣編寫代碼就可以了,如果要在多個promise對象都變為FulFilled狀態的時候才要進行某種處理話該如何操作呢?
我們以當所有XHR(異步處理)全部結束后要進行某操作為例來進行說明。
各位讀者現在也許有點難以在大腦中描繪出這么一種場景,我們可以先看一下下面使用了普通的回調函數風格的XHR處理代碼。
## 2.7.1\. 通過回調方式來進行多個異步調用
multiple-xhr-callback.js
~~~
function getURLCallback(URL, callback) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
callback(null, req.responseText);
} else {
callback(new Error(req.statusText), req.response);
}
};
req.onerror = function () {
callback(new Error(req.statusText));
};
req.send();
}
// <1> 對JSON數據進行安全的解析
function jsonParse(callback, error, value) {
if (error) {
callback(error, value);
} else {
try {
var result = JSON.parse(value);
callback(null, result);
} catch (e) {
callback(e, value);
}
}
}
// <2> 發送XHR請求
var request = {
comment: function getComment(callback) {
return getURLCallback('http://azu.github.io/promises-book/json/comment.json', jsonParse.bind(null, callback));
},
people: function getPeople(callback) {
return getURLCallback('http://azu.github.io/promises-book/json/people.json', jsonParse.bind(null, callback));
}
};
// <3> 啟動多個XHR請求,當所有請求返回時調用callback
function allRequest(requests, callback, results) {
if (requests.length === 0) {
return callback(null, results);
}
var req = requests.shift();
req(function (error, value) {
if (error) {
callback(error, value);
} else {
results.push(value);
allRequest(requests, callback, results);
}
});
}
function main(callback) {
allRequest([request.comment, request.people], callback, []);
}
// 運行的例子
main(function(error, results){
if(error){
return console.error(error);
}
console.log(results);
});
~~~
這段回調函數風格的代碼有以下幾個要點。
* 直接使用?`JSON.parse`?函數的話可能會拋出異常,所以這里使用了一個包裝函數?`jsonParse`
* 如果將多個XHR處理進行嵌套調用的話層次會比較深,所以使用了?`allRequest`?函數并在其中對request進行調用。
* 回調函數采用了?`callback(error,value)`?這種寫法,第一個參數表示錯誤信息,第二個參數為返回值
在使用?`jsonParse`?函數的時候我們使用了?`bind`?進行綁定,通過使用這種偏函數(Partial Function)的方式就可以減少匿名函數的使用。(如果在函數回調風格的代碼能很好的做到函數分離的話,也能減少匿名函數的數量)
~~~
jsonParse.bind(null, callback);
// 可以認為這種寫法能轉換為以下的寫法
function bindJSONParse(error, value){
jsonParse(callback, error, value);
}
~~~
在這段回調風格的代碼中,我們也能發現如下一些問題。
* 需要顯示進行異常處理
* 為了不讓嵌套層次太深,需要一個對request進行處理的函數
* 到處都是回調函數
下面我們再來看看如何使用?`Promise#then`?來完成同樣的工作。
## 2.7.2\. 使用Promise#then同時處理多個異步請求
需要事先說明的是?`Promise.all`?比較適合這種應用場景的需求,因此我們故意采用了大量?`.then`?的晦澀的寫法。
使用了[`.then`](http://liubin.github.io/promises-book/#promise.then)?的話,也并不是說能和回調風格完全一致,大概重寫后代碼如下所示。
multiple-xhr.js
~~~
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
var request = {
comment: function getComment() {
return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
},
people: function getPeople() {
return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
}
};
function main() {
function recordValue(results, value) {
results.push(value);
return results;
}
// [] 用來保存初始化的值
var pushValue = recordValue.bind(null, []);
return request.comment().then(pushValue).then(request.people).then(pushValue);
}
// 運行的例子
main().then(function (value) {
console.log(value);
}).catch(function(error){
console.error(error);
});
~~~
將上述代碼和[回調函數風格](http://liubin.github.io/promises-book/#multiple-xhr-callback.js)相比,我們可以得到如下結論。
* 可以直接使用?`JSON.parse`?函數
* 函數?`main()`?返回promise對象
* 錯誤處理的地方直接對返回的promise對象進行處理
向前面我們說的那樣,main的?`then`?部分有點晦澀難懂。
為了應對這種需要對多個異步調用進行統一處理的場景,Promise準備了?`Promise.all`?和?`Promise.race`?這兩個靜態方法。
在下面的小節中我們將對這兩個函數進行說明。
- 前言
- 第一章 - 什么是Promise
- 1.1. 什么是Promise
- 1.2. Promise簡介
- 1.3. 編寫Promise代碼
- 第二章 - 實戰Promise
- 2.1. Promise.resolve
- 2.2. Promise.reject
- 2.3. 專欄: Promise只能進行異步操作?
- 2.4. Promise#then
- 2.5. Promise#catch
- 2.6. 專欄: 每次調用then都會返回一個新創建的promise對象
- 2.7. Promise和數組
- 2.8. Promise.all
- 2.9. Promise.race
- 2.10. then or catch?
- 第三章 - Promise測試
- 3.1. 基本測試
- 3.2. Mocha對Promise的支持
- 3.3. 編寫可控測試(controllable tests)
- 第四章 - Advanced
- 4.1. Promise的實現類庫(Library)
- 4.2. Promise.resolve和Thenable
- 4.3. 使用reject而不是throw
- 4.4. Deferred和Promise
- 4.5. 使用Promise.race和delay取消XHR請求
- 4.6. 什么是 Promise.prototype.done ?
- 4.7. Promise和方法鏈(method chain)
- 4.8. 使用Promise進行順序(sequence)處理
- 第五章 - Promises API Reference
- 5.1. Promise#then
- 5.2. Promise#catch
- 5.3. Promise.resolve
- 5.4. Promise.reject
- 5.5. Promise.all
- 5.6. Promise.race
- 第六章 - 用語集
- 第七章 - 參考網站
- 第八章 - 關于作者
- 第九章 - 關于譯者