最近忙於業務開發,好久沒有更新部落格了,把最近開發中踩到的關於錯誤捕獲的坑,拿出來分享下;這些暗坑浪費了我大量的開發時間,只怪自己學識太淺。開始正文。
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 處理掉。
希望這些能讓你在開發中少踩些坑。