9

Is there a way in Dart to throttle function execution like this

Observable.throttle(myFunction,2000);

Shady Aziza
  • 50,824
  • 20
  • 115
  • 113
  • `thottle` or `debounce` from https://github.com/ReactiveX/rxdart should provide that. https://medium.com/fantageek/throttle-vs-debounce-in-rxswift-86f8b303d5d4 might be related. – Günter Zöchbauer Nov 01 '18 at 17:12
  • I am not sure how to incorporate this information with what I have, is listening on buttons is valid with flutter? – Shady Aziza Nov 02 '18 at 00:07
  • In Dart a function that is going to be called repeatedly is most commonly a listener on a `Stream` - do you have a `Stream` here and you're trying to throttle the function passed to `listen`? If that is the case we should think of this as throttling the stream instead of the function. For that use `values.transform(throttle(Duration(seconds: 2))).listen(myFunction)`. https://pub.dartlang.org/packages/stream_transform If this isn't a Stream listener can you elaborate on the use case? Do you need a `Function` you can pass as an argument? – Nate Bosch Nov 02 '18 at 00:25
  • I want to throttle function execution, for example imagine there is a button that is going to send a request to an api, I want the button to be always clickable but throttle the api call for 1 call every 1 minute for example, so user can click as much as they want but I will only send one request per minute. – Shady Aziza Nov 02 '18 at 01:44
  • 1
    You can use a StreamController to transform function calls to a Stream. Perhapsrxdart Observable provides something out-of-the-box to do that. – Günter Zöchbauer Nov 02 '18 at 04:12
  • "for example imagine there is a button that is going to send a request to an api" Does that button expose presses in a Stream? It does on the web... – Nate Bosch Nov 02 '18 at 18:52

5 Answers5

3

Using https://pub.dartlang.org/documentation/rxdart/latest/rx/Observable/throttle.html

So, your example in Dart 2 with RxDart is

final subject = new ReplaySubject<int>();
myCaller(Event event) {
  subject.add(event);
}
subject
  .throttle(Duration(seconds: 2))
  .listen(myHandler);
TruongSinh
  • 4,662
  • 32
  • 52
3
// you can run the code in dartpad: https://dartpad.dev/
typedef VoidCallback = dynamic Function();

class Throttler {
  Throttler({this.throttleGapInMillis});

  final int throttleGapInMillis;

  int lastActionTime;

  void run(VoidCallback action) {
    if (lastActionTime == null) {
      action();
      lastActionTime = DateTime.now().millisecondsSinceEpoch;
    } else {
      if (DateTime.now().millisecondsSinceEpoch - lastActionTime > (throttleGapInMillis ?? 500)) {
        action();
        lastActionTime = DateTime.now().millisecondsSinceEpoch;
      }
    }
  }
}

void main() {
  var throttler = Throttler();
  // var throttler = Throttler(throttleGapInMillis: 1000);
  throttler.run(() {
    print("will print");
  });
  throttler.run(() {
    print("will not print");
  });
  Future.delayed(Duration(milliseconds: 500), () {
    throttler.run(() {
      print("will print with delay");
    });
  });
}
聂超群
  • 1,659
  • 6
  • 14
1

Along the lines of Günter Zöchbauer you can use a StreamController to transform the function calls to a Stream. For the sake of the example let's say that myFunction has an int return value and an int parameter.

import 'package:rxdart/rxdart.dart';

// This is just a setup for the example
Stream<int> timedMyFunction(Duration interval) {
  late StreamController<int> controller;
  Timer? timer;
  int counter = 0;

  void tick(_) {
    counter++;
    controller.add(myFunction(counter)); // Calling myFunction here
  }

  void startTimer() {
    timer = Timer.periodic(interval, tick);
  }

  void stopTimer() {
    if (timer != null) {
      timer?.cancel();
      timer = null;
    }
  }

  controller = StreamController<int>(
    onListen: startTimer,
    onPause: stopTimer,
    onResume: startTimer,
    onCancel: stopTimer,
  );

  return controller.stream;
}

// Setting up a stream firing twice a second of the values of myFunction
var rapidStream = timedMyFunction(const Duration(milliseconds: 500));

// Throttling the stream to once in every two seconds
var throttledStream = rapidStream.throttleTime(Duration(seconds: 2)).listen(myHandler);

Note: pay attention to the extra parameters of the throttleTime. The defaults are trailing=false and leading=true. If you want to keep the latest sample within the throttle period then you rather want trailing: true and leading: false.

Csaba Toth
  • 10,021
  • 5
  • 75
  • 121
1
import 'package:flutter/foundation.dart';
import 'dart:async';

// A simple class for throttling functions execution
class Throttler {
  @visibleForTesting
  final int milliseconds;

  @visibleForTesting
  Timer? timer;

  @visibleForTesting
  static const kDefaultDelay = 2000;

  Throttler({this.milliseconds = kDefaultDelay});

  void run(VoidCallback action) {
    if (timer?.isActive ?? false) return;

    timer?.cancel();
    action();
    timer = Timer(Duration(milliseconds: milliseconds), () {});
  }

  void dispose() {
    timer?.cancel();
  }
}

// How to use
void main() {
  var throttler = Throttler();

  throttler.run(() {
    print("will print");
  });
  throttler.run(() {
    print("will not print");
  });
  Future.delayed(const Duration(milliseconds: 2000), () {
    throttler.run(() {
      print("will print with delay");
    });
  });

  throttler.dispose();
}
Radomir Epur
  • 369
  • 4
  • 4
0

Here's the code for throttling regular functions:

class Throttler {
  final int milliseconds;

  int _lastActionTime;

  int get _millisecondsSinceEpoch => DateTime.now().millisecondsSinceEpoch;

  Throttler({required this.milliseconds})
      : _lastActionTime = DateTime.now().millisecondsSinceEpoch;

  void run(void Function() action) {
    if (_millisecondsSinceEpoch - _lastActionTime > milliseconds) {
      action();
      _lastActionTime = _millisecondsSinceEpoch;
    }
  }
}

Usage:

final throttler = Throttler(milliseconds: 100);

throttler.run(() => print('1'));
throttler.run(() => print('2'));

// only '1' is printed
Alex Lomia
  • 6,705
  • 12
  • 53
  • 87