在我的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);
});
}
答案 0 :(得分:2)
正如您在问题中所展示的那样,带有Observables的If / Then逻辑可能很棘手。关于此here的讨论很好。
您已经有了一些可以在某些条件下使用的答案,我本该冲浪,然后将您留在范或杰弗里的精干手中,但是您对一个答案的评论使我很感兴趣:“如果添加更多条件会怎样?”。那引起了我的兴趣,因此,我想找到一种基于可观察/功能,清晰易读且易于扩展的模式,如果以后再决定添加其他条件的话。一个好的问题使我很难思考,而您当然已经做到了。 :)
我尝试应用上面链接到您的问题的文章中建议的模式,下面的代码正是我想到的。正如我在上面对您的原始问题的评论中提到的那样,当用户不是新用户时,我不确定100%确定您打算更新该联系人,但是现在我假设您愿意。免责声明:我没有严格测试过诸如创建Stackblitz和创建各种测试用例之类的内容,尽管我通常希望以此方式回答这样的问题,因此对于以下代码中的任何错误或意外后果,我深表歉意。 las,周末来了。 ;)
在本文的模式中,建议通过if / then逻辑为所有可能的路径创建分支。我提出了两个主要分支,这将导致调用updateContact()。请注意,使用这种模式很容易在任何分支中添加tap()
以在需要时进行其他工作。如果我错过了您希望添加的分支,那么只需添加即可也很容易。
此模式的核心是结尾处的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();
}
答案 1 :(得分:1)
一种更加实用的方式。
onSomeEvent() {
const contact: IContact = {
...this.userForm.value
};
const updateContact=this.userService.updateContact(contact)
.pipe(tap(res => {
this.activeModal.close(res);
}))
return of(this.isNewUser()).pipe(mergeMap(newUser=>{
if(!newUser)
return updateContact
return this.userService.duplicateUserSearch(contact.email, contact.userName)
.pipe(mergeMap(result=>result.result === ''?updateConcat:empty()))
}))
}
// execute
this.onSomeEvent().subscribe()
答案 2 :(得分:1)
问题在于onSomeEvent
是同步执行的,而cancelProcessing
的分配是异步执行的-稍后。我认为您可以改为执行以下操作(RxJS 5):
onSomeEvent() {
const contact: IContact = {
...this.userForm.value
};
if (this.isNewUser()) {
this.userService.duplicateUserSearch(contact.email, contact.userName)
.filter((result) => result.result === '')
.switchMap(() => this.userService.updateContact(contact))
.subscribe((res) => this.activeModal.close(res));
} else {
this.userService.updateContact(contact)
.subscribe((res) => this.activeModal.close(res));
}
}
If子句
首先,第一个条件filter
对可观察流进行编辑。这将返回一个可观察对象,该对象将仅在duplicateUserSearch
是为空字符串时转发来自result.result
可观察对象的项目。
接下来,传递的值将像您在示例中一样被丢弃,并由updateContact
返回的可观察值代替,只有在某些值通过了以前使用的过滤器后才按需调用。
switchMap
展平输入值,这意味着如果它们是可观察的,它们将被订阅,并且它们的值将通过switchMap返回的observable而不是observable传播。实例本身。
最后,我们可以订阅switchMap返回的流,并直接观察updateContact
返回的值。
其他条款
如果不是新用户,则无条件调用updateContact
,而无需先与duplicateUserSearch
进行检查。
使用RxJS 6.x时,pipe
运算符用于一次性指定运算符的顺序:
onSomeEvent() {
const contact: IContact = {
...this.userForm.value
};
if (this.isNewUser()) {
this.userService.duplicateUserSearch(
contact.email,
contact.userName,
).pipe(
filter((result) => result.result === ''),
switchMap(() => this.userService.updateContact(contact)),
).subscribe((res) => this.activeModal.close(res));
} else {
this.userService.updateContact(contact)
.subscribe((res) => this.activeModal.close(res));
}
}