查看原文
其他

callback 和 promise 的错误捕获-暗坑集锦

前端大全 2022-06-29

(点击上方公众号,可快速关注)


作者:大搜车前端团队博客

链接:http://f2e.souche.com/blog/callback-he-promise-de-cuo-wu-bu-huo-an-keng-ji-jin/


最近忙于业务开发,好久没有更新博客了,把最近开发中踩到的关于错误捕获的坑,拿出来分享下;这些暗坑浪费了我大量的开发时间,只怪自己学识太浅。开始正文。


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 {  [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 的函数,类似如下代码:


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 处理掉。


希望这些能让你在开发中少踩些坑。



【今日微信公号推荐↓】

更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存