24
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

RxJS の Operators (6) - Observable のエラーハンドリング

この記事は bouzuya's RxJS Advent Calendar 2015 の 8 日目かつ RxJS Advent Calendar 2015 の 8 日目です。

はじめに

今日は ReactiveX の Operators の Error Handling について RxJS の API ドキュメントやソースコードを見ていきます。

また RxJS 4.0.7 を対象にしています。

Observable のエラーハンドリング

subscribe (Observer) の onError

各種 Operator よりまず subscribe (Observer) の onError です。Observable.throw でエラーを流してみましょう。

import { Observable } from 'rx';

Observable
  .throw(new Error('ERROR!!'))
  .subscribe(
    value => console.log(`onNext: ${value}`),
    error => console.log(`onError: ${error}`),
    () => console.log('onCompleted')
  );
// onError: Error: ERROR!!

onError に入ると onCompleted は流れないようです。

では、途中で throw したらどうでしょう。

import { Observable } from 'rx';

Observable
  .from([1, 2, 3])
  .map(x => {
    if (x < 2) return x;
    throw new Error('x >= 2');
  })
  .subscribe(
    value => console.log(`onNext: ${value}`),
    error => console.log(`onError: ${error}`),
    () => console.log('onCompleted')
  );
// onNext: 1
// onError: Error: x >= 2

当然 try catch されて onError に流れてきます。

Observable.catch / Observable.prototype.catch

onError の際に代わりに流す Observable または Observable を返す関数を指定できます。

import { Observable } from 'rx';

Observable
  .from([1, 2, 3])
  .map(x => {
    if (x < 2) return x;
    throw new Error('x >= 2');
  })
  .catch(error => {
    console.log(`catch: ${error}`);
    return Observable.just(666);
  })
  .subscribe(
    value => console.log(`onNext: ${value}`),
    error => console.log(`onError: ${error}`),
    () => console.log('onCompleted')
  );
// onNext: 1
// catch: Error: x >= 2
// onNext: 666
// onCompleted

Observable.prototype.catch(handler)Error を受けて続く observable を返します。

エラーをさらに流す場合は throw するか Observable.throw です。おそらく Observable.throw が行儀が良いのでしょう。

import { Observable } from 'rx';

Observable
  .from([1, 2, 3])
  .map(x => {
    if (x < 2) return x;
    throw new Error('x >= 2');
  })
  .catch(error => {
    console.log(`catch: ${error}`);
    return Observable.throw(error);
  })
  .subscribe(
    value => console.log(`onNext: ${value}`),
    error => console.log(`onError: ${error}`),
    () => console.log('onCompleted')
  );
// onNext: 1
// catch: Error: x >= 2
// onError: Error: x >= 2

Observable.prototype.catch(observable)onError 時に代わりに流すものを指定するようですね。

import { Observable } from 'rx';

Observable
  .from([1, 2, 3])
  .map(x => {
    if (x < 2) return x;
    throw new Error('x >= 2');
  })
  .catch(Observable.just(666))
  .subscribe(
    value => console.log(`onNext: ${value}`),
    error => console.log(`onError: ${error}`),
    () => console.log('onCompleted')
  );
// onNext: 1
// onNext: 666
// onCompleted

どちらの場合でも、元の Observable のエラーを無視して流し続けるのはちょっと難しそうですね。

Observable.prototype.retry

onError の際に指定した回数だけリトライします。

import { Observable } from 'rx';

const maxRetryCount = 4;
let retryCount = 0;
Observable
  .from([1, 2, 3])
  .map(x => {
    console.log(`retryCount: ${retryCount}`);
    if (x >= 2 && retryCount < 2) {
      retryCount += 1;
      throw new Error('retryCount < 2');
    }
    return x;
  })
  .retry(maxRetryCount)
  .subscribe(
    value => console.log(`onNext: ${value}`),
    error => console.log(`onError: ${error}`),
    () => console.log('onCompleted')
  );
// retryCount: 0
// onNext: 1
// retryCount: 0
// retryCount: 1
// onNext: 1
// retryCount: 1
// retryCount: 2
// onNext: 1
// retryCount: 2
// onNext: 2
// retryCount: 2
// onNext: 3
// onCompleted

途中の onNext が流れる点、onError 以外であれば無視される点が注意点ですかね……。

おわりに

今日は Observable のエラーハンドリングを見ました。

これで本当にエラーハンドリングに十分なのか不安です。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
24
Help us understand the problem. What are the problem?