一、實現串行任務隊列
任務隊列是指 A 完成了調用 B,B 完成了調用 C(當然,這裡的 ABC 都表示異步操作)...以前的做法是嵌套回調,代碼類似於:
taskA(data1, function(res) {
if (res.state === OK) {
taskB(data2, function(res) {
if (res.state === OK) {
taskC(data3, function(res) {
if (res.state === OK) {
// ...
}
else {
// err3
}
});
}
else {
// err2
}
});
}
else {
// err1
}
});
其間每一步都可能出錯(err1, err2, err3...),錯誤處理也可能不同,代碼結構會變得非常臃腫(不可控制地「橫向發展」,最終變成「一坨」代碼)。因為每一步都是異步 + 回調,所以很難把各個 callback 分離開
Promise 最明顯的效果就是消除了這種「回調金字塔」,代碼再也不會橫向發展:
// taskX 都返回 Promise 對象
taskA(data1).then(
taskB(data2),
errHandler1
).then(
taskC(data3),
errHandler2
).then(
displayData,
errHandler3
);
如果 taskA 和 taskB 的錯誤處理相同,還可以更簡單:
// taskX 都返回 Promise 對象
taskA(data1).then(taskB(data2)).then(
taskC(data3),
errHandler12
).then(
displayData,
errHandler3
);
如果想統一處理錯誤,可以這樣做:
// taskX 都返回 Promise 對象
taskA(data1).then(taskB(data2)).then(taskC(data3)).then(displayData).catch(errHandler);
// 等價於
taskA(data1).then(taskB(data2)).then(taskC(data3)).then(displayData, errHandler);
鏈式調用 + 錯誤自動後拋,寫出來好看,讀起來也不錯
二、實現串行任務管道
任務管道是指當前任務的輸出可以作為下一個任務的輸入,形成一條數據管道,jQuery 代碼類似於:
$.post(url1, data, function(res) {
if (res.state === OK) {
$.post(url2, res.data, function(res) {
if (res.state === OK) {
$.post(url3, res.data, function(res) {
if (res.state === OK) {
// ...
}
else {
// err3
}
});
}
else {
// err2
}
});
}
else {
// err1
}
});
實際場景很常見,比如從 url1 獲取參數 userId,拿到後再從 url2 換取第三方 openId,最後再從 url3 換取 orderList,然後把結果展示給用戶,類似的邏輯都是天然的任務管道
用 Promise 可以這樣實現:
new Promise(function(resolve, reject) {
resolve(1);
}).then(function(res) {
return new Promise(function(resolve, reject) {
resolve(res + 1);
});
}).then(function(res) {
return new Promise(function(resolve, reject) {
resolve(res + 1);
});
}).then(function(res) {
console.log(res);
});
// => 3
P.S. jQuery 的 $.when() 就是一種 Promise 實現,但與 Promise A+ 規範有些差異,詳細請查看 jQuery deffered 和 promise 對象方法
三、處理運行時才能確定執行順序的串行任務
假設有 A,B,C3 個任務,執行順序不確定,有可能是 BCA、BAC 等等,運行時才能確定三者的執行順序,一旦確定順序,就要立即串行執行
此時回調嵌套已經無法滿足了,因為每一種順序都要對應一種回調嵌套,我們需要一種能夠隨意組合回調的機制,比如 Promise:
var tasks = getOrderedTasks();
var p = tasks[0];
tasks.forEach(function(item, index) {
p.then(item);
});
這樣寫的副作用是管道沒了(任務的輸出無法傳遞下去作為下一個任務的輸入),不過沒關係,很容易修改:
function catchFuture(future) {
// 處理/傳遞 future
// ...
console.log('catchFuture: ' + future);
}
var tasks = getOrderedTasks();
var p = tasks[0];
tasks.forEach(function(item, index) {
// p.then(item);
p.then(item.then(catchFuture));
});
暫無評論,快來發表你的看法吧