Intro
When I write C#, I often write like below.
private async Task ExecuteExampleAsync()
{
var result1 = await DoSomethingAsync1();
if (result1 == null)
{
return;
}
var result2 = await DoSomethingAsync2();
if (result2 == null)
{
return;
}
return await DoSomethingAsync3();
}
I want to stop operations and return earlier when I get some invalid result from the pervious operation.
How I can do with RxJS?
I try it.
Environments
- Angular ver.10.1.0-next.1
- RxJS ver.6.6
empty, throwError, throw new Error
I think I can use three ways to stop operations.
Base sample class
In this time, I call some methods to try.
workflow-page.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable, of, empty, throwError } from 'rxjs';
import { flatMap, catchError } from 'rxjs/operators';
@Component({
selector: 'app-workflow-page',
templateUrl: './workflow-page.component.html',
styleUrls: ['./workflow-page.component.css']
})
export class WorkflowPageComponent implements OnInit {
constructor() { }
ngOnInit(): void {
// call methods
}
}
empty
Once "empty" is called, "complete" handler will be called immediately.
...
ngOnInit(): void {
console.log('-- Throw empty from first call --');
this.executeEmpty(4);
console.log('-- Throw empty from second call --');
this.executeEmpty(3);
console.log('-- End --');
}
private executeEmpty(startValue: number) {
this.getEmpty(startValue)
.pipe(
flatMap(result => {
console.log(`2nd execution: ${result}`);
return this.getEmpty(result);
}),
catchError(error => {
console.error(`catch: ${error}`);
return of(error);
})
)
.subscribe(result => console.log(`next: ${result}`),
error => console.error(`error: ${error}`),
() => console.log('complete'));
}
private getEmpty(lastValue: number): Observable<number> {
if (lastValue > 3) {
return empty();
}
return of(lastValue + 1);
}
result
-- Throw empty from first call --
complete
-- Throw empty from second call --
2nd execution: 4
complete
-- End --
If the method uses "empty", it must handle the result of operations.
Because the caller("ngOnInit()" in this sample) only can know the operation is completed.
throwError
...
ngOnInit(): void {
console.log('-- Throw throwError from first call --');
this.executeThrowError(4);
console.log('-- Throw throwError from second call --');
this.executeThrowError(3);
console.log('-- Throw throwError with catchError from first call --');
this.executeThrowErrorWithCatchError(4);
console.log('-- Throw throwError with catchError from second call --');
this.executeThrowErrorWithCatchError(3);
console.log('-- End --');
}
private executeThrowError(startValue: number) {
this.getThrowError(startValue)
.pipe(
flatMap(result => {
console.log(`2nd execution: ${result}`);
return this.getThrowError(result);
})
)
.subscribe(result => console.log(`next: ${result}`),
error => console.error(`error: ${error}`),
() => console.log('complete'));
}
private executeThrowErrorWithCatchError(startValue: number) {
this.getThrowError(startValue)
.pipe(
flatMap(result => {
console.log(`2nd execution: ${result}`);
return this.getThrowError(result);
}),
catchError(error => {
console.error(`catch: ${error}`);
return of(error);
})
)
.subscribe(result => console.log(`next: ${result}`),
error => console.error(`error: ${error}`),
() => console.log('complete'));
}
public getThrowError(lastValue: number): Observable<number> {
if (lastValue > 3) {
return throwError('Error from throwError');
}
return of(lastValue + 1);
}
result
-- Throw throwError from first call --
error: Error from throwError
...
-- Throw throwError from second call --
2nd execution: 4
error: Error from throwError
...
-- Throw throwError with catchError from first call --
catch: Error from throwError
...
next: Error from throwError
complete
-- Throw throwError with catchError from second call --
2nd execution: 4
catch: Error from throwError
...
next: Error from throwError
complete
-- End --
When I don't add "catchError", "error" handler of "subscribe" will be fired.
When I add "catchError", it will be fired and if it return "of(error)", "next" and "complete" handler of "subscribe" will be fired.
So when I want to handle the invalid value on "catchError" or "error" handler, I can use "throwError".
throw new Error
How about throwing error, or when some errors are occurred in methods?
...
ngOnInit(): void {
console.log('-- Throw new Error from first call --');
this.executeThrowNewError(4);
console.log('-- Throw new Error from second call --');
this.executeThrowNewError(3);
}
private executeThrowNewError(startValue: number) {
this.getThrowNewError(startValue)
.pipe(
flatMap(result => {
console.log(`2nd execution: ${result}`);
return this.getThrowNewError(result);
})
)
.subscribe(result => console.log(`next: ${result}`),
error => console.error(`error: ${error}`),
() => console.log('complete'));
}
// throw new Error
public getThrowNewError(lastValue: number): Observable<number> {
if (lastValue > 3) {
throw new Error('Error from new Error()');
}
return of(lastValue + 1);
}
result
-- Throw new Error from first call --
ERROR Error: Error from new Error()
Why I got only the first result?
It's because if errors are occurred in first execution, "catchError" and "error" handler won't be able to handle them.
So if I change the argument from 4 to 3, the result will be like below.
-- Throw new Error from first call --
2nd execution: 4
error: Error: Error from new Error()
...
So I think the methods(functions) what returns Observable value shouldn't use "throw new Error".
They should notify caller some errors by "throwError".
Should I use "throwError" to notify?
I think if I choose from "empty", "throwError", "throw new Error" I prefer to "throwError".
I still don't know if I should use "throwError" to notify some operations are failed :<
So I will read more samples or documents.
Top comments (0)