最近忙于业务开发,好久没有更新博客了,把最近开发中踩到的关于错误捕获的坑,拿出来分享下;这些暗坑浪费了我大量的开发时间,只怪自己学识太浅。开始正文。
callback
运行 callbask.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
'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); }); |
输出:
1 2 3 4 5 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
'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); |
输出:
1 2 3 |
UR:Catch in process [Error: Error from promise by reject] RH:Catch in process Promise { [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#processeventrejectionhandled。
2. uncaughtException
并不能捕获 Promise
内抛出的错误,如果开发者是从基于 callback 的 Async 转向 Promise
的,尤其需要注意未知错误的捕获。
由于历史代码历史包袱,有时我们会写一个 promiseToCallback
的函数,类似如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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 处理掉。
希望这些能让你在开发中少踩些坑。