javascript - 处理来自多个 API 的错误时如何避免嵌套 promise
问题描述
我正在开发一个需要向两个 api 发出请求的应用程序。我使用 cognito 来处理身份验证,然后使用 lambda 与数据库通信。但是,我不认为我的问题特定于这些实现中的任何一个。任何两个 api 都可能出现这种情况。
我正在尝试编写注册新用户的过程。我需要在 cognito 中创建一个新用户,以便新用户能够登录,然后我需要在数据库中创建一个相应的用户,该用户将为该用户存储非身份验证相关数据。如果其中一个 api 请求遇到错误,那么我需要删除我在另一个 api 中创建的项目。
我目前的实现基本上是这样的:
const signUpNewUser = (userInfo) => {
API.post("user", "/user", userInfo)
.then((response) => {
return COGNITO.post("user", "/user", response.newUserID);
})
.then((res) => {
//BOTH REQUESTS OCCURED WITH NO ERRORS
})
.catch((error) => {
if (error.origin === "COGNITO_ERROR") {
//IF DB CHANGES CONFIRMED BUT COGNITO FAILED, DELETE CREATED GUEST IN DB
return API.delete("guest", "/guest", userInfo);
} else if (error.origin === "DATABASE_ERROR") {
//IF DB CHANGES FAILED THEN COGNITO HAS NOT RUN YET, SO DON'T NEED TO DELETE IN THIS CASE
}
});
};
这遵循我在互联网上看到的模式。但是,我无法区分认知错误和数据库错误。在上面的代码中,我按 error.origin 对它们进行了排序,但它们实际上并没有可靠地指示其来源的属性。在使用您无法控制的多个 api 时,这个问题一定很常见,但我很难找到一个好的解决方案。
感觉我需要在这种情况下嵌套承诺。我可以在 API.Post 和 COGNITO.post 之后嵌套一个 catch,并使用该 catch 抛出一个具有 origin 属性的新错误。然后它会冒泡并被处理所有错误的最终捕获捕获。像这样:
const signUpNewUser2 = (userInfo) => {
API.post("user", "/user", userInfo)
.catch((err) => {
let parsedError = err;
parsedError.origin = "DATABASE_ERROR";
throw parsedError;
})
.then((response) => {
let newGuestID = response.id;
return COGNITO.post("user", "/user", newGuestID)
.then((res) => {
return res;
})
.catch((err) => {
let parsedError = err;
parsedError.origin = "COGNITO_ERROR";
throw parsedError;
});
})
.then((res) => {
//BOTH REQUESTS OCCURED WITH NO ERRORS
})
.catch((error) => {
if (error.origin === "COGNITO_ERROR") {
//IF DB CHANGES CONFIRMED BUT COGNITO FAILED, DELETE CREATED GUEST IN DB
return API.delete("guest", "/guest", guestInfo);
} else if (error.origin === "DATABASE_ERROR") {
//IF DB CHANGES FAILED THEN COGNITO HAS NOT RUN YET, SO DON'T NEED TO DELETE IN THIS CASE
}
});
};
但是我读过的所有内容都说你应该避免嵌套承诺。
或者,我可以将 API.post 和 COGNITO.post 放在带有内部 .then .catch 语句的单独函数中,然后让这些函数返回一个承诺或抛出一个带有附加属性的错误以指示来源。但是我看到人们说这只是隐藏了问题并使代码更难遵循。
我看到的标准模式是,您在 .then 链的末尾有一个捕获,它知道如何处理多种错误。但是,如果您不控制正在使用的 API,您如何自信地对这些错误进行排序?我缺少关于 js 中错误性质的一些基本信息吗?
解决方案
因为您想以串行方式进行 API 调用,所以这应该很容易管理。您需要做的就是COGNITO.post
在.then
第一次 API 调用之后执行 - 无需.catch
在其间插入另一个。
const signUpNewUser2 = (userInfo) => {
API.post("user", "/user", userInfo)
.then((response) => {
let newGuestID = response.id;
return COGNITO.post("user", "/user", newGuestID)
.then(handleBothSuccess)
.catch((err) => {
// COGNITO failed
return API.delete("guest", "/guest", guestInfo);
});
})
.then((res) => {
//BOTH REQUESTS OCCURED WITH NO ERRORS
})
.catch((error) => {
// Some error other than COGNITO failing occurred
});
};
当您需要实现的控制流需要它时,嵌套 Promise 没有任何问题 - 或者首先在单独的独立变量中声明.then
or函数,这样可以避免视觉嵌套。.catch
或者,考虑async
/ await
,这可能更容易理解。
const signUpNewUser2 = async (userInfo) => {
let newGuestId;
try {
newGuestId = await API.post("user", "/user", userInfo);
} catch (e) {
// API failed, do something here if you want...
return;
}
let cognitoResponse;
try {
cognitoResponse = await COGNITO.post("user", "/user", newGuestID);
} catch (e) {
// COGNITO failed
// If deleting throws, the error will percolate to the caller
return API.delete("guest", "/guest", guestInfo);
}
//BOTH REQUESTS OCCURED WITH NO ERRORS
};
推荐阅读
- python - 构建 tkinter 应用程序时,cx_freeze 不包括 macOS 上的 tcl8.6 和 tk8.6 文件夹
- android - 颤振数据集。如何转换地图
用一个坐标列表加倍,并将其插入地图? - python-3.6 - 我已经安装了 GDAL 库,但是在导入和使用它时遇到了麻烦。我应该怎么办?
- python - 我怎么会单独得到这个文本?
- spring-boot - 使用 apache qpid 使用 amqpwss 连接到 azure 服务总线端口 443
- cython -
在赛通 - excel - 范围类的Excel vba 1004方法失败:自动填充到可变长度列
- stm32 - 如何在没有 vsnprintf 的情况下编写 UART 函数
- angular - Angular Jest 异步测试似乎将结果从一个测试溢出到另一个
- google-cloud-storage - GCP 存储桶转移