Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/408.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript承诺模式-区分错误_Javascript_Node.js_Promise - Fatal编程技术网

Javascript承诺模式-区分错误

Javascript承诺模式-区分错误,javascript,node.js,promise,Javascript,Node.js,Promise,考虑这段代码: getUser(userId) .catch(function(error){ crashreporter.reportError('User DB failed', error); // show user a generic error }) .then(function(user) { return chargeCreditCard(user); }) .catch(functi

考虑这段代码:

    getUser(userId)
    .catch(function(error){
        crashreporter.reportError('User DB failed', error);
        // show user a generic error
    })
    .then(function(user) {
        return chargeCreditCard(user);
    })
    .catch(function(error){
        crashreporter.reportError('Credit card failed', error);
        // show user an error saying that their credit card got rejected
    })
显然,问题在于,如果用户DB失败,那么就会执行THEN(USER)块。另一个选项是将第一个捕捉块移动到链的末端。然而,这会引起另一个问题!我们无法区分错误是来自用户数据库还是信用卡

我认为解决问题的以下模式是否被视为承诺反模式?有更好的方法吗?我看到的问题是,你最终可能会陷入半地狱

    getUser(userId)
    .then(function(user) {
        return chargeCreditCard(user)
              .catch(function(error){
                  crashreporter.reportError('Credit card failed', error);
                  // show user an error saying that their credit card got rejected
              });
    })
    .catch(function(error){
        crashreporter.reportError('User DB failed', error);
        // show user a generic error
    })

编辑:我想我不是很清楚。如果有更多的块,比如下面的。问题是,一旦您遇到一个错误,您就根本不希望链继续

getUser(userId)
    .then(function(user) {
        return chargeCreditCard(user);
    }, function(error){
        crashreporter.reportError('User DB failed', error);
        // show user a error 1
    })
    .then(function(chargeId) {
        return saveChargeId(chargeId);
    }, function(error){
        crashreporter.reportError('ChargeId DB failed', error);
        // show user a error 2
    })
    .then(function(chargeHistoryId) {
        return associateChargeToUsers(chargeHistoryId);
    }, function(error){
        crashreporter.reportError('chargeHistoryId DB failed', error);
        // show user a error 3
    })
    .catch(function(error){
        crashreporter.reportError('Credit card failed', error);
        // show user a error 4
    })

下面是如何在不嵌套回调的情况下完成“取消”链。这就是为什么承诺是解决“回调地狱”的方法


从本质上讲,如果第一个承诺失败,您将在失败回调链中传递其错误,并在它到达末尾时记录它。没有创建其他承诺,因此您在第一次失败时有效地“取消”了操作。

您的链应该只有一个
catch
,但您可以为每个函数中抛出的错误添加更多上下文。例如,当
chargeCreditCard
中出现错误时,您可以定位到与要报告的内容相对应的错误a
message
属性。然后在
catch
错误处理程序中,您可以将
消息
属性传递给报告程序:

getUser(userId)
  .then(chargeCreditCard)
  .catch(reportError);

function reportError(error) {
  crashreporter.reportError(error.message, error);
}
我认为解决问题的以下模式是否被视为承诺反模式

不,很好

有更好的方法吗

是的,看一看。如果要严格区分成功案例(continue)和错误案例(报告此特定问题),最好将两个回调传递给
,然后
。这样,外部处理程序就不能由成功案例代码中的失败触发,而只能由它所安装的promise中的失败触发。就你而言:

getUser(userId)
.then(function(user) {
    return chargeCreditCard(user)
    .then(function(chargeId) {
        return saveChargeId(chargeId)
        .then(function(chargeHistoryId) {
            return associateChargeToUsers(chargeHistoryId);
            .then(function(result) {
                return finalFormatting(result);
            }, function(error){
                crashreporter.reportError('chargeHistoryId DB failed', error);
                return 3;
            });
        }, function(error){
            crashreporter.reportError('ChargeId DB failed', error);
            return 2;
        });
    }, function(error){
        crashreporter.reportError('Credit card failed', error);
        return 4;
    });
}, function(error){
    crashreporter.reportError('User DB failed', error);
    return 1;
})
.then(showToUser);
尽管您可能希望使用通用错误处理程序:

getUser(userId)
.catch(function(error){
    crashreporter.reportError('User DB failed', error);
    throw new Error(1);
})
.then(function(user) {
    return chargeCreditCard(user)
    .catch(function(error){
        crashreporter.reportError('Credit card failed', error);
        throw new Error(4);
    });
})
.then(function(chargeId) {
    return saveChargeId(chargeId);
    .catch(function(error){
        crashreporter.reportError('ChargeId DB failed', error);
        throw new Error(2);
    });
})
.then(function(chargeHistoryId) {
    return associateChargeToUsers(chargeHistoryId);
    .catch(function(error){
        crashreporter.reportError('chargeHistoryId DB failed', error);
        throw new Error(3);
    });
})
.then(function(result) {
    return finalFormatting(result);
}, function(error) {
    return error.message;
})
.then(showToUser);
在这里,每个
then
回调都会返回一个承诺,该承诺会以其自身的适当错误拒绝。理想情况下,每个被调用的函数都已经这样做了,如果它们没有这样做,并且您需要向每个函数附加一个特定的
catch
,那么您可能希望使用一个包装器帮助器函数(可能是
crashreporter
?)的一部分)

我看到的问题是,你最终可能会陷入半地狱

    getUser(userId)
    .then(function(user) {
        return chargeCreditCard(user)
              .catch(function(error){
                  crashreporter.reportError('Credit card failed', error);
                  // show user an error saying that their credit card got rejected
              });
    })
    .catch(function(error){
        crashreporter.reportError('User DB failed', error);
        // show user a generic error
    })
不,这只是一个适当的包装水平。与callbackhell不同,它可以被压平,最多嵌套两个,并且它总是有一个返回值

如果您绝对希望避免嵌套和回调,请使用
async
/
wait
,尽管这实际上更难看:

try {
    var user = await getUser(userId);
} catch(error) {
    crashreporter.reportError('User DB failed', error);
    return showToUser(1);
}
try {
    var chargeId = chargeCreditCard(user);
} catch(error) {
    crashreporter.reportError('Credit card failed', error);
    return showToUser(4);
}
try {
    var chargeHistoryId = saveChargeId(chargeId);
} catch(error) {
    crashreporter.reportError('ChargeId DB failed', error);
    return showToUser(2);
}
try {
    var result = associateChargeToUsers(chargeHistoryId);
} catch(error) {
    crashreporter.reportError('chargeHistoryId DB failed', error);
    return showToUser(3);
}
return showToUser(finalFormatting(result));

是的,我明白。我想我的问题不是很清楚。我更新了我的问题,这就是为什么我认为这种模式容易出错和丑陋:当代码> GETUSER()/代码>失败时,您的代码不起作用,而不是抛出一个<代码>用户DB失败的错误,因为它应该抛出一个<代码> 'CalGeEythyIdB失败'错误。如果你想要一个扁平的链子,你应该像我回答的第二段那样做。我错过了那句话。我只是在头顶上打了下来。现在检查一下。它工作得很好。丑陋是你个人的喜好。承诺是为了解决地狱。你的回答充满了地狱。如果您想要一个漂亮的解决方案,可以使用async/Wait或Generators是的,但是很容易错过,而且会出现大量重复代码,不得不一遍又一遍地重复错误消息。我的回答可能充满了回电,但不是地狱般的。我相信第三个代码片段在各个方面都优于所有其他解决方案。甚至async/await也没有那么漂亮或简洁。显然,可以对其进行重构以避免错误消息的重复。箭头函数也会有所帮助。对我来说,单一深度层次的简单性仍然是可取的。其他一切都只是标签与空间你可能还想看一看,相关:我不同意这是好的。如果说承诺有什么反模式的话,那就是。它们的存在是为了避免这种回调嵌套,也就是“回调地狱”@CharlieMartin,这都是关于控制流的。如果您希望代码中有不同的路径(分支),而“不继续链”正是这一点,那么您需要嵌套控制结构。然后带有两个回调的
是这里的一个控制结构。避免嵌套的唯一方法是使用
async
/
await
的早期返回,但我认为这不是更好的方法。这不是唯一的方法。您抛出一个自定义错误,然后在下一个错误回调中,您说“这是来自上面的自定义错误吗?抛出它并继续向下转发链。如果不是,请在这里处理它”。你不能停止链条,但根据传递给你的信息,你可以做出不同的行为。当您收到自定义错误时,通过不做任何操作来有效地“取消”链。每件事都可以用一根链条来完成。这就是Promise的美妙之处。我不知道这是否有意义,但请检查错误类型,并在下一个错误回调中相应地执行条件逻辑。这种逻辑可能意味着回归新的承诺,回到成功的轨道。这可能意味着,如果你在剩下的过程中这样做,除了重新抛出相同的错误(实际上是“取消”链)之外,什么也不做。或者这可能意味着抛出另一种错误。这可能意味着将一个错误添加到一个错误数组中,并在最后抛出该数组以同时处理所有错误。希望我能在这里放一个代码示例。你认为它很漂亮,我觉得很难看。一位专家说,创造这么多承诺是相当低效的
try {
    var user = await getUser(userId);
} catch(error) {
    crashreporter.reportError('User DB failed', error);
    return showToUser(1);
}
try {
    var chargeId = chargeCreditCard(user);
} catch(error) {
    crashreporter.reportError('Credit card failed', error);
    return showToUser(4);
}
try {
    var chargeHistoryId = saveChargeId(chargeId);
} catch(error) {
    crashreporter.reportError('ChargeId DB failed', error);
    return showToUser(2);
}
try {
    var result = associateChargeToUsers(chargeHistoryId);
} catch(error) {
    crashreporter.reportError('chargeHistoryId DB failed', error);
    return showToUser(3);
}
return showToUser(finalFormatting(result));