21

I am creating an angular 4 app with typescript.

I'm having a function that needs to be executed every 10 seconds untill a specified stopcondition. I created a loop with some testcode using setTimeout to see if it would work.

My Testcode:

public run() {
    let i = 0;
    while (i < 4) {
        setTimeout(this.timer,3000);
        i++;
    }
}

public timer(){
    console.log("done")
}

However this seems to wait for 3 seconds, or browser is just slow... and then it prints 4 times done. So the code isn't working. Am I doing this wrong or are there other possibilities to do this kind of things?

7 Answers 7

34

Since you are using Angular you can probably do this in a much simpler way using takeWhile:

Observable.interval(10000)
    .takeWhile(() => !stopCondition)
    .subscribe(i => { 
        // This will be called every 10 seconds until `stopCondition` flag is set to true
    })
Sign up to request clarification or add additional context in comments.

3 Comments

when I try this I get an error: ERROR TypeError: WEBPACK_IMPORTED_MODULE_3_rxjs_Observable.Observable.interval is not a function
That's ridiculous I imported { Observable } from rxjs/Observable, that did not work...Thank you for the answer :)
@fangio don't ever import from "rxjs". That will import all the Observable static methods and operators, which will make your application bundle way bigger than it should be. Use the import you had, and add import 'rxjs/add/observable/interval'; import 'rxjs/add/operator/takeWhile';`. That will only import the methods and operators that you actually use.
6

Yes, you're doing it wrong: you have a loop telling 4 times in a row to execute timer() 3 seconds later, from now.

To do what you want, you would have to reschedule the next timer each time timer() is called, or, more simply, to use setInterval():

let count = 0;
const interval = window.setInterval(() => {
    this.timer();
    count++;
    if (count >= 4) {
        window.clearInterval(interval);
    }
}, 3000); 

Note that, since you're using angular, using observables would be muuuch easier:

Observable.interval(3000).take(4).subscribe(() => this.timer());

4 Comments

Just for my own reference, how can you make the Observable interval stop if a certain condition has been met. For instance when a class field becomes true
takeWhile(() => !this.someField)
Lovely! It's like they have a solution for everything :)
@JBNizet Could you please show how to import the libraries correctly?
6

I did it with Angular 6. This code requests every 5 seconds to get the progress of rendering. It will stops sending request when the progress reaches %100.

import {interval} from "rxjs";

getProgress(searchId): void{
const subscription = interval(5000)
  .subscribe(()=>{
    //Get progress status from the service every 5 seconds
    this.appService.getProgressStatus(searchId)
      .subscribe((jsonResult:any)=>{
          //update the progress on UI 

          //cancel subscribe until it reaches %100
          if(progressPercentage === 100)
            subscription.unsubscribe();
        },
        error => {
          //show errors
        }
      );
  });
}

Comments

3

This is indeed not the way to use an async method. The while loop just goes 4 times through it in one go, and initiate the 4 timers. Which will output simultaneously as well in 3 seconds. You can however leverage the await and async functionality from TypeScript:

public stopCondition: boolean = false;

public async run(): Promise<void> {
    while (!this.stopCondition) {
       await new Promise<void>(resolve => {
           setTimeout(resolve, 10000);
       });
       this.execute();
    }
    console.log('done');
}

public execute(): void {
    if ('whatever should trigger your stop condition') {
       this.stopCondition = true;
    }
}

This will run the execute method after every 10 seconds, for as long as the stopCondition === false. When the stopCondition === true it will output done.

2 Comments

This worked for me. I had to remove "void" as not supported ES5 for an async function. But the above approach worked. Thank you for taking time to contribute this.
@Kirk my bad indeed. Should be Promise<void> for typescript, and ES5 does not support typings, so you should remove it :)
2

Use the function setInterval(hander:(args:any[]),ms:Number,args:any[]) which is one of the methods of OnInit.

setInterval(a=>{
  alert("yes....");
},10000,[]);

Will show alert "yes" after 10 secs.

1 Comment

have you tried this inside an angular fucntion that toggles a boolean attribute? Doesnt seem to work on mine...
0

Yes, it is correct behavior. You have synchronous loop which created 4 delayed actions and ends this loop. It happens in few milliseconds. So all 4 delayed actions are registered to be started in 3 seconds, about at same time.

So in 3 secs you will receive all 4 responses from this delayed actions.

If you want to have consequence call execution (first after 3 sec, then second after first) consider to use promises for this and call new promise withh 3 second delay after previous completion.

fisrtPromise
   .then(secondPromise)
   .then(thirdPromise);

https://developer.mozilla.org/uk/docs/Web/JavaScript/Reference/Global_Objects/Promise

Comments

0

Since you are calling setTimeout inside while loop and because of asynchronous execution of statements it will not wait to execute Timer function before going to next iteration. You can achieve required functionality by using below code

public run() {
    var i = 0;
    var interval = setInterval(() => {
        if (++i === 4) {                
            clearInterval(interval);
        }
        else {
            this.timer();
        }
    }, 3000);

}

public timer() {
    console.log("done")
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.