99

I need a timer in Angular 2, which tick after a time interval and do some task (may be call some functions).

How to do this with Angular 2?

Mel
  • 5,837
  • 10
  • 37
  • 42
kuntal
  • 1,591
  • 2
  • 16
  • 36
  • 2
    Include just enough code to allow others to reproduce the problem. For help with this, read How to create a Minimal, Complete, and Verifiable example. If it is possible to create a live example of the problem that you can link to (for example, on http://sqlfiddle.com/ or http://jsbin.com/) then do so - but also include the code in your question itself. Not everyone can access external sites, and the links may break over time. – Prasad Mar 05 '16 at 11:10
  • 16
    This is actually a very helpful question. Observables are important to learn and use. Even if the user didn't have code to go from this question will be helpful to others. – Winnemucca Apr 12 '16 at 16:36
  • This comment falls in the same bucket, but neither of the previous 2 people have helped, just commented on the question... Apparently people are using setTimeout again now. – rbnzdave Aug 01 '16 at 19:09
  • setTimeout is really old-school - checkout out the fresh TimerObservable below – Philip Sep 23 '16 at 15:50
  • 2
    let this._timer = setInterval(() => this.getWidgetData(), 10000); and make sure to call clearInterval(this._timer); on destroy – crh225 Nov 09 '16 at 18:36

10 Answers10

129

In Addition to all the previous answers, I would do it using RxJS Observables

please check Observable.timer

Here is a sample code, will start after 2 seconds and then ticks every second:

import {Component} from 'angular2/core';
import {Observable} from 'rxjs/Rx';

@Component({
    selector: 'my-app',
    template: 'Ticks (every second) : {{ticks}}'
})
export class AppComponent {
  ticks =0;
  ngOnInit(){
    let timer = Observable.timer(2000,1000);
    timer.subscribe(t=>this.ticks = t);
  }
}

And here is a working plunker

Update If you want to call a function declared on the AppComponent class, you can do one of the following:

** Assuming the function you want to call is named func,

ngOnInit(){
    let timer = Observable.timer(2000,1000);
    timer.subscribe(this.func);
}

The problem with the above approach is that if you call 'this' inside func, it will refer to the subscriber object instead of the AppComponent object which is probably not what you want.

However, in the below approach, you create a lambda expression and call the function func inside it. This way, the call to func is still inside the scope of AppComponent. This is the best way to do it in my opinion.

ngOnInit(){
    let timer = Observable.timer(2000,1000);
    timer.subscribe(t=> {
        this.func(t);
    });
}

check this plunker for working code.

Abdulrahman Alsoghayer
  • 16,462
  • 7
  • 51
  • 56
  • Should I able to pass a function to ` timer.subscribe(t=>this.ticks = t);` which is call in every tick ? – kuntal Mar 06 '16 at 14:40
  • @matrixwebtech Yes, the 't=>this.ticks = t' is a lambda expression, exactly the same as 'function(t){ this.ticks = t }' – Abdulrahman Alsoghayer Mar 06 '16 at 14:42
  • I try timer.subscribe(function name() { console.log("hhhhhh") }); which working but how I call a function which declared separately ngOnInit() { let timer = Observable.timer(2000,1000); timer.subscribe(function() { this.fun(); }); } fun() { console.log("hhhhhh") } – kuntal Mar 07 '16 at 14:42
  • @matrixwebtech please check my updated answer for examples. – Abdulrahman Alsoghayer Mar 07 '16 at 17:17
  • I am interested in creating a timer with milliseconds as well but when I say `Observable.timer(1000, 1)` it returns some kind of weird fraction of seconds, not millis, someone knows why? – Kutyel Apr 08 '16 at 06:26
  • @Kutyel [Check out this plunker](http://plnkr.co/edit/tWAjtYNfZBLZi6FAoX00?p=preview). The behavior is not Angular neither Rxjs related. I can't explain it though. But I think this would a good general javascript question :) – Abdulrahman Alsoghayer Apr 08 '16 at 14:40
  • I answered below, that Observable.timer us outdated - You need to use **TimerObservable** now – Philip Sep 23 '16 at 15:49
  • @PhilipMiglinci thanks for your input. But `timer` is not outdated. It's a static method that returns a **TimerObservable** and shorthand for `TimerObservable.create(...)`. Please [check the documentation](http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-timer) and [source code](https://github.com/ReactiveX/rxjs/blob/master/src/observable/timer.ts) – Abdulrahman Alsoghayer Sep 23 '16 at 20:46
  • @Abdulrahman Thanks,```Oberservable.timer()``` didn't work for me in the first place, that's why I thought it was deprecated, but good to now! – Philip Sep 24 '16 at 06:42
  • How is this affecting performance when going to 1 ms, is this using requestanimationframe under the hood? Thanks for your answer. – Miguel Stevens Feb 20 '17 at 16:17
  • 1
    @Notflip Under the hood, `timer` seems to use `setInterval()`. But, you can pass the `animationFrame` scheduler (which uses `requestAnimationFrame()`) to use it instead of the default `async` scheduler. All you need to do is `Observable.timer(*,*,Scheduler.animationFrame)`, given `import {Scheduler} from ‘rxjs’`.Although, on `timer` it doesn't seem to work. It still seems to use `setInterVal()`. However, on other kinds of observable such as `Observable.range(0,1000,Scheduler.animationFrame)`, the `requestAnimationFrame` is used for sure. performance wise, I can't answer you for sure right now. – Abdulrahman Alsoghayer Feb 21 '17 at 00:31
  • @Abdulrahman Is it possible to unsubscribe / Stop the timer when you wish to stop it ,similar to what Philip Miglinci has done with ngOnDestroy(). – sur Mar 06 '17 at 06:08
  • @sur Yes, exactly as in Philip Miglinci's answer. That also applies to all types of subscriptions, not just the timer. – Abdulrahman Alsoghayer Mar 06 '17 at 13:20
  • Is there a way to sustain the timer? I mean I have created a timer in a service which will call logout() function after certain interval. But the problem is that as soon as I redirect to another component , the timer no longer exists – SamuraiJack May 11 '18 at 06:53
79

Another solution is to use TimerObservable

TimerObservable is a subclass of Observable.

import {Component, OnInit, OnDestroy} from '@angular/core';
import {Subscription} from "rxjs";
import {TimerObservable} from "rxjs/observable/TimerObservable";

@Component({
  selector: 'app-component',
  template: '{{tick}}',
})
export class Component implements OnInit, OnDestroy {

  private tick: string;
  private subscription: Subscription;

  constructor() {
  }

  ngOnInit() {
    let timer = TimerObservable.create(2000, 1000);
    this.subscription = timer.subscribe(t => {
      this.tick = t;
    });
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

P.S.: Don't forget to unsubsribe.

Philip
  • 23,316
  • 2
  • 31
  • 31
  • 3
    the unsubscribe part is key – Tucker Jan 08 '17 at 15:38
  • how do you unsubscribe? and when? – Miguel Stevens Feb 20 '17 at 15:54
  • 2
    @Notflip ngOnDestroy is called during component deinitialization: `this.subscription.unsubscribe();` unsubscribes. – Philip Feb 21 '17 at 10:06
  • How are you able to use this.subscription without referencing it above? – Winnemucca Mar 28 '17 at 20:57
  • This works like a treat. I am using in Ionic 2 and placed the unsubscribe in the ionViewDidLeave lifecycle event and it clears the interval lovely, something that does not work so well with the old school clear interval. – Gurnard Apr 03 '17 at 14:30
  • From what I've read the takeWhile() operator is the preferred method of subscribing. See my answer to this HTTP interval question for some usage. https://stackoverflow.com/questions/35316583/angular2-http-at-an-interval/44418812#44418812 – ZackDeRose Oct 27 '17 at 11:41
  • 2
    How do you pause and restart it? – Dani Jan 28 '18 at 18:55
12
import {Component, View, OnInit, OnDestroy} from "angular2/core";

import { Observable, Subscription } from 'rxjs/Rx';

@Component({

})
export class NewContactComponent implements OnInit, OnDestroy {

    ticks = 0;
    private timer;
    // Subscription object
    private sub: Subscription;


    ngOnInit() {
        this.timer = Observable.timer(2000,5000);
        // subscribing to a observable returns a subscription object
        this.sub = this.timer.subscribe(t => this.tickerFunc(t));
    }
    tickerFunc(tick){
        console.log(this);
        this.ticks = tick
    }

    ngOnDestroy(){
        console.log("Destroy timer");
        // unsubscribe here
        this.sub.unsubscribe();

    }


}
Pardeep Jain
  • 84,110
  • 37
  • 165
  • 215
8

With rxjs 6.2.2 and Angular 6.1.7, I was getting an:

Observable.timer is not a function

error. This was resolved by replacing Observable.timer with timer:

import { timer, Subscription } from 'rxjs';

private myTimerSub: Subscription;    

ngOnInit(){    
    const ti = timer(2000,1000);    
    this.myTimerSub = ti.subscribe(t => {    
        console.log("Tick");    
    });    
}    

ngOnDestroy() {    
    this.myTimerSub.unsubscribe();    
}
BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
Matt Saunders
  • 3,538
  • 2
  • 22
  • 30
  • 3
    To do the unsubscribe, you have to have a subscription variable like @alexis-poo above. See: https://stackoverflow.com/questions/40181169/timer-unsubscribe-is-not-a-function-angular2. I base this on your answer not working as written in that regard. – Reid Oct 16 '18 at 05:57
  • I love this guy who is always updating the Posts to the newest Angular versions... Everytime so much struggle with AngularJS and Angular. Thanks man! – chainstair Apr 02 '19 at 22:24
4

You can simply use setInterval utility and use arrow function as callback so that this will point to the component instance.

For ex:

this.interval = setInterval( () => { 
    // call your functions like 
    this.getList();
    this.updateInfo();
});

Inside your ngOnDestroy lifecycle hook, clear the interval.

ngOnDestroy(){
    clearInterval(this.interval);
}
Shivang Gupta
  • 3,139
  • 1
  • 25
  • 24
3

I faced a problem that I had to use a timer, but I had to display them in 2 component same time, same screen. I created the timerObservable in a service. I subscribed to the timer in both component, and what happened? It won't be synched, cause new subscription always creates its own stream.

What I would like to say, is that if you plan to use one timer at several places, always put .publishReplay(1).refCount() at the end of the Observer, cause it will publish the same stream out of it every time.

Example:

this.startDateTimer = Observable.combineLatest(this.timer, this.startDate$, (localTimer, startDate) => {
  return this.calculateTime(startDate);
}).publishReplay(1).refCount();
Graham
  • 7,431
  • 18
  • 59
  • 84
xyztdanid4
  • 143
  • 4
  • 8
1

Found a npm package that makes this easy with RxJS as a service.

https://www.npmjs.com/package/ng2-simple-timer

You can 'subscribe' to an existing timer so you don't create a bazillion timers if you're using it many times in the same component.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
1

If you look to run a method on ngOnInit you could do something like this:

import this 2 libraries from RXJS:

import {Observable} from 'rxjs/Rx';
import {Subscription} from "rxjs";

Then declare timer and private subscription, example:

timer= Observable.timer(1000,1000); // 1 second for 2 seconds (2000,1000) etc
private subscription: Subscription;

Last but not least run method when timer stops

ngOnInit() {
  this.subscription = this.timer.subscribe(ticks=> {
    this.populatecombobox();  //example calling a method that populates a combobox
    this.subscription.unsubscribe();  //you need to unsubscribe or it will run infinite times
  });
}

That's all, Angular 5

Alexis Poo
  • 82
  • 7
0

Set Timer and auto call service after certain time

 // Initialize from ngInit
    ngOnInit(): void {this.getNotifications();}
    
    getNotifications() {
        setInterval(() => {this.getNewNotifications();
        }, 60000);  // 60000 milliseconds interval 
    }
    getNewNotifications() {
        this.notifyService.getNewNotifications().subscribe(
            data => { // call back },
            error => { },
        );
    }
0

on the newest version of Angular (I work on 12.2.*) Observable.timer is not supported. You can use it with a bit change at @Abdulrahman Alsoghayer example.

import {Component} from '@angular/core';
import {timer} from 'rxjs';

@Component({
    selector: 'my-app',
    template: 'Ticks (every second) : {{ticks}}'
})
export class AppComponent {
  ticks =0;
  ngOnInit(){
    let timer$ = timer(2000,1000);
    timer$.subscribe(t=>this.ticks = t);
  }
}
Kemot 90
  • 96
  • 4