RxJSでの簡単なエラー処理
複雑な監視可能なパイプラインを作成することはすべて良いことですが、それらのパイプライン内のエラーをどのように効果的に処理しますか? ここでは、 catch 、 finally 、 try 、およびtryWhen演算子を使用していくつかの基本事項について説明します。
オブザーバーのエラーコールバック
最も基本的には、オブザーバーはエラーコールバックを取得して、オブザーバブルストリーム内の未処理のエラーを受信します。 たとえば、ここではobservableが失敗し、エラーメッセージがコンソールに出力されます。
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
});
obs$.subscribe(value => {
console.log(value);
},
err => {
console.error('Oops:', err.message);
},
() => {
console.log(`We're done here!`);
});
そして、コンソールに出力されるものは次のとおりです。
0
1
2
3
Oops: too high!
キャッチオペレーター
catch 演算子を使用して、ストリームで発生したエラーを処理できるため、未処理のエラーをオブザーバーに伝播させることは最後の手段となるはずです。 catch は、別のオブザーバブルを返すか、追加のキャッチオペレーターがない場合は、次のキャッチオペレーターまたはオブザーバーのエラーハンドラーによって処理されるように再度スローする必要があります。
ここでは、たとえば、値3のオブザーバブルを返します。
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.catch(error => {
return Rx.Observable.of(3);
});
obs$.subscribe(value => {
console.log(value);
},
err => {
console.error('Oops:', err.message);
},
() => {
console.log(`We're done here!`);
});
これがコンソールで得られるものです。 catch を使用して返されたオブザーバブルが完了した後、メインのオブザーバブルストリームがどのように完了するかに注目してください。
0
1
2
3
3
We're done here!
ストリームには、必要な数の catch 演算子を含めることができます。多くの場合、失敗する可能性のあるストリームのステップの近くにキャッチを配置することをお勧めします。
値を返さずにストリームを完了させたい場合は、空のオブザーバブルを返すことができます。
.catch(error => {
return Rx.Observable.empty();
})
または、オブザーバブルをぶら下げ続けて完了を防止したい場合は、neverオブザーバブルを返すことができます。
.catch(error => {
return Rx.Observable.never();
})
監視可能なソースを返す
Catch は、監視可能なソースである2番目の引数を取ることもできます。 このソースを返すと、observableは効果的に最初からやり直して再試行します。
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.catch((error, source$) => {
return source$;
})
これが私たちが得るものです:
0
1
2
3
0
1
2
3
0
1
2
3
...
注意して、エラーが断続的に発生する場合にのみ、監視可能なソースを返す必要があります。 そうしないと、ストリームが引き続き失敗する場合、無限ループが作成されます。 より柔軟な再試行メカニズムについては、tryおよびretryWhenについて以下を参照してください。
ついに
finally 演算子を使用すると、オブザーバブルが正常に完了したかエラーが発生したかに関係なく、操作を実行できます。 これは、未処理のエラーが発生した場合のクリーンアップに役立ちます。 finallyに提供されるコールバック関数は常に実行されます。 簡単な例を次に示します。
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.finally(() => {
console.log('Goodbye!');
});
obs$.subscribe(value => {
console.log(value);
},
err => {
console.error('Oops:', err.message);
},
() => {
console.log(`We're done here!`);
});
これは次のことを示しています。
0
1
2
3
Oops: too high!
Goodbye!
再試行
再試行演算子
try 演算子を使用して、監視可能なストリームを最初から再試行できます。 引数がない場合は無期限に再試行し、引数が渡されると指定された回数だけ再試行します。
次の例では、2回再試行するため、監視可能なシーケンスは合計3回実行されてから、最終的にオブザーバーのエラーハンドラーに伝播されます。
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.retry(2)
obs$.subscribe(value => {
console.log(value);
},
err => {
console.error('Oops:', err.message);
},
() => {
console.log(`We're done here!`);
});
出力された結果は次のとおりです。
0
1
2
3
0
1
2
3
0
1
2
3
Oops: too high!
tryの直後にcatchを追加して、再試行が失敗した後にエラーをキャッチすることもできます。
.retry(1)
.catch(error => {
return Rx.Observable.of(777);
});
0
1
2
3
0
1
2
3
777
We're done here!
restartWhen演算子
try 演算子を使用することはすべて問題ありませんが、バックエンドからのデータのフェッチを再試行することがよくあります。失敗した場合は、少し時間を取ってから再試行してサーバーに負担をかけます。不必要に。 tryWhen 演算子を使用すると、まさにそれを実行できます。 retryWhen は観察可能なエラーを取得し、再試行のスペースを空けるために追加の遅延を使用してそのシーケンスを返すことができます。
ここでは、再試行の間に500ミリ秒待機します。
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.retryWhen(error$ => {
return error$.delay(500);
});
エラーが引き続き発生する場合、上記のコードは永久に再試行します。 設定された回数再試行するには、 scan 演算子を使用して、再試行が行われた回数を追跡し、再試行回数が特定の回数を超えた場合にエラーをチェーンのさらに下流にスローします。
ここで4回目の再試行で、あきらめてエラーをオブザーバーに伝播させます。
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.retryWhen(error$ => {
return error$.scan((count, currentErr) => {
if (count > 3) {
throw currentErr;
} else {
return count += 1;
}
}, 0);
});