2

Note: I know there is a timer observable from rxjs, I am using this as a proof of concept to learn how observables work.

I have created a timer in angular that has a button to start, a text with the number of seconds ellapsed and a text with the state (stopped/executing/finished).

When I click the button, the current example starts running and the state changes to "executing" and the number of second changes. But when it gets to 5, it stops (the "finishing" log is shown and the number of seconds no longer changes) but the state does not change to "finished" (and neither the "finished" console log appears). And the error method is not executed, either.

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-reloj',
  templateUrl: './reloj.component.html',
  styleUrls: ['./reloj.component.css']
})
export class RelojComponent implements OnInit {

  public time: number = 0;
  public state: string = "Stopped";
  private crono = new Observable<number>(
    observer => {
      let start: number = (new Date()).getTime();
      let lastCount = 0;
      (function repeat() {
        let now: number = (new Date()).getTime();
        let seconds = ((now - start) / 1000 | 0);
        if (lastCount < seconds) {
          observer.next(seconds);
          if (seconds >= 5) {
            console.log("Finishing");
            return;
          }
          lastCount = seconds;
        }
        setTimeout(
          repeat,
          100);
        })();
    });

  constructor() { }

  ngOnInit(): void {
  }

  onStart(): void {
    console.log("Starting");
    let self = this;
    this.state = "Executing";
    this.crono.subscribe({
      next(seconds: number) {
        console.log(`At next ${seconds}`);
        self.time = seconds;
      },
      error(err) {
        console.log("Error");
      },
      complete() {
        console.log("Finished");
        self.state = "Finished";
      }
    })
  }
}

and the html

<button (click)="onStart()">Start</button>
<div>{{time}}</div>
<div>{{state}}</div>
SJuan76
  • 24,532
  • 6
  • 47
  • 87

1 Answers1

1

You need to invoke observer.complete() to tell the subscriber that the operation completed.

if (seconds >= 5) {
        console.log("Finishing");
        observer.complete();
        return;
      }
Julio Cachay
  • 780
  • 5
  • 10
  • It seems that you are right, I was kind of expecting that complete() would have been called automatically once the observer method had exited, or at least that if you needed to call complete from your code the documentation would be more explicit. – SJuan76 Apr 28 '22 at 16:40
  • The `add()` method, chained after `subscribe()` [seems to do what we want](https://stackoverflow.com/a/60239062/1149880): `.subscribe(...).add(...)`. – twigmac Feb 06 '23 at 13:11