管理異步程式設計的一個是錯誤處理。同步代碼中隻要使用try語句塊包裝一段代碼很容易一下子處理所有的錯誤。
try{
f();
g();
h();
} catch(e){
//這裡用來下得出現的錯誤
}
try語句塊
但對于異步的代碼,多步的處理通常會被分隔到事件隊列的單獨輪次中,是以,不可能将它們包裝在一個try語句塊中。事實上異步的API甚至根本不可能抛出異常,因為,當一個異步的錯誤發生時,沒有一個明顯的執行上下文來抛出異常!相反,異步的API傾向于将錯誤表示為回調函數的特定參數,或使用一個附加的錯誤處理回調函數。例如,一個涉及下載下傳檔案的異步API可能會有一個額外的回調函數來處理網絡錯誤。
downloadAsync('http://cnblogs.com/wengxuesong',function(text){
console.log('file contents:'+text);
},function(error){
console.log('error:'+error);
});
如果下載下傳多個檔案,可以像62條講的,使用回調函數嵌套起來。
downloadAsync('a.txt',function(a){
downloadAsync('b.txt',function(b){
downloadAsync('c.txt',function(c){
console.log('Contents:'+a+b+c);
},function(error){
console.log('error:'+error);
});
},function(error){
console.log('error:'+error);
});
},function(error){
console.log('error:'+error);
});
上面代碼上,每一步的處理都使用了相同的錯誤處理邏輯,然而我們在多個地方重複了相同的代碼。在程式設計領域裡,應該努力堅持避免重複代碼。通過共享作用域中定義一個錯誤處理的函數,将重複代碼抽象出來。
function onError(error){
console.log('Error:'+error);
}
downloadAsync('a.txt',function(a){
downloadAsync('b.txt',function(b){
downloadAsync('c.txt',function(c){
console.log('Contents:'+a+b+c);
},onError);
},onError);
},onError);
如果使用工具函數downloadAllAsync将多個步驟合并到一個複合的操作中,那麼,隻需要提供一個錯誤處理的回調函數。
downloadAllAsync(['a.txt','b.txt','c.txt'],function(abc){
console.log('Contents:'+abc[0]+abc[1]+abc[2]);
},function(error){
console.log('Error:'+error);
});
回調函數錯誤參數
另一種錯誤處理API的風格是Node.js平台使用的。該風格隻需要一個回調函數,該回調函數的第一個參數如果有錯誤發生就表示一個錯誤。否則就是一個假值,比如null。對于這類API,我們可以定義一個通用的錯誤處理函數,需要使用if語句來控制每個回調函數。
function onError(error){
console.log('Error:'+error);
}
downloadAsync('a.txt',function(error,a){
if(error){
onError(error);
return;
}
downloadAsync('b.txt',function(error,b){
if(error){
onError(error);
return;
}
downloadAsync('c.txt',function(error,c){
if(error){
onError(error);
return;
}
console.log('Contents:'+a+b+c);
});
});
});
程式員通常會放棄if語句而使用大括号結構跨越多行的約定,以使得錯誤處理更簡潔、更集中。
function onError(error){
console.log('Error:'+error);
}
downloadAsync('a.txt',function(error,a){
if(error)return onError(error);
downloadAsync('b.txt',function(error,b){
if(error)return onError(error);
downloadAsync('c.txt',function(error,c){
if(error)return onError(error);
console.log('Contents:'+a+b+c);
});
});
});
也可以使用一個抽象合并步驟來幫助消除重複
var filenames=['a.txt','b.txt','c.txt'];
downloadAllAsync(filenames,function(error,abc){
if(error){
console.log('Error:'+error);
return;
}
console.log('Contents:'+abc[0]+abc[1]+abc[2]);
});
try...catch語句和在異步API中典型的錯誤處理邏輯的一個實際差異是,try語句使得定義一個"捕獲所有"的邏輯很容易導緻程式員難以忘懷整個代碼區的錯誤處理。而上面給出的異步API,非常容易忘記在程序的任意一步提供錯誤處理。這将導緻錯誤被丢棄。忽視錯誤處理的程式會令使用者非常沮喪:應用程式出錯時沒有任何的回報。類似的,預設的錯誤不好調試。因為沒有提供問題來源的線索。最好是做好防禦,即使用異步API需要警惕,確定明确地處理所有的錯誤狀态條件。
提示
- 通過編寫共享的錯誤處理函數來避免複制和粘貼錯誤處理代碼
- 確定明确地處理所有的錯誤條件以避免丢棄錯誤
版權聲明
翻譯的文章,版權歸原作者所有,隻用于交流與學習的目的。
原創文章,版權歸作者所有,非商業轉載請注明出處,并保留原文的完整連結。