2017-08-31 48 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:不幸的是,這就是節點式回調的本質。我用一個建議添加了答案的結尾。 –