我有以下方法,应该像这样调用:
registerDomain
并返回operationId
10
秒之后,getOperationDetail
应该调用operationId
getOperationDetail
应该每隔10
秒调用一次,直到successful
返回。getOperationDetail
完成后,应调用createRecordSets
。getChangeStatus
,直到它返回INSYNC
下面的代码调用registerDomain和getOperationDetail,但是在getOperationDetail完成后,它不会移动到createRecordSets。
registerDomain(domain) {
return this._adminService.registerDomain(domain)
.concatMap(operation => this.getOperationDetail(operation.OperationId))
.concatMap(() => this._adminService.createRecordSets(domain));
}
getOperationDetail(operationId) {
return Observable.interval(10000)
.mergeMap(() => this._adminService.getOperationDetail(operationId))
.takeWhile((info) => info.Status.Value !== 'SUCCESSFUL');
}
createRecordSets(caseWebsiteUrl) {
return this._adminService.createRecordSets(caseWebsiteUrl.Url)
.concatMap(registerId => this.getChangeStatus(registerId));
}
getChangeStatus(registerId) {
return Observable.interval(5000)
.mergeMap(() => this._adminService.getChange(registerId))
.takeWhile((info) => info.ChangeInfo.Status.Value !== 'INSYNC');
}
我更新了getOperationDetail
以使用first
运算符:
getOperationDetail(operationId) {
return Observable.interval(3000)
.mergeMap(() => this._adminService.getOperationDetail(operationId))
.first((info) => info.Status.Value === 'SUCCESSFUL')
}
现在它实际上会调用createRecordSets
,但在createRecordSets
之后,它会继续调用getOperationDetail
大约13次并最终调用getChangeStatus
。我正在看它的方式,我认为它会:
getOperationDetail
,直至收到SUCCESS
。createRecordSets
。getChangeStatus
,直至收到INSYNC
为什么要追加电话?
我将registerDomain更改为:
registerDomain(domain) {
return this._adminService.registerDomain(domain)
.concatMap(operation => this.getOperationDetail(operation.OperationId))
.concatMap((op) => this.createRecordSets(op));
在.concatMap((op) => this.createRecordSets(op))
之后我this.getOperationDetail
链接之前。一旦我把它移到外面,它开始按预期工作。我不确定为什么。有人可以解释一下吗?
答案 0 :(得分:3)
当takeWhile
满足满足指定条件的值时,它将完成observable而不传播该值。这意味着下一个链式运算符将不会接收该值,也不会调用其回调。
假设在您的示例中,对this._adminService.getOperationDetail(...)
的前两次调用导致非成功状态,第三次调用成功。这意味着getOperationDetail()
返回的可观察量只会产生两个info
值,每个值都具有非成功状态。而且可能也很重要的是,下一个链式concatMap
运算符将针对每个非成功值调用其回调,这意味着createRecordSets()
将被调用两次。我想你可能想避免这种情况。
我建议改为使用first
运算符:
getOperationDetail(operationId) {
return Observable.interval(10000)
.concatMap(() => this._adminService.getOperationDetail(operationId))
.first(info => info.Status.Value !== 'SUCCESSFUL');
}
这样getOperationDetail()
只会产生一个成功的" this._adminService.getOperationDetail(operationId)
成功后的价值。 first
运算符发出与指定条件匹配的源observable的第一个值,然后完成。
<强>更新强>
您遇到的意外行为(getOperationDetail()
完成后first()
继续被调用)似乎是rxjs
中的bug。如this issue中所述,
每个take-ish运算符(一个比其源Observable更早完成的运算符),在与延长订阅的运算符(此处为switchMap)结合使用时,将继续订阅源Observable。
first
和takeWhile
都是这种“运营商”和“运营商”的例子。&#34;延长&#34;例如,订阅是switchMap
,concatMap
和mergeMap
。在下面的示例中,数字将保持记录状态,而concatMap
的内部可观察值正在发出值:
var takeish$ = Rx.Observable.interval(200)
// will keep logging until inner observable of `concatMap` is completed
.do(x => console.log(x))
.takeWhile(x => x < 2);
var source = takeish$
.concatMap(x => Rx.Observable.interval(200).take(10))
.subscribe();
看起来这可以通过将包含这样一个take-ish运算符的observable转换为更高阶的可观察量来解决 - 就像你已经完成的那样:
var takeish$ = Rx.Observable.interval(200)
// will log only 0, 1, 2
.do(x => console.log(x))
.takeWhile(x => x < 2);
var source = Rx.Observable.of(null)
.switchMap(() => takeish$)
.concatMap(x => Rx.Observable.interval(200).take(1))
.subscribe();
更新2:
从rxjs版本5.4.2开始,上述错误似乎仍然存在。例如,当first
满足指定条件时,它会影响first
运算符的源可观察量是否取消订阅。当first
运算符紧跟concatMap
后,其源可观察量将不会被取消订阅,并将继续发出值,直到concatMap
的内部可观察量完成。在您的情况下,这意味着this._adminService.getOperationDetail()
会一直被调用,直到createRecordSets()
返回的observable完成为止。
这里简化了您的示例来说明行为:
function registerDomain() {
return Rx.Observable.of("operation")
.concatMap(() => getOperationDetail()
.concatMap(() => Rx.Observable.interval(200).take(5)));
}
function getOperationDetail() {
return Rx.Observable.interval(100)
// console.log() instead of the actual service call
.do(x => console.log(x))
.first(x => x === 2);
}
registerDomain().subscribe();
&#13;
<script src="https://unpkg.com/@reactivex/rxjs@5.0.3/dist/global/Rx.js"></script>
&#13;
如果我们展开第一个concatMap
运算符的内部可观察量,我们将得到以下可观察值:
Rx.Observable.interval(100)
.do(x => console.log(x))
.first(x => x === 2)
.concatMap(() => Rx.Observable.interval(200).take(5));
请注意,first
后面紧跟concatMap
,这会阻止first
运算符(即interval(100).do(x => console.log(x)
)的源可观察性被取消订阅。值将被记录(或者在您的情况下,服务调用将继续发送),直到concatMap
的内部可观察量(即interval(200).take(5)
)完成。
如果我们修改上面的示例并将第二个concatMap
移出第一个concatMap
的内部可观察对象,则first
将不再与其链接,并将取消订阅一旦条件满足,源可观察,意味着间隔将停止发射值,不再记录更多数字(或不再发送服务请求):
function registerDomain() {
return Rx.Observable.of("operation")
.concatMap(() => getOperationDetail())
.concatMap(() => Rx.Observable.interval(200).take(5));
}
function getOperationDetail() {
return Rx.Observable.interval(100)
// console.log() instead of the actual service call
.do(x => console.log(x))
.first(x => x === 2);
}
registerDomain().subscribe();
&#13;
<script src="https://unpkg.com/@reactivex/rxjs@5.0.3/dist/global/Rx.js"></script>
&#13;
在这种情况下,内在的可观察性可以简单地扩展到:
Rx.Observable.interval(100)
.do(x => console.log(x))
.first(x => x === 2)
请注意first
后面不再有concatMap
。
值得一提的是,在两种情况下registerDomain()
返回的可观察值都会产生完全相同的值,如果我们将记录从do()
运算符移动到subscribe()
,则会写入相同的值在两种情况下都到控制台:
registerDomain.subscribe(x => console.log(x));