angular - 如何等待一个可选的 Observable
问题描述
在我的 Angular 应用程序中,我进行了可选的 HTTP 调用来检查用户是否存在。根据这次调用的结果,我要么想打另一个电话,要么停止处理。如果不诉诸.toPromise()
,我如何等待在一个if
块中进行的呼叫?
onSomeEvent() {
const contact: IContact = {
...this.userForm.value
};
let cancelProcessing = false;
if (this.isNewUser()) {
// *** This is optional
this.userService.duplicateUserSearch(contact.email, contact.userName)
.subscribe(result => {
if (result.result !== '') {
cancelProcessing = true;
}
});
}
// *** This statement is hit before the observable completes
if (cancelProcessing) {
return;
}
// *** which means that this runs when it shouldn't
this.userService.updateContact(contact)
.subscribe(res => {
this.activeModal.close(res);
});
}
解决方案
正如您在问题中展示的那样,使用 Observables 的 If/Then 逻辑可能很棘手。这里有一个很好的讨论。
你已经有一些在特定条件下可以工作的答案,我会冲浪离开,把你交给范或杰弗里有能力的人,但我对你对一个答案的评论很感兴趣:“如果我添加更多条件?”。这引起了我的兴趣,因此我想找到一种基于可观察/基于功能的模式,如果您稍后决定添加其他条件,则易于阅读且易于扩展。一个好问题让我很难思考,而你确实做到了。:)
我尝试将上面链接的文章中建议的模式应用于您的问题,下面的代码就是我想出的。正如我在上面对您原来的问题的评论中提到的,我不是 100% 确定您打算在用户不是新用户时更新联系人,但现在我假设您这样做了。免责声明:我没有严格测试这一点,例如创建 Stackblitz 和创建各种测试用例,尽管这是我通常想要回答这样的问题的方式,所以对于以下代码中的任何错误或意外后果,我深表歉意。唉,周末来了。;)
在文章的该模式中,建议为通过 if/then 逻辑的所有可能路径创建分支。我想出了两个会导致调用 updateContact() 的主要分支。请注意,使用此模式很容易tap()
在任何分支中添加 a 以在需要时进行额外的工作。如果我错过了你想添加的分支,那也应该很容易添加。
这种模式的核心是最后的 merge()。这将创建一个合并两个传入的可观察对象。如果其中任何一个完成,则订阅将执行并运行updateContact()
。在这种情况下,它们永远不会都完成,因为过滤器保证只有一个是活动的,但是如果你在其他地方应用这个模式,如果你只关心第一个“获胜”的异步isNewUser()
模式,你可能想要添加一个。take(1)
我还显示订阅和取消订阅,因为我认为这是最佳做法。
onSomeEventSub : Subscription; // component scope variable to add to later unsubscribe with
onSomeEvent() {
const contact: IContact = {
...this.userForm.value
};
// define duplicateUserSearch$ now for easier readability below
const duplicateUserSearch$: Observable<boolean> =
this.userService.duplicateUserSearch(contact.email, contact.userName).pipe(
map(result => (result.result === '')));
// First, create a shareable source for eventual merge of all branches.
// of(0) is completely arbitrary. It would be more interesting to tie
// this to the original event, but that wasn't part of the question. :)
const source$ = of(0).pipe(
share() // same observable will have multiple observers
);
// branch 1 is for when the user is a new user and duplicate logic asserts
const isNewUserAndIsDupe$ = source$.pipe(
filter(() => isNewUser()),
mergeMap(() => duplicateUserSearch$),
filter(res => res)
);
// branch 2 is when the user is NOT a new user
const isNotNewUser$ = source$.pipe(
filter(() => !isNewUser())
);
// and finally the merge that pulls this all together and subscribes
this.onSomeEventSub = merge(isNewUserAndIsDupe$, isNotNewUser$).pipe(
mergeMap(() => this.userService.updateContact(contact))
).subscribe(res => this.activeModal.close(res));
}
ngOnDestroy() {
if (this.onSomeEventSub) this.onSomeEventSub.unsubscribe();
}
推荐阅读
- spinnaker - Spinnaker 配置作为代码从 gitlab 加载
- html - 使导航栏超过最大宽度 [CSS]
- angular-cli - Angular CLI 未安装
- bash - grep:使用 while 循环时重复计数无效
- java - 将具有“.dotx”扩展名(模板)的文件转换为“docx”(Word 文件)在 windows 10 和 windows server 2016 中不起作用
- php - 无法通过 PHP 文件在 GET HTTP 方法中输出键的值
- docker - 如何确保 Kubernetes 中容器副本的就绪性?
- reactjs - 反应路由器不显示功能组件
- c# - 从字符串到硬编码(C# HashTables)
- vb.net - 如何使用 rasphone.exe 获得 vpn 连接而不总是提示输入密码