一方面,我有一个可能偶尔会抛出错误的流:
this.behaviorSubject.error(error)
然而,稍后,我想继续使用该流:
this.behaviorSubject.next(anotherValue)
另一方面,我有一个订阅者订阅了behaviorSubject.asObservable()
。
在订阅中,我正在处理值和错误:
.subscribe(
( value ) => { /* ok */ },
( error ) => { /* some error */ }
);
我希望效果与简单的onSuccess
和onError
回调相同,每次发生错误时都会调用onError
并且不会阻止将来{{1}来自被叫的电话。我如何使用RXJS做到这一点?
我已经研究了捕获,但它似乎只是防止错误被订阅者调用。
答案 0 :(得分:18)
简答:这是不可能的。
如何使用此功能:RxJS的基本概念是任何error
或complete
- 调用基本上“kill” a流。这个概念迫使你“只是随心所欲地抛出错误”,但要正确处理错误和应用程序中的数据流。例如,BehaviorSubject
通常用于保存数据,但是不还应包括检索/创建该数据的过程,并处理在检索期间可能发生的错误数据。
因此,如果你想按照本书,你应该将你的流程分成两部分:
作为示例,您的数据流可能如下所示(粗略草图):
<强> store.ts 强>
dataStore: BehaviorSubject<IData> = new BehaviorSubject<IData>();
errorMessage: BehaviorSubject<IErrorMsg> = new BehaviorSubject<IErrorMsg>();
数据-retrieval.ts 强>
fetchDataById(id: string) {
httpService.get(`some/rest/endpoint/${id}`)
.subscribe(handleData, handleError);
}
handleData(data: IData) {
errorMessage.next(null);
dataStore.next(data);
}
handleError(error: Error) {
errorMessage.next(error.message);
dataStore.next(null);
}
“但这看起来像很多开销......” - 没错,但是它可以确保应用程序内的数据流清晰易懂,易于测试和保持。此外,还有可以使用的即用型商店概念,例如ngrx或redux。
答案 1 :(得分:4)
Rx基本上建立在observable活动或最终化(onComplete或onError)的概念之上。当Observable最终确定时,它将取消订阅其上游的Observable。没有.catch
可以修复该行为,它只为您提供了将错误映射到其他内容的选项。
Rx.Observable.interval(500)
.mergeMap(i => i % 3 == 2 ? Rx.Observable.throw(new Error('kboom')) : Rx.Observable.of(i))
.catch(err => Rx.Observable.of(err.message))
.subscribe(
val => console.log('val: ' + val),
err => console.log('err: ' + err),
() => console.log('stream completed')
)
请注意,此示例在3次排放后完成,而不是5次
当您调用this.behaviorSubject.error(error)
时,它将在内部完成主题中包含的Observable。如果你想以某种方式发出错误,那么你需要使你的错误成为非错误值:
this.behaviorSubject.next({ value: 'somevalue' });
this.behaviorSubject.next({ error: error });
this.behaviorSubject.next({ value: 'somevalue' });
然后,您可以根据发射值的属性区分您应该采取的行动。
答案 2 :(得分:0)
这可能不适用于您的情况,但在使用Angular 2时遇到了同样的问题,因为我们会在屏幕之间导航,并希望服务重试API,而不是再次调用错误函数。它实际上会导致更大的问题,因为在我们的构造函数中调用了函数,错误函数会尝试更新尚未准备好的UI。
我所做的似乎工作得很好而且非常干净。我在错误处理程序中创建了一个重置主题。
subject.subscribe(
( value ) => { /* ok */ },
( error ) => {
//handle error
//reset subject
this.subject = new Subject<any>();
}
);
这适用于我们的情况,因为每次您导航到屏幕时,新订阅都会从旧屏幕中拆除,然后在新屏幕中设置,因此新主题不会对任何事情造成伤害。
答案 3 :(得分:0)
正如其他人所说,错误结果预计是终端。
我个人认为(在你的情况下)有 3 种类型的结果(技术上是 4)
理想的结果是调用 next() 的成功案例。
致命失败(内存不足错误或任何其他形式的“调用无法继续”错误)应该调用 error()。
第三种形式也是解决问题的关键是非终端错误。这是“结果不成功”的形式。因为您的意思是继续,所以它不是 rxjs 意义上的错误。这只是另一种类型的结果。结果表明发生了其他事情。
(第 4 种形式是“处理完成”:尽我所能,并且没有错误地退出)
现在我不确定细节,但我记得打字稿可以处理联合类型(如果不是,你可能不得不使用“any”的结果类型)。
使用 Unions,您可以将对象声明为(例如) Subject
DoCoolThingFunction():Subject<string|failtype>
{
const response = new Subject<string|failtype>();
deeperPeriodicAsyncOperation.subscribe((result) => {
if (result is something I like) {
response.next(result.toString());
} else if (result is final value) {
response.next(result.toString());
response.complete();
} else if (result is something teminal) {
response.error(result);
} else if (result is non-terminal error) {
response.next(new failtype(result));
}
});
return response;
}
基本上,这是说“此范围内的错误是非终止性的。因此它不是错误,它只是一种不同类型的操作数据”。
当然,由您的接收代码来确定它所传递的结果类型。我不知道是否有任何巧妙的方法可以做到这一点。如果结果处理程序可以有多个不同类型的响应 ((result:string) =>{}, (result:failtype)=>{}) 等,那就太好了,但这并不是该线程的真正主题。>