2017-08-31 46 views
1

我正在使用回调在redis db async上设置一些ip。在异步回调中捕获尝试错误

我想抓住错误,并通过快递发送给我的错误处理中间件。

我在select方法上产生了一个错误,但它没有发现我的错误。

见下面的代码:

module.exports = (req, res, next) => { 
    const redis = require('redis') 
    const client = redis.createClient() 
    try { 
    client.select('2d', (err) => { // instead of 2 number, i use '2d' string, to generate an error on purpose 
     const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress 
     client.set(ip, true, 'EX', 120, (err, rep) => { 
     return next() 
     }) 
    }) 
    } catch (err) { 
    err.type = 'SilentSystem' 
    next(err) 
    } 
} 

回答

2

documentation of the redis npm package,很明显,它采用标准的节点样式的回调。在标准的Node-style回调中,传递给您提供的回调的第一个参数是错误或null;这就是报告错误的地方和方式。 (你甚至在你的代码中定义了一个叫err的参数。)它们不能被try/catch捕获,因为在错误发生很久以前,控制已经超出了try/catch(事实上它已经不在函数中)。

所以,你会处理这样的:

module.exports = (req, res, next) => { 
    const redis = require('redis') 
    const client = redis.createClient() 
    client.select('2d', (err) => { // instead of 2 number, i use '2d' string, to generate an error on purpose 
    if (err) { 
     // Handle error here 
     err.type = 'SilentSystem' 
     next(err) 
    } else { 
     // Handle success here 
     const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress 
     client.set(ip, true, 'EX', 120, (err, rep) => { 
     if (err) { 
      err.type = 'SilentSystem' 
      next(err) 
     } else { 
      next() 
     } 
     }) 
    } 
    }) 
} 

在评论你说:

我实际的代码是一个比较复杂的,所以我想避免打电话,以免使用try重复呼叫if(err)next(err)。在这里处理错误有什么更好的方法(不太详细)?

不幸的是,这就是节点式回调的本质。一种选择是给自己一个过滤功能,让所有这些结果都通过,所以你的常见错误处理代码就在那里。

但是:您可能会考虑使用一个“promisifies”节点式回调函数库,以便您可以使用promise,并使用它们的链接机制来完成集中式错误处理。 (一个这样的包是promisify,但还有其他的。)随着client.selectclient.set“promisified”版本等,这些代码可能看起来像这样:

module.exports = (req, res, next) => { 
    const redis = require('redis') 
    const client = makeNiftyPromiseVersionOf(redis.createClient()) 
    client.select('2d') 
    .then(data => { 
     const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress 
     return client.set(ip, true, 'EX', 120) 
    }) 
    .then(() => { 
     next() 
    }) 
    .catch(err => { 
     err.type = 'SilentSystem' 
     next(err) 
    }) 
} 

注意的错误处理是如何在巩固结束;如果client.select中有错误,则跳过then回调,并将控制传递给catch。如果不是,则执行then回调并执行client.set,并且其中的任何错误也将转到该catch

这也将打开大门,使用ES2017的async/await在同步风格编写异步代码:

module.exports = (req, res, next) => { 
    (async() => { 
    const redis = require('redis') 
    const client = makeNiftyPromiseVersionOf(redis.createClient()) 
    try { 
     const data = await client.select('2d'); 
     const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress 
     await client.set(ip, true, 'EX', 120) 
     next() 
    } catch (err) { 
     err.type = 'SilentSystem' 
     next(err) 
    } 
    })(); 
} 

附注:我会删除require呼叫出来的导出函数的 ,而是在模块级别上执行:

const redis = require('redis') 
module.exports = { 
    // ... 
} 
+0

我的实际代码有点复杂所以我试图避免调用if(err),然后使用try返回next(err)。在这里处理错误有什么更好的方法(不太详细)? –

+0

*尝试避免重复调用,如果(err)和next(err)应该读取 –

+0

@ S.Schenk:不幸的是,这就是节点式回调的本质。我用一个建议添加了答案的结尾。 –