callback 和 promise 的錯誤捕獲-暗坑集錦
最近忙于業務開發,好久沒有更新博客了,把最近開發中踩到的關于錯誤捕獲的坑,拿出來分享下;這些暗坑浪費了我大量的開發時間,只怪自己學識太淺。開始正文。
callback
運行 callbask.js
'use strict';
function cbAfter3s(callback){
setTimeout(function(){
// s2
try{
callback(null, '3s');
}catch(e){
console.error('Catch in cbAfter3s', e);
callback(new Error('Error from cbAfter3s'));
}
throw new Error('Error from cbAfter3s ASync');
}, 3e3);
throw new Error('Error from cbAfter3s Sync');
}
function handle(err, data){
console.info('Reveive: ', err, data);
if(!err){
// s2
throw new Error('Error from handle');
}
}
try{
cbAfter3s(handle);
}catch(e){
console.error('Catch in global', e);
}
process.on('uncaughtException', function(e){
console.error('Catch in process', e);
});
輸出:
Catch in global [Error: Error from cbAfter3s Sync]
Reveive: null 3s
Catch in cbAfter3s [Error: Error from handle]
Reveive: [Error: Error from cbAfter3s] undefined
Catch in process [Error: Error from cbAfter3s ASync]
總結(s):
1. try catch 只能捕獲同步拋出的錯誤
2. 不要輕易在 callback 里 throw 錯誤,不然容易形成兩次回調。
3. 代碼未捕獲的錯誤,會出現在 uncaughtException 事件上,建議做些日志記錄;不然,假如你用了進程守護程序(如pm2等),會自動重啟應用,進而湮沒日志。
4. promise 的錯誤捕獲又是不同的,不能想當然。
promise
運行 promise.js
'use strict';
// 內置P romise
var p = (new Promise(function(resolve, reject){
reject(new Error('Error from promise by reject'));
// 或者通過 throw 的方式拋出,效果相同
// throw new Error('Error from promise by throw');
}));
// 或者在 then 通過 throw 拋出錯誤,也有同樣效果
/**
var p = (new Promise(function(resolve){
resolve('Data');
}))
.then(function(res){
console.info('Receive: ', res);
throw new Error('Error from promise by throw');
});
*/
process.on('uncaughtException', function(e){
console.error('UE:Catch in process', e);
});
process.on('unhandledRejection', (reason) => {
console.info('UR:Catch in process', reason);
});
process.on('rejectionHandled', (p) => {
console.info('RH:Catch in process', p);
});
setTimeout(function(){
p.catch(function(e){
console.error('Catch in Promise', e);
});
}, 1e3);
輸出:
UR:Catch in process [Error: Error from promise by reject]
RH:Catch in process Promise { <rejected> [Error: Error from promise by reject] }
Catch in Promise [Error: Error from promise by reject]
總結(s):
1. rejectionHandled 事件的觸發條件為, promise 沒有被及時 catch 到錯誤并觸發了 unhandledRejection 事件,在這之后的一段時間里, promise 錯誤又被處理了,此時觸發 rejectionHandled ,詳情見 Node-Docs-4.4.1#process event rejectionhandled 。
2. uncaughtException 并不能捕獲 Promise 內拋出的錯誤,如果開發者是從基于 callback 的 Async 轉向 Promise 的,尤其需要注意未知錯誤的捕獲。
由于歷史代碼歷史包袱,有時我們會寫一個 promiseToCallback 的函數,類似如下代碼:
function promiseToCallback(func){
'use strict';
return function(){
let args = Array.prototype.slice.apply(arguments);
let cb = args.pop();
func.apply(null, args)
.then(function(result){
cb(null, result);
})
.catch(function(err){
log.error(err);
cb(err);
});
};
};
這時候,尤其需要當心,cb 內如果拋出錯誤,或觸發 catch 事件,導致發生兩次回調,建議直接把 cb 的錯誤通過 try-catch 處理掉。
希望這些能讓你在開發中少踩些坑。
來自: http://f2e.souche.com/blog/callback-he-promise-de-cuo-wu-bu-huo-an-keng-ji-jin/