簡體   English   中英

如何處理錯誤然后立即突破承諾鏈?

[英]How do I handle an error and then immediately break out of a promise chain?

所以我有一個Express應用程序,它使用中間件來解析JSON POST請求,然后填充req.body對象。 然后我有一個promise鏈,使用Joi對模式驗證數據,然后將其存儲在數據庫中。

我想要做的是檢查在這些進程之一之后是否拋出錯誤,通過發送狀態代碼來適當地處理它,然后完全拒絕承諾鏈。 我覺得應該有一些非常干凈和簡單的方法來做到這一點,(也許某種破壞聲明?)但我無法在任何地方找到它。 這是我的代碼。 我留下評論顯示我希望中止諾言鏈的地方。

const joi = require("joi");

const createUserSchema = joi.object().keys({
    username: joi.string().alphanum().min(4).max(30).required(),
    password: joi.string().alphanum().min(2).max(30).required(),
});

//Here begins my promise chain 
app.post("/createUser", (req, res) => {
    //validate javascript object against the createUserSchema before storing in database
    createUserSchema.validate(req.body)
        .catch(validationError => {
           res.sendStatus(400);

           //CLEANLY ABORT the promise chain here

           })
        .then(validatedUser => {
            //accepts a hash of inputs and stores it in a database 
            return createUser({
                    username: validatedUser.username,
                    password: validatedUser.password
                })
        .catch(error => {
            res.sendStatus(500);

            //CLEANLY ABORT the promise chain here

        })
        //Only now, if both promises are resolved do I send status 200
        .then(() => {
            res.sendStatus(200); 
            }                 
        )

});

你不能在中間中止一個承諾鏈。 它將在鏈中稍后調用.catch() .then().catch() (假設有兩者並假設您的承諾解決或拒絕)。

通常,您處理此方法的方法是在鏈的末尾放置一個.catch() ,它會檢查錯誤的類型並采取適當的操作。 您不會在鏈中的早期處理錯誤。 你讓最后一個.catch()處理事情。

這是我的建議:

// helper function
function err(status, msg) {
    let obj = new Error(msg);
    obj.status = status;
    return obj;
}

//Here begins my promise chain 
app.post("/createUser", (req, res) => {
    //validate javascript object against the createUserSchema before storing in database
    createUserSchema.validate(req.body).catch(validationError => {
        throw err("validateError", 400)
    }).then(validatedUser => {
            //accepts a hash of inputs and stores it in a database 
            return createUser({
                    username: validatedUser.username,
                    password: validatedUser.password
            }).catch(err => {
                throw err("createUserError", 500);
            });
    }).then(() => {
        // success
        res.sendStatus(200); 
    }).catch(error => {
        console.log(error);
        if (error && error.status) {
            res.sendStatus(error.status);
        } else {
            // no specific error status specified
            res.sendStatus(500);
        }
    });
});

這有幾個好處:

  1. 任何錯誤都會傳播到記錄它的鏈末尾的最后一個.catch() ,並在代碼中的一個位置發送適當的狀態。
  2. 成功僅在發送該狀態的一個地方處理。
  3. 這對於鏈中的更多鏈接是無限可擴展的。 如果你有更多可能有錯誤的操作,他們可以“中止”鏈的其余部分(除了最后一個.catch() ,只需拒絕一個適當的錯誤對象)。
  4. 這有點類似於在整個函數中沒有大量return value語句的設計實踐,而是累積結果然后在最后返回它,有些人認為這是復雜函數的良好實踐。
  5. 調試時,你可以在一個設置斷點.then()和一個.catch()看到許鏈的最終解決,因為整個產業鏈經過要么最后.then()或最后.catch()

.catch默認返回已解析的Promise 你想要一個被拒絕的Promsise 所以,你應該return從里面被拒絕的承諾 .catch ,使未來的.then旨意不執行:

     .catch(validationError => {
       res.sendStatus(400);
       return Promise.reject();
     })

但請注意,這將導致控制台警告:

未捕(承諾)......

所以最好在結尾添加另一個 .catch ,以抑制錯誤(以及捕獲任何其他錯誤):

 const resolveAfterMs = ms => new Promise(res => setTimeout(() => { console.log('resolving'); res(); }), ms); console.log('start'); resolveAfterMs(500) .then(() => { console.log('throwing'); throw new Error(); }) .catch(() => { console.log('handling error'); return Promise.reject(); }) .then(() => { console.log('This .then should never execute'); }) .catch(() => void 0); 

如果你想避免未來所有.then S 未來.catch上課,我想你可以返回一個Promise ,從來沒有解決,雖然這並沒有真正聽起來像一個精心設計的代碼庫的跡象:

 const resolveAfterMs = ms => new Promise(res => setTimeout(() => { console.log('resolving'); res(); }), ms); console.log('start'); resolveAfterMs(500) .then(() => { console.log('throwing'); throw new Error(); }) .catch(() => { console.log('handling error'); return new Promise(() => void 0); }) .then(() => { console.log('This .then should never execute'); }) .catch(() => { console.log('final catch'); }); 

您嘗試完成的更清晰的解決方案可能是使用快速驗證 ,這是一個簡單的joi包裝器,它為您提供快速中間件,用於驗證基於的快速請求的正文,參數,查詢,標題和cookie。你的Joi架構。

這樣,您可以簡單地處理“通用”快速錯誤處理程序中的中間件拋出的任何Joi驗證錯誤,例如:

const ev = require('express-validation');
app.use(function (err, req, res, next) {
  // specific for validation errors
  if (err instanceof ev.ValidationError) 
     return res.status(err.status).json(err);
  ...
  ...
  ... 
}

如果你不希望使用快遞驗證包,您可以編寫自己的簡單的中間件,做或多或少同樣的事情,如所描述這里 (見例如這里 )。

一種策略是將子語句中的錯誤處理分開,這些子語句具有各自的錯誤處理。 如果你從它們拋出錯誤,你將繞過主要的承諾鏈。

就像是:

return Promise.resolve().then(() => {
  return createUserSchema.validate(req.body)
    .catch(validationError => {
       res.sendStatus(400);
       throw 'abort';
    });
}).then(validatedUser => {
   // if an error was thrown before, this code won't be executed
   // accepts a hash of inputs and stores it in a database 
   return createUser({
      username: validatedUser.username,
      password: validatedUser.password
   }).catch(error => {
      // if an error was previously thrown from `createUserSchema.validate`
      // this code won't execute

      res.sendStatus(500);
      throw 'abort';
   });
}).then(() => {
   // can put in even more code here
}).then(() => {
  // it was not aborted
  res.sendStatus(200); 
}).catch(() => {
  // it was aborted
});

您可以跳過Promise.resolve().then()換行,但它是為了說明細分每個任務及其錯誤處理的一般模式而包含的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM