最近node寫的比較多,後臺應用你懂的,一個異常沒處理好,分分鐘crash給你看。在開發過程當中總結了一些經驗,分享給你們node
Error類是JS的原生類,在平常開發中也很常見,也很簡單,我在寫文檔以前去MDN
上查了下資料:api
Error類的用法很簡單,new
或者直接把Error
當成function來用都行,而後在你認爲須要拋出異常的地方throw
它。promise
JS中有幾種內置的Error類型,好比最多見的ReferrenceError
,都繼承自Error
,所以咱們本身也能夠定義本身的錯誤類型,只須要繼承Error便可,直接上MDN
的栗子:安全
class CustomError extends Error {
constructor(foo = 'bar', ...params) {
// Pass remaining arguments (including vendor specific ones) to parent constructor
super(...params);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError);
}
// Custom debugging information
this.foo = foo;
this.date = new Date();
}
}
try {
throw new CustomError('baz', 'bazMessage');
} catch(e){
console.log(e.foo); //baz
console.log(e.message); //bazMessage
console.log(e.stack); //stacktrace
}
複製代碼
try...catch
就很少說了,這裏須要提一下Promise
和await
的捕獲方式async
Promise
裏咱們通常在最後加一個.catch
,用來處理整個Promise執行鏈路中任何可能出現的異常,好比:函數
Promise.resolve()
.then(() => {
console.log(a); // 這裏會出現異常
})
.then(() => {
console.log('hi'); // 這裏不會執行
})
.catch(err => {
console.log(err); // ReferenceError
});
複製代碼
await
語法返回的也是Promise對象,不過你能夠經過try...catch
語法來接住異常post
async function sayHi() {
try {
let ret = await anotherPromiseFunction();
}
catch (err) {
console.log(err); // anotherPromiseFunction拋出的異常在這裏處理
}
}
複製代碼
自定義錯誤類
。JS原生的錯誤類型只能定義基本的語言類異常,而咱們在業務代碼中,須要頻繁地定義、拋出一些與業務強相關的異常,好比:校驗驗證碼的api,驗證碼格式不對時須要拋出一個異常,這個異常應該是跟校驗相關的,且調用者能清晰解讀而且可以根據錯誤信息作出相應處理的。優化
個人自定義錯誤類:ui
/** * @file 錯誤類型彙總 * @author arlenyang */
class ApiError extends Error {
/** * @constructor * @param {string} code 錯誤碼 * @param {string} msg 中文描述 */
constructor(code, msg) {
super(msg);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError);
}
this.code = code;
this.msg = msg;
}
toString() {
return `Api${this.stack}\n ${this.msg}, errCode: ${this.code}`;
}
}
// 錯誤類型
ApiError.MYSQL_QUERY_ERROR = 1;
ApiError.MYSQL_QUERY_ERROR_DESC = '查詢數據失敗';
// ......
複製代碼
構造函數有兩個參數,code
和message
。this
node
裏很常見在這個類裏,我用靜態變量的形式存放全部的錯誤碼和它的描述字段,其實也能夠放在一個單獨的存放靜態變量的文件裏
你還能夠擴展你的異常類作更多相關的事情,好比記錄錯誤日誌,上報或者寫入本地日誌。
另外,你還能夠自定義異常的輸出,經過重寫toString
方法。還記得以前提到過的error.stack
和Error.captureStackTrace
嗎?你能夠在toString
方法裏優化異常的輸出格式,加入額外的信息,等等
checked exception
)。而其餘異常,多是咱們的代碼自己有bug,也多是系統調用產生的error,這類異常須要調用者本身考慮了舉個栗子,寫一個讀取文件內容的api。
/** * 讀取文件 * @param {String} filepath 文件路徑 * @return {Buffer} 文件內容 */
async function readFile(filepath) {
}
複製代碼
根據這個api的行爲能夠預見幾個異常:
至於可能出現調用系統讀取文件的api出現的異常
、filepath不符合文件路徑格式
等等的問題,都不是這個api應該考慮的範圍。實現以下:
/** * 讀取文件 * @param {String} filepath 文件路徑 * @return {Buffer} 文件內容 */
async function readFile(filepath) {
// 檢查filepath是否爲空
if (!filepath) {
// 使用自定義錯誤類
throw new ApiError(
ApiError.PARAMETER_FORMAT_ERROR,
ApiError.PARAMETER_FORMAT_ERROR_DESC,
);
}
try {
let stat = await fs.stat(filepath);
// 檢查對應的文件是否爲文件類型
if (!stat.isFile()) {
throw new ApiError(
ApiError.FILE_FORMAT_ERROR,
ApiError.FILE_FORMAT_ERROR_DESC,
);
}
let content = await fs.readFile(filepath);
return content;
}
catch (err) {
// 檢查文件是否存在
if (err.code === 'ENOENT') {
throw new ApiError(
ApiError.FILE_NOT_FOUND_ERROR,
ApiError.FILE_NOT_FOUND_ERROR_DESC,
);
}
throw err;
}
}
複製代碼
對於promise的異常處理,千萬不要爲了'安全起見'把全部函數都.catch
,這可能會致使exception被吞掉,查錯時找不到異常信息
對於須要catch的promise,儘可能先處理異常,處理不了的,再向後拋
用Promise.reject(error)
代替throw error
,更優雅
promise的then(resolve, reject)
和then(resolve, null).catch()
的區別
promise.catch
裏若是沒有再reject
或throw
,以後邏輯會走到resolve
裏而非reject
Promise.resolve()
.then(() => {
console.log(a); // 這一行報錯,會被catch接住
})
.catch(err => {
console.log(err);
return 1;
})
.then(ret => {
console.log(ret); // 會執行,且打印 1
});
複製代碼