gpt4 book ai didi

javascript - 在 Promises 中实现错误区分

转载 作者:行者123 更新时间:2023-11-30 15:24:50 25 4
gpt4 key购买 nike

背景

我有一个使用 MongoDB、Node.js 和 Express 的 REST API,它向我的 NoSQL DB 发出请求,并且根据不同的结果,我想区分我发送给客户的错误。

问题

我的代码的当前版本有一个通用的错误处理程序,并且总是向客户端发送相同的错误消息:

api.post("/Surveys/", (req, res) => {
const surveyJSON = req.body;
const sender = replyFactory(res);

Survey.findOne({_id: surveyJSON.id})
.then(doc => {
if(doc !== null)
throw {reason: "ObjectRepeated"};

//do stuff
return new Survey(surveyJSON).save();
})
.then(() => sender.replySuccess("Object saved with success!"))
.catch(error => {
/*
* Here I don't know if:
* 1. The object is repeated
* 2. There was an error while saving (eg. validation failed)
* 3. Server had a hiccup (500)
*/
sender.replyBadRequest(error);
});
});

这是一个问题,因为客户端总是会得到相同的错误信息,无论如何我需要错误区分!

研究

我找到了一个可能的解决方案,基于逻辑和错误/响应处理的划分:
  • Handling multiple catches in promise chain

  • 但是,我不明白一些事情:
  • 至少在我的示例中,我看不到如何将逻辑与响应分开。回复 将取决于 毕竟逻辑上!
  • 我想避免错误子类化和层次结构。首先是因为我不使用 bluebird ,而且我不能子类化答案建议的错误类,其次是因为我不希望我的代码包含十亿个不同的错误类,这些错误类在 future 会发生变化。

  • 我的想法,我也不喜欢

    使用这种结构,如果我想要区分错误,我唯一能做的就是检测发生的错误,使用该信息构建一个对象,然后将其抛出:
    .then(doc => {
    if(doc === null)
    throw {reason: "ObjectNotFound"};

    //do stuff
    return doc.save();
    })
    .catch(error => {
    if(error.reason === "ObjectNotFound")
    sendJsonResponse(res, 404, err);
    else if(error.reason === "Something else ")
    sendJsonResponse(/*you get the idea*/);
    else //if we don't know the reasons, its because the server likely crashed
    sendJsonResponse(res, 500, err);
    });

    我个人并不觉得这个解决方案特别有吸引力,因为这意味着我将拥有一个巨大的 if then else我的 catch 中的一系列语句堵塞。

    此外,如上一篇文章所述,通用错误处理程序通常不受欢迎(并且有充分的理由 imo)。

    问题

    如何改进此代码?

    最佳答案

    目标

    当我开始这个线程时,我有两个目标:

  • 有错误区分
  • 避免使用 if then else普通捕手中的厄运

  • 我现在提出了两个截然不同的解决方案,我现在将其发布在这里,以供将来引用。

    解决方案 1:带有 Errors 对象的通用错误处理程序

    此解决方案基于 @Marc Rohloff 中的解决方案,但是,我没有一个函数数组并循环遍历每个函数,而是有一个包含所有错误的对象。

    这种方法更好,因为它更快,并且不需要 if验证,这意味着你实际上做的逻辑更少:
    const errorHandlers = {
    ObjectRepeated: function(error){
    return { code: 400, error };
    },
    SomethingElse: function(error){
    return { code: 499, error };
    }
    };

    Survey.findOne({
    _id: "bananasId"
    })
    .then(doc => {

    //we dont want to add this object if we already have it
    if (doc !== null)
    throw { reason: "ObjectRepeated", error:"Object could not be inserted because it already exists."};

    //saving empty object for demonstration purposes
    return new Survey({}).save();
    })
    .then(() => console.log("Object saved with success!"))
    .catch(error => {
    respondToError(error);
    });

    const respondToError = error => {
    const errorObj = errorHandlers[error.reason](error);

    if (errorObj !== undefined)
    console.log(`Failed with ${errorObj.code} and reason ${error.reason}: ${JSON.stringify(errorObj)}`);
    else
    //send default error Obj, server 500
    console.log(`Generic fail message ${JSON.stringify(error)}`);
    };

    该解决方案实现:
  • 部分误差区分(我会解释原因)
  • 避免 if then else厄运。

  • 该解决方案仅具有部分误差区分。这是因为您只能通过 throw {reaon: "reasonHere", error: "errorHere"} 区分您专门构建的错误。机制。

    在此示例中,您将能够知道文档是否已经存在,但如果保存所述文档时出现错误(假设是验证文档),那么它将被视为“通用”错误并作为 500 抛出。

    为了实现完全的错误区分,您必须使用 nested Promise anti pattern如下所示:
    .then(doc => {

    //we dont want to add this object if we already have it
    if (doc !== null)
    throw { reason: "ObjectRepeated", error:"Object could not be inserted because it already exists." };

    //saving empty object for demonstration purposes
    return new Survey({}).save()
    .then(() => {console.log("great success!");})
    .catch(error => {throw {reason: "SomethingElse", error}});
    })

    它会起作用......但我认为这是避免反模式的最佳实践。

    解决方案 2:通过 co 使用 ECMA6 生成器.

    此解决方案使用 Generators通过图书馆 co .打算在不久的将来用类似于 async/await 的语法替换 Promise这个新功能允许您编写读起来像同步(嗯,几乎)的异步代码。

    要使用它,首先需要安装 co , 或类似 ogen .我非常喜欢 co,所以这就是我将在这里使用的。
    const requestHandler = function*() {

    const survey = yield Survey.findOne({
    _id: "bananasId"
    });

    if (survey !== null) {
    console.log("use HTTP PUT instead!");
    return;
    }

    try {
    //saving empty object for demonstration purposes
    yield(new Survey({}).save());
    console.log("Saved Successfully !");
    return;
    }
    catch (error) {
    console.log(`Failed to save with error: ${error}`);
    return;
    }

    };

    co(requestHandler)
    .then(() => {
    console.log("finished!");
    })
    .catch(console.log);

    生成器函数 requestHandleryield对库的所有 promise ,它将解决它们并相应地返回或抛出。

    使用这种策略,您可以像编写同步代码一样有效地编写代码(使用 yield 除外)。

    我个人更喜欢这种策略,因为:
  • 您的代码易于阅读并且看起来是同步的(同时仍然具有异步代码的优点)。
  • 不要必须在每个地方构建和抛出错误对象,您可以立即发送消息。
  • 而且,您可以通过 return 中断代码流.这在 promise 链中是不可能的,因为在那些你必须强制 throw (很多时候是无意义的)并捕获它以停止执行。

  • 生成器函数只有在传入库 co 后才会执行。 ,然后返回一个 Promise,说明执行是否成功。

    该解决方案实现:
  • 误差区分
  • 避免if then else hell 和广义的捕手(虽然你会在你的代码中使用 try/catch,如果你需要一个广义的捕手,你仍然可以使用它)。

  • 在我看来,使用生成器更灵活,并且更易于阅读代码。并非所有情况都是生成器使用的情况(如视频中的 mpj 建议),但在这种特定情况下,我相信它是最佳选择。

    结论

    解决方案一 : 解决问题的经典方法,但存在 Promise 链接固有的问题。您可以通过嵌套 promise 来克服其中的一些问题,但这是一种反模式并且违背了它们的目的。

    解决方案二 :更通用,但需要一个库和有关生成器如何工作的知识。此外,不同的库会有不同的行为,所以你应该意识到这一点。

    关于javascript - 在 Promises 中实现错误区分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43159752/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com