[Angular][RxJS] I want to return immediatly when some operations are failed
Tags: angular, rxjs
Published: 2020-07-18 05:17:22

## Intro
When I write C#, I often write like below.

```csharp
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
```typescript
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.

```typescript
...
  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.

![empty](https://dev-to-uploads.s3.amazonaws.com/i/tq2yp2izs8fefdtn6rzp.png)

* [RxJS - empty](https://rxjs.dev/api/index/function/empty)

### throwError

```typescript
...
  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. 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/epauzxb42ri64pp5ae4h.png)

* When I add "catchError", it will be fired and if it return "of(error)", "next" and "complete" handler of "subscribe" will be fired.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/v5usy6fbwf3f2x5bx7nj.png)

So when I want to handle the invalid value on "catchError" or "error" handler, I can use "throwError".

* [RxJS - throwError](https://rxjs.dev/api/index/function/throwError)
* [RxJS - catchError](https://rxjs.dev/api/operators/catchError)

### throw new Error
How about throwing error, or when some errors are occurred in methods?

```typescript
...
  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.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fmekd28ixlxxdg8xnbhs.png)

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()
...
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/x2j7a4vr8khob5jye4w7.png)

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.