2

I want to delay a function once it has been run so it can't be run within that time again.

An example of approaching it:

ngOnInit() {
 setInterval(() => this.functionWithTimeLimit(), 500);
}

functionWithTimeLimit() {
 if(allowedToRun){
  console.log('Run me every 2 seconds at most');

  allowedToRun = false;
  setTimeout(() => allowedToRun = true, 2000); // Allow to execute this function again after two seconds
 }
}

But I am sure there is a much better way to do this with probably rxjs. How could I do that?

Sinan Samet
  • 6,432
  • 12
  • 50
  • 93
  • 1
    Just look through the different "throttle" approaches in this question: [Simple throttle in js](https://stackoverflow.com/questions/27078285/simple-throttle-in-js) – Andreas Jul 31 '20 at 09:44
  • Throttle was the terminology I was looking for thank you! It led me to this page: https://www.telerik.com/blogs/debouncing-and-throttling-in-javascript#:~:text=Throttling%20is%20a%20technique%20in,Hello%2C%20world%20on%20the%20console. Which allowed me to understand I really need throttle and not debounce. – Sinan Samet Jul 31 '20 at 09:51

2 Answers2

2

There are multiple ways to do it.

One way would be to create a RxJS Subject and route the function calls to emit a notification from it. And in the subscription to this observable you could use RxJS throttleTime operator to ignore the subsequent emissions for a specified amount of time.

Try the following

Controller

import { Subject } from 'rxjs';
import { throttleTime, takeUntil } from 'rxjs/operators';

export class AppComponent implements OnInit, OnDestroy {
  completed$ = new Subject<any>();
  trigger$ = new Subject<any>();

  constructor() { }

  functionWithTimeLimit() {
    console.log('called functionWithTimeLimit');
  }

  ngOnInit() {
    this.trigger$.pipe(
      throttleTime(2000),             // <-- ignore notifications for 2s after an emission
      takeUntil(this.completed$)      // <-- use to close the subscription
    ).subscribe(this.functionWithTimeLimit.bind(this));
  }

  ngOnDestroy() {
    this.completed$.next();           // <-- close impending subscriptions 
  }
}

Template

<button (mouseup)="trigger$.next()">Trigger function</button>

Working example: Stackblitz

In the above example, say originally the button press was supposed to trigger the function directly. But now we emit the observable when the button is pressed and trigger the function in it's subscription.

Update: Difference between throttleTime and debounceTime

throttleTime will suspend subsequent notifications for a fixed amount of time after a single notification.

var { fromEvent } = rxjs;
var { throttleTime, map } = rxjs.operators;

var inputBox = document.getElementById('search');
var displayBox = document.getElementById('display');

fromEvent(inputBox, 'keyup').pipe(
  map(event => event.currentTarget.value),
  throttleTime(2000),
).subscribe(value => displayBox.innerHTML = value);
<script src="https://unpkg.com/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>

Keep on typing. It will render the value continuously every 2 sec.<br><br>

<input type="text" id="search" />
<p id="display"></p>

debounceTime will suspend subsequent notifications for a fixed amount of time after every notification.

var { fromEvent } = rxjs;
var { debounceTime, map } = rxjs.operators;

var inputBox = document.getElementById('search');
var displayBox = document.getElementById('display');

fromEvent(inputBox, 'keyup').pipe(
  map(event => event.currentTarget.value),
  debounceTime(2000),
).subscribe(value => displayBox.innerHTML = value);
<script src="https://unpkg.com/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>

Keep on typing. It'll render only after 2 seconds after the typing stops.<br><br>

<input type="text" id="search" />
<p id="display"></p>
ruth
  • 29,535
  • 4
  • 30
  • 57
  • Thank you, this is what I was looking for! It isn't possible to call the function again within a timespan, and the timespan doesn't reset when it is called again unlike debouncing. – Sinan Samet Jul 31 '20 at 09:56
1

You can use interval along with debounceTime to better handle this occasion.

Be aware to unsubscribe when appropriate (probably on ngOnDestroy)

I have a working example, not sure if it is your case, but you can play with it. StackBlitz

StPaulis
  • 2,844
  • 1
  • 14
  • 24
  • 1
    Thank you, it's close but it isn't exactly what I'm looking for. I think throttling would suit my needs better. – Sinan Samet Jul 31 '20 at 09:54