callback 和 promise 的錯誤捕獲-暗坑集錦

wm1998 8年前發布 | 30K 次閱讀 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/

 本文由用戶 wm1998 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!