1.什么是Promise
所謂 Promise,就是JS的一個(gè)對(duì)象,用來(lái)傳遞異步操作結(jié)果的消息。它代表了某個(gè)未來(lái)才會(huì)知道結(jié)果的事件(通常是一個(gè)異步操作),并且這個(gè)事件提供了統(tǒng)一的 API,用來(lái)對(duì)異步調(diào)用需要的兩種結(jié)果(成功與失敗)進(jìn)行處理。
Promise 使用示例:
1.使用new Promise(fn)創(chuàng)建并得到一個(gè)Promise對(duì)象,Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù),該函數(shù)的兩個(gè)參數(shù)分別是resolve方法和reject方法。
2.在 fn 中指定異步等處理
(1) 如果異步操作成功,則用 resolve 方法將 Promise 對(duì)象的狀態(tài),從"未完成" 變?yōu)?成功"(即從 pending 變?yōu)?resolved)。
(2) 如果異步操作失敗,則用 reject 方法將 Promise 對(duì)象的狀態(tài),從"未完成"變?yōu)?失敗"(即從 pending 變?yōu)?rejected)。
3.通過(guò)promise的then方法,對(duì)不同狀態(tài)的promise進(jìn)行處理。
var promise = new Promise(function(resolve, reject) {
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function onFullfilled(value) {
// success
}, function onRejected(error) {
// failure
});
2.使用Promise與正常異步回調(diào)的區(qū)別
在callback的模型里邊,我們假設(shè)需要執(zhí)行一個(gè)異步隊(duì)列,代碼看起來(lái)可能像這樣。
var adminIndex = function(params, callback) {
storeAdmin.getApiTokens(function(err, tokens){
if ( err ) {
callback(err);
return;
}
storeAdmin.getApiServices(function(err, apiServices){
if ( err ) {
callback(err);
return;
}
storeAdmin.getSocketioServices(function(err, socketioServices){
if ( err ) {
callback(err);
return;
}
callback(0, {status : true, data : {api_tokens : tokens,api_services : apiServices,socketio_services : socketioServices}});
});
});
});
};
這就是常說(shuō)的回調(diào)金字塔,當(dāng)異步的任務(wù)很多的時(shí)候,維護(hù)大量的callback將是一場(chǎng)災(zāi)難。而Promise則是把類似的異步處理對(duì)象和處理規(guī)則進(jìn)行規(guī)范化, 并采用統(tǒng)一的接口來(lái)編寫(xiě),除promise對(duì)象規(guī)定的方法(這里的 then 或 catch )以外的方法都是不可以使用的, 而不會(huì)像回調(diào)函數(shù)方式那樣可以自己自由的定義回調(diào)函數(shù)的參數(shù),從而必須嚴(yán)格遵守固定、統(tǒng)一的編程方式來(lái)編寫(xiě)代碼。各個(gè)階段拋出的錯(cuò)誤都可以在一個(gè)catch下統(tǒng)一處理,同時(shí)promise的then方法會(huì)返回另一個(gè)promise對(duì)象,以便于形成promise管道,這種返回promise對(duì)象的方式能夠支持開(kāi)發(fā)人員把異步操作串聯(lián)起來(lái),從而避免了回調(diào)金子塔的狀況,達(dá)到一個(gè)"線性編碼、異步執(zhí)行"的效果。
3.用.then 或 .catch 添加promise對(duì)象的處理函數(shù)
(1).Promise 狀態(tài):
Promise存在三個(gè)狀態(tài),分別是Fulfilled、 Rejected、 Pending。其創(chuàng)建后狀態(tài)的初始化值為pending,當(dāng)promise中的異步處理任務(wù)得到結(jié)果后,設(shè)置對(duì)應(yīng)的Promise狀態(tài)。而在使用Promise.resolve方法的時(shí)候,可以認(rèn)為它的作用就是將異步操做返回的結(jié)果填充(Fulfilled)到promise對(duì)象并返回這個(gè)promise對(duì)象。

之后根據(jù)promise的不同狀態(tài),使用then方法對(duì)其進(jìn)行相應(yīng)的處理。
asyncFunction().then(function onFulfilled(value) {
console.log(value);
}, function onRejected(error) {
console.log(error);
});
(2).Promise catch:
Promise提供then方法加載回調(diào)函數(shù),或者使用catch方法捕捉執(zhí)行過(guò)程中拋出的錯(cuò)誤。
aPromise.then(function taskA(value){
// task A
}).then(function taskB(vaue){
throw new Error(’taskB has a bug’);
}).catch(function onRejected(error){
console.log(error);
});
以上數(shù)據(jù)處理流程相當(dāng)于:
aPromise.then(function taskA(value){
// task A
}).then(function taskB(vaue){
throw new Error(’taskB has a bug’);
}).then(undefined, function onRejected(error){
console.log(error);
});
使用promise中的 .then方法 、 .catch方法創(chuàng)建一個(gè)異步獲取網(wǎng)絡(luò)數(shù)據(jù)的流程
我們可以對(duì)過(guò)程中的匿名函數(shù)進(jìn)行封裝, 大概像這樣: getURL(URL).then(parseJson(response)).then(checkResCode(json)).then(handleResponse(json)).catch(....)。可以發(fā)現(xiàn)這段代碼近乎達(dá)到了自然語(yǔ)言的程度,十分優(yōu)雅。
3.Promise chain:
每次調(diào)用promise的then方法后都會(huì)返回一個(gè)新的promise對(duì)象,通過(guò)這種特性我們可以創(chuàng)建對(duì)應(yīng)的promise chain(類似RAC中的signalOperation, 每個(gè)signal的operation方法都返回一個(gè)signal)。
function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
對(duì)應(yīng)的流程圖:

4.Promise與RAC的異同
以上的promise處理流程如果使用RAC來(lái)表示的話可以表示成下圖代碼:


RAC的CreateSignal 類似于創(chuàng)建一個(gè)promise;sendNext, sendError 可以看成resolve與reject操作;RAC中的try->try->catch 與 promise中的then->then-> catch相似。從相同的代碼格式我們可以看出,RAC與Promise都是在初始化的時(shí)候,就規(guī)劃好數(shù)據(jù)處理流程,使得代碼更加直觀與優(yōu)雅,當(dāng)然在兩者比較舉例的同時(shí)我們可以排除掉一些不影響宏觀層面上不同點(diǎn),比如:RAC信號(hào)需要被訂閱,而Promise在創(chuàng)建好后即執(zhí)行內(nèi)部代碼等。
除去以上的異同點(diǎn),非常值得關(guān)注的就是RAC與Promise都采用了高階函數(shù)的思想。何為高階函數(shù)?高階函數(shù)是至少滿足以下兩個(gè)條件其中之一的函數(shù):1.接收一個(gè)或多個(gè)函數(shù)作為輸入。2.輸出一個(gè)函數(shù)。(同時(shí)FP下高階函數(shù)都有一個(gè)共性:可以鏈?zhǔn)秸{(diào)用)。比如promise中的then, catch使用了函數(shù)參數(shù), RAC的try, catch使用了block就是很好的例子。再比如swift中提供的filter, reduce 與map等。那么它們使用高階函數(shù)有什么好處呢?能夠?qū)F(xiàn)有的代碼由許多粒度非常小的功能函數(shù)來(lái)組合,如果將粒度縮到足夠小的話,這些小粒度的功能函數(shù)能夠達(dá)到極強(qiáng)的通用性,有通用性就可以對(duì)其進(jìn)行下沉, 使其能夠?yàn)槠渌K使用。最后,原先的信號(hào)流或者數(shù)據(jù)流就會(huì)由大量的小粒度功能函數(shù)構(gòu)成,從而把繁瑣而又乏味的任務(wù)抽象出來(lái),這將使代碼顯的直觀與優(yōu)雅。
修改后的RAC代碼:

修改后的promise代碼:

RAC與Promise還有一個(gè)相同的優(yōu)點(diǎn),縱向加載。在SDK寫(xiě)網(wǎng)絡(luò)接口回調(diào)的時(shí)候,往往我們需要驗(yàn)證各種數(shù)據(jù)的正確性,condition1, condition2, condition3...每個(gè)驗(yàn)證條件都是一個(gè)if-else, 條件一多,if-else的多層嵌套讓人眩暈,不僅不利于它人review,對(duì)于編碼者而言,時(shí)間一久也會(huì)頭大。同時(shí)多層的嵌套也非常容易漏掉拋往上層的錯(cuò)誤通知。這種通過(guò)嵌套if-else的寫(xiě)法稱之為橫向發(fā)展,這樣的代碼很快就會(huì)亂成一團(tuán),無(wú)法管理。