简体   繁体   English

如何捕获错误并继续执行 RxJs 中的序列?

[英]How to catch error and continue executing a sequence in RxJs?

I have a list of items to parse, but the parsing of one of them can fail.我有一个要解析的项目列表,但其中一个项目的解析可能会失败。

What is the "Rx-Way" to catch error but continue executing the sequence捕获错误但继续执行序列的“Rx-Way”是什么

Code Sample:代码示例:

 var observable = Rx.Observable.from([0,1,2,3,4,5]).map( function(value){ if(value == 3){ throw new Error("Value cannot be 3"); } return value; }); observable.subscribe( function(value){ console.log("onNext " + value); }, function(error){ console.log("Error: " + error.message); }, function(){ console.log("Completed;"); });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>

What I want to do in a non-Rx-Way:我想以非 Rx 方式做的事情:

 var items = [0,1,2,3,4,5]; for (var item in items){ try{ if(item == 3){ throw new Error("Value cannot be 3"); } console.log(item); }catch(error){ console.log("Error: " + error.message); } }

I would suggest that you use flatMap (now mergeMap in rxjs version 5) instead, which will let you collapse errors if you don't care about them.我建议你改用flatMap (现在mergeMap版本 5 中的 mergeMap),如果你不关心它们,它会让你折叠错误。 Effectively, you will create an inner Observable that can be swallowed if an error occurs.实际上,您将创建一个内部 Observable,如果发生错误,它可以被吞下。 The advantage of this approach is that you can chain together operators and if an error occurs anywhere in the pipeline it will automatically get forwarded to the catch block.这种方法的优点是你可以将操作符链接在一起,如果管道中的任何地方发生错误,它都会自动转发到 catch 块。

 const {from, iif, throwError, of, EMPTY} = rxjs; const {map, flatMap, catchError} = rxjs.operators; // A helper method to let us create arbitrary operators const {pipe} = rxjs; // Create an operator that will catch and squash errors // This returns a function of the shape of Observable<T> => Observable<R> const mapAndContinueOnError = pipe( //This will get skipped if upstream throws an error map(v => v * 2), catchError(err => { console.log("Caught Error, continuing") //Return an empty Observable which gets collapsed in the output return EMPTY; }) ) const observable = from([0, 1, 2, 3, 4, 5]).pipe( flatMap((value) => iif(() => value != 3, of(value), throwError(new Error("Value cannot be 3")) ).pipe(mapAndContinueOnError) ) ); observable.subscribe( (value) => console.log("onNext " + value), (error) => console.log("Error: " + error.message), () => console.log("Completed!") );
 <script src="https://unpkg.com/rxjs@7.0.0/dist/bundles/rxjs.umd.min.js"></script>

You need to switch to a new disposable stream, and if an error occurs within it will be disposed safely, and keep the original stream alive:您需要切换到一个新的一次性流,如果其中发生错误将被安全处理,并保持原始流处于活动状态:

 Rx.Observable.from([0,1,2,3,4,5]) .switchMap(value => { // This is the disposable stream! // Errors can safely occur in here without killing the original stream return Rx.Observable.of(value) .map(value => { if (value === 3) { throw new Error('Value cannot be 3'); } return value; }) .catch(error => { // You can do some fancy stuff here with errors if you like // Below we are just returning the error object to the outer stream return Rx.Observable.of(error); }); }) .map(value => { if (value instanceof Error) { // Maybe do some error handling here return `Error: ${value.message}`; } return value; }) .subscribe( (x => console.log('Success', x)), (x => console.log('Error', x)), (() => console.log('Complete')) );
 <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.1/Rx.min.js"></script>

More info on this technique in this blog post: The Quest for Meatballs: Continue RxJS Streams When Errors Occur这篇博文中有关此技术的更多信息: The Quest for Meatballs: Continue RxJS Streams When Errors Occurs

To keep your endlessObservable$ from dying you can put your failingObservable$ in a higher-order mapping operator (eg switchMap , concatMap , exhaustMap ...) and swallow the error there by terminating the inner stream with an empty() observable returning no values.为了防止你的endlessObservable$死亡,你可以将你的failingObservable$放在一个高阶映射操作符(例如switchMapconcatMapexhaustMap ...)中,并通过用一个empty() observable终止内部流而不返回任何值来吞下错误.

Using RxJS 6:使用 RxJS 6:

endlessObservable$
    .pipe(
        switchMap(() => failingObservable$
            .pipe(
                catchError((err) => {
                    console.error(err);
                    return EMPTY;
                })
            )
        )
    );

In a case where you don't want or can't access the inner observable that causes the error, you can do something like this:如果您不想或无法访问导致错误的内部可观察对象,您可以执行以下操作:

Using RxJS 7:使用 RxJS 7:

const numbers$ = new Subject();

numbers$
  .pipe(
    tap(value => {
      if (value === 3) {
        throw new Error('Value cannot be 3');
      }
    }),
    tap(value => {
      console.log('Value:', value);
    }),
    catchError((err, caught) => {
      console.log('Error:', err.message);
      return caught;
    })
  )
  .subscribe();

for (let n = 1; n <= 10; n++) {
  numbers$.next(n);
}

What is interesting here is the "caught" argument in the catchError operator that can be returned.这里有趣的是可以返回的 catchError 运算符中的“捕获”参数。 https://rxjs.dev/api/operators/catchError https://rxjs.dev/api/operators/catchError

It only works when the source observable is Hot.它仅在源 observable 为 Hot 时有效。

In my case, I use redux-observable and I wanted a way to handle my errors in a single place.就我而言,我使用 redux-observable 并且我想要一种方法来在一个地方处理我的错误。

I came up with this:我想出了这个:

const allEpics = combineEpics(
    // all my epics
);

export const rootEpic = (action$, state$, dependencies) => {
  return allEpics(action$, state$, dependencies).pipe(
    catchError((err, caught) => {
        if (err instanceof MyError) {
        return concat(of(displayAlert(err.message)), caught);
      }
      throw err;
    })
  );
};

If any of my epic throw a "MyError" exception, It will be caught and another action will be dispatched.如果我的任何史诗抛出“MyError”异常,它将被捕获并调度另一个动作。

You can actually use try/catch inside your map function to handle the error.您实际上可以在map函数中使用try/catch来处理错误。 Here is the code snippet这是代码片段

 var source = Rx.Observable.from([0, 1, 2, 3, 4, 5]) .map( function(value) { try { if (value === 3) { throw new Error("Value cannot be 3"); } return value; } catch (error) { console.log('I caught an error'); return undefined; } }) .filter(function(x) { return x !== undefined; }); source.subscribe( function(value) { console.log("onNext " + value); }, function(error) { console.log("Error: " + error.message); }, function() { console.log("Completed!"); });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM