在使用promises /构建承诺链时,如何设计/集成错误处理?

时间:2017-11-27 00:53:34

标签: javascript node.js promise

我开始越来越多地实施承诺,并且不确定它们应该如何实施。

例如,考虑一个注册函数,它接受电子邮件地址,用户名和密码,并按顺序执行以下异步操作:

  1. 检查数据库以确保电子邮件地址尚未注册。
  2. 检查数据库以确保尚未使用用户名。
  3. 哈希密码。
  4. 将新用户凭据存储在数据库中。
  5. 如果在任何步骤中出现问题,则暂停进度并将JSON对象发送回用户以通知他们该问题。否则,将通知用户他们已成功注册。

    如何使用promises实现此功能/错误处理?

    以电子邮件检查(返回承诺)为例。如果查询数据库时发生错误,则显然应该拒绝承诺。如何处理实际的电子邮件检查?

    例如,如果电子邮件地址已经注册,并且checkEmail函数中指定了错误对象,是否应该拒绝承诺?

    e.g。

    function checkEmail(email) {
        return new Promise((resolve, reject) => {
            // check email against database
            // if database error occurs, reject()
    
            if (email already registered) {
                reject({error: 'email already registered'});
            } else {
                resolve(); // move on to check username
            }
        });
    }
    

    使用此样式会产生如下所示的承诺链:

    checkEmail(email)
        .then(() => checkUsername(username))
        .then(() => hashPassword(password))
        .then(passwordHash => addUser(email, username, passwordHash))
        .then(() => {
            // notify user of successful signup
        })
        .catch(error => {
            // all error objects are passed here
            // can then simply send error object directly to client
    
            /* if different error handling functionality is needed,
            additional property can be included on error object passed to
            resolve(), and used to differentiate between errors */
        });
    

    或者应该在checkEmail函数中解析promise还是将处理checkEmail()结果的函数延迟到链中的下一个函数?

    e.g。

    function checkEmail(email) {
        return new Promise((resolve, reject) => {
            // check email against database
            // if database error occurs, reject()
    
            resolve(result); // process result / potential errors in next step of chain
            });
        }
    

    然后会产生类似于以下内容的内容:

    checkEmail(email)
        .then((result) => {
            if (email already registered) {
                // error handling specified here, instead of in checkEmail()
                return Promise.reject({error: 'email already registered'});
            }
        })
        .then()...
        .then()...
        .then()...
        .catch(error => {/* handle errors */});
    

    这会导致更长/更混乱的承诺链,但似乎更灵活。

    无论如何,我不确定如何说出来,所以希望这些例子说明我想要达到的目的。我刚刚开始实施承诺,而且对此很多都不确定。这有什么问题吗?这些方法中的一种优于另一种吗?有没有更好的方法来实现这些承诺链/处理错误?

    非常感谢任何帮助/建议。

    由于

1 个答案:

答案 0 :(得分:1)

这完全取决于你做出拒绝的内容以及你的回报价值。承诺的方式是用链接设计的,你通常希望拒绝意味着你希望链条中止的东西。如果一个条件可能希望链继续而没有特殊处理,那么它不应该拒绝,因为很明显,拒绝将中止promise链而不在.catch()处理程序中进行特殊处理以保持链的运行。

fetch()示例是一个有趣的案例,我认为,它解释了一个常见问题,即您不仅仅有结果或错误的二进制结果。在fetch()的示例中,您有一个明确的错误,您无法联系服务器,这确实是拒绝。并且,您可以在成功联系服务器并获得结果的情况下获得明确的结果。然后,你有一个中间案例,你成功地联系了服务器并获得了除2xx以外的某种状态(甚至可能是404)。在fetch()的情况下,他们决定拒绝将被定义,因为它们无法到达服务器,所有其他返回值将被视为成功呼叫并将解决。

虽然我认为fetch()的设计决策对于通用设计具有逻辑意义,但是有很多很多情况下实际上并不是你想要的东西。我经常使用一个小的包装器来检查状态仅在状态为2xx时解析。如果您正在尝试获取特定页面,那么在这种情况下成功只有在您获得页面时才会成功 - 其他一切只是一种不同类型的失败。因此,理想的设计确实在呼叫者的眼中。通常没有完美的解决方案。

所以,这是我的一般准则:

  1. 首先,请像您的API调用者一样思考。我的API用户最希望拒绝什么,以及解决的价值应该是什么?
  2. 想象一下在链中间使用API​​的一系列调用。考虑一下调用者最希望链条继续使用并使其成为决心的条件。如果链条无法在没有干预的情况下继续,那么它可能是拒绝,并且调用者可以让链中止或者他们可以使用.catch()处理程序进行干预来决定链是否应该继续。
  3. 如果API可以通过多种方式完成,那么只需考虑使用描述其完成方式的对象进行解析,然后让调用者决定他们希望链在.then()处理程序中如何继续。
  4. 如果您的API是二进制的,是否收到值,那么只有在没有值时拒绝,而不是返回null或类似的东西。期望值的链条应该可能拒绝。而且,如果他们想要一种更不寻常的行为,呼叫者总是可以使用.catch()进行自定义。
  5. 如果API完全失败(例如fetch()甚至无法联系服务器),那么在所有情况下这显然都是拒绝。
  6. 在您的具体情况中,您说明了这一点:

      

    如果在任何步骤中出现问题,则暂停进度并将JSON对象发送回用户以通知他们该问题。否则,将通知用户他们已成功注册。

    这似乎非常简单,只要你想要暂停进度,你就应该拒绝使用描述性错误对象。然后你可以运行链,如果它拒绝,你有一个错误对象反馈给用户。如果连锁店没有拒绝,那你就取得了成功。

      

    以电子邮件检查(返回承诺)为例。如果查询数据库时发生错误,则显然应该拒绝承诺。如何处理实际的电子邮件检查?

         

    例如,如果电子邮件地址已经注册,并且checkEmail函数中指定了错误对象,是否应该拒绝承诺?

    根据我们之前的讨论,如果已经注册的电子邮件是您要中止链并将错误对象反馈给用户,那么您可能想要拒绝它已经注册。如果这是一个良性的操作,他们的电子邮件已经注册,并且操作链可以继续正常,那么你就可以解决它已经存在的问题。我没有完全遵循你的逻辑,但如果用户要求注册一个新帐户,那么已经存在的电子邮件地址可能是一个错误,因此被拒绝,因为这是一个帐户冲突,你不能拥有另一个帐户相同的识别电子邮件。