2015-09-25 103 views
1

背景的允許傳播JS承諾:錯誤

比方說,我與+的NodeJS工作快。我在Express中註冊了一些錯誤處理程序,它將以適當的方式處理我的應用程序中可能出現的所有錯誤。

因此,我可以把我的應用程序錯誤時,我必須這樣做。如果有未處理的錯誤,我讓它傳播,直到它到達一個錯誤處理程序。然而,在嘗試在承諾鏈內部拋出錯誤時,我遇到了一個問題。看看下面的例子:

function find() { 
    // consider this to be a promise from a library such as Bluebird 
    return new Promise(function (resolve, reject) { 
     // ... logic ... 
    }); 
} 

function controller (req, res) { 

    // ... omitted ... 

    find().then(function (result)) { 
     if (result) { 
      // let 'res' be the Express response object 
      res.send("It exists!"); 
     } else { 
      // let SpecificError be a prototypical subclass of Error 
      throw new SpecificError("Couldn't find it."); 
     } 
    }).catch(function (error) { 
     // throw the error again, so that the error handler can finish 
     // the job 
     throw error; 
    }); 
} 

雖然我一直在期待,我再扔最終至少擊中一般錯誤處理程序中的錯誤,我也看不到我發送給我的應用程序掛起的請求,以及我正在使用的承諾庫抱怨Unhandled rejection

問題

很簡單,我想知道怎麼解決,我似乎是處理不當,我在我的諾言鏈拋出一個錯誤創建拒絕的事實。

編輯:爲澄清什麼(具體)的錯誤處理程序和controller功能的,看下面的評論。

+0

什麼是你期望它命中的「通用錯誤處理程序」?如果'controller'是一個路由,你應該使用第三個'next'參數並執行'next(error)' – loganfsmyth

+0

我想你會看到我想要做的事情 - 'next(error)'聽起來像是正確的回答 – nmagerko

+0

如果你顯示你如何調用controller(),我們會更好地瞭解你在做什麼以及哪些選項最有意義。 – jfriend00

回答

3

假設你已經綁定的功能與類似

app.get('/', controller); 

當快遞電話controller,它已經取得了100%的控制你。如果從controller同步引發異常,Express很不錯,並且會將其視爲您的錯誤。但是,只要調用任何異步代碼,就有責任決定如何處理任何錯誤。

在高速的情況下,你有兩個選擇:

  1. 由於您是通過reqres,您可以捕獲錯誤併發送你想返回給用戶作出的任何反應。
  2. controller在Express中實際上具有function(req, res, next)的功能簽名。這是一種非常常見的格式。

next回調預期被稱爲如果您在響應具有寫任何東西。如果您不帶任何參數調用next(),則這告訴Express看起來繼續處理它具有的一組URL處理程序,試圖找到一個將處理該請求的處理程序,如果找不到任何處理程序,則返回404。

但是,如果您將參數傳遞到next(如next(err)),Express將跳過其餘的URL處理程序,而不是尋找錯誤處理程序。 Express允許您註冊自定義處理程序,但如果沒有找到,它將返回500.

那麼你應該怎麼做你的例子?你可能想是這樣

function controller (req, res, next) { 
    find().then(function (result)) { 
     if (!result) throw new SpecificError("Couldn't find it."); 

     res.send("It exists!"); 
    }).catch(next); 
} 

這意味着,如果任何異常的承諾鏈內拋出,next功能將它稱爲,以及Express將在那裏接手。

+0

是的,我認爲這是對我如何使用'req','res'和'next'的誤解。 – nmagerko

1

無極處理程序是「扔安全」。這意味着你在任何承諾處理程序中拋出的任何異常都會被自動捕獲並轉化爲被拒絕的承諾。這是規範如何對承諾以及它們如何工作的書面(除了jQuery的承諾的某些版本,但是這只是因爲他們沒有按照規範)。

所以,如果你從你的承諾庫中得到「未處理的拒絕」,那就是一個有用的警告,告訴你你有一個被拒絕的承諾,它沒有處理它,所以拒絕被默默地忽略,通常是一個編碼錯誤。

而且,事實上在你controller()功能,你必須正是:

function controller (req, res) { 

    // ... omitted ... 

    find().then(function (result)) { 
     if (result) { 
      // let 'res' be the Express response object 
      res.send("It exists!"); 
     } else { 
      // let SpecificError be a prototypical subclass of Error 
      throw new SpecificError("Couldn't find it."); 
     } 
    }).catch(function (error) { 
     // throw the error again, so that the error handler can finish 
     // the job 
     throw error; 
    }); 
} 

如果你站上罰球線的地方說throw new SpecificError,那麼將會把承諾變成拒絕承諾。這會導致你的.catch()處理程序被調用。在那個處理程序中,你再次拋出這個將承諾作爲被拒絕的承諾。因此,以find().then(...)開頭的最初承諾現在將被拒絕承諾。但是,有沒有更多的拒絕處理,並不必返回從controller()的承諾。所以,當時你有一個未處理的拒絕承諾。這通常是編碼錯誤。

您有如何糾正這種編碼錯誤的幾種選擇:

  1. 你可以通過調用某種錯誤處理功能,你傳遞錯誤給自己處理的錯誤在.catch()處理和傳遞res的說法,然後不要拋出錯誤。

  2. 您可以從controller()函數返回承諾,並且無論調用哪個代碼,該函數都可以處理拒絕的承諾。

+0

爲了解釋爲什麼我得到這個錯誤,你肯定會得到滿意的結果。由於其他答案提供了特定於Express的解決方案,因此我將不得不將其標記爲解決方案。 – nmagerko