首页 文章

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

提问于
浏览
0

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

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

  • 检查数据库以确保电子邮件地址尚未注册 .

  • 检查数据库以确保尚未使用用户名 .

  • 哈希密码 .

  • 在数据库中存储新用户凭据 .

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

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

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

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

例如

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
        }
    });
}

使用此样式然后会产生如下所示的promise链:

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函数/处理checkEmail()结果延迟到链中下一个函数的结果中解决承诺?

例如

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 */});

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

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

任何帮助/建议都非常感谢 .

谢谢

3 回答

  • 1

    这取决于 . 在某些情况下,通过解析Promise传递错误很有用 .

    考虑Fetch API,无论响应状态如何,它都会解析 . 在这种情况下,这是有用的,因为即使响应返回状态404,我们可能希望稍后在链中获取备份文件 . 如果Promise被拒绝,这是不可能的,除非我们将它包装在另一个Promise中,除非我们在Promise上添加一个catch以处理404,然后再进入链中 .

    在这种特定情况下,拒绝而不是推迟更有效 . 如果链中的第二个承诺取决于第一个承诺的成功,拒绝是跳过剩余链的最有效方式 . 明确地检查下一个承诺是可以避免的,因为电子邮件必须对我们有效,甚至可以考虑执行链的其余部分 .

    它基本归结为:如果错误是链中的下一个函数感兴趣解决它,否则拒绝它 .

  • 0

    这取决于 . 您可以使用 assertNameAvailable 方法对其进行建模,该方法在已经采用名称时抛出(或拒绝承诺),或者使用返回(或履行承诺)布尔值的 isNameAvailable 方法 . 随后,您将使用 try / catch 块(或promise方法)或 if / else 语句处理结果 . 两种方法都是可行的 .

  • 1

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

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

    虽然我认为 fetch() 的设计决策对于通用设计具有逻辑意义,但是有很多很多情况下没有尝试获取特定页面,在这种情况下成功只是在你获得页面时 - 其他一切只是一种不同类型的失败 . 所以,理想的设计确实在眼前呼叫者,召集者 . 通常没有完美的解决方案 .

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

    • 首先,请像您的API调用者一样思考 . 我的API用户最想成为什么拒绝,什么应该是解决的 Value ?

    • 想象一下在链中间使用API的一系列调用 . 考虑一下调用者最希望链条继续使用并使其成为决心的条件 . 如果有's no way the chain can continue without intervention, then it'可能是拒绝,并且调用者可以让链中止,或者他们可以用 .catch() 处理程序干预以决定该链是否应该继续 .

    • 如果API可以通过多种方式完成,那么只需考虑使用描述其完成方式的对象进行解析,并让调用者决定他们希望链如何在 .then() 处理程序中继续 .

    • 如果您的API是二进制的,是否收到一个值,那么只有在没有值时拒绝而不是返回 null 或类似的东西 . 期望值的链条应该可能拒绝 . 而且,如果他们想要一个更不寻常的行为,调用者总是可以使用 .catch() 进行自定义 .

    • 如果API完全失败(例如 fetch() 甚至无法联系服务器),那么在所有情况下这显然都是拒绝 .

    在您的具体情况下,您声明:

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

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

    以电子邮件检查(返回承诺)为例 . 如果在查询数据库时发生错误,则显然应该拒绝承诺 . 如何处理实际的电子邮件检查?例如,如果电子邮件地址已经注册,并且在checkEmail函数中指定了错误对象,是否应该拒绝承诺?

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

相关问题