110

I'd like to run a function over and over, with a delay in between. How can I do this with Dart?

Seth Ladd
  • 112,095
  • 66
  • 196
  • 279

7 Answers7

230

You can use the Timer class to schedule one-shot and repeating functions.

Repeating

Here is how you run a repeating function:

import 'dart:async';
main() {
  const oneSec = Duration(seconds:1);
  Timer.periodic(oneSec, (Timer t) => print('hi!'));
}

The Timer takes two arguments, a duration and a function to run. The duration must be an instance of Duration. The callback must take a single parameter, the timer itself.

Canceling a repeating timer

Use timer.cancel() to cancel a repeating timer. This is one reason why timer is passed to the callback run from a repeating timer.


One-shot after a delay

To schedule a one-shot function after a delay (execute once, some time in the future):

import 'dart:async';
main() {
  const twentyMillis = Duration(milliseconds:20);
  Timer(twentyMillis, () => print('hi!'));
}

Notice the callback for a one-shot timer does not take a parameter.


One-shot as soon as possible

You can also request that a function is run as soon as possible, at least one event-loop tick in the future.

import 'dart:async';
main() {
  Timer.run(() => print('hi!'));
}

In HTML

Timers even work in HTML. In fact, window.setTimeout was removed, so Timer is the only way to run a function in the future.

iDecode
  • 22,623
  • 19
  • 99
  • 186
Seth Ladd
  • 112,095
  • 66
  • 196
  • 279
  • If you are wondering, as I was, how to make a timer that does not depend on a compile time constant, the API docs for the timer has the following solution: const ms = const Duration(milliseconds: 1); var duration = ms * variableMS; return new Timer(duration, handleTimeout); where variableMS is some int set other than as a compile time constant. – scribeGriff Feb 21 '13 at 02:25
  • No constructor 'Timer.repeating' declared in class 'Timer'. dart editor intellisense shows Timer.periodic, I think they changed name – SRN Sep 23 '13 at 20:07
  • 1
    in another post you talk about using repeating futures, which one is the 'best' ? – GameAlchemist Feb 19 '14 at 19:52
  • 1
    Don't forget to call timer.cancel() in dispose() method – stan Aug 07 '20 at 06:04
34

5 Sec Timer Example

bool isStopped = false; //global

sec5Timer() {
  Timer.periodic(Duration(seconds: 5), (timer) {
    if (isStopped) {
      timer.cancel();
    }
    print("Dekhi 5 sec por por kisu hy ni :/");
  });
}

Call from any function

sec5Timer(); 

Stop from any function

isStopped = true;

To dispose you can use this code or technique.

 @override
  void initState() {
    _timer = new Timer.periodic(widget.refreshRate, 
      (Timer timer) => _updateDisplayTime(inheritedWidget));
    super.initState();
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }
23

https://api.dartlang.org/stable/1.24.3/dart-async/Stream/Stream.periodic.html

import 'dart:async';

StreamSubscription periodicSub;

void main() {
  periodicSub = new Stream.periodic(const Duration(milliseconds: 500), (v) => v)
      .take(10)
      .listen((count) => print('tick $count'));
}

or if the counter isn't required just

import 'dart:async';

StreamSubscription periodicSub;

void main() {
  periodicSub = new Stream.periodic(const Duration(milliseconds: 500))
      .take(10)
      .listen((_) => print('tick'));
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    How can we use an "infinite" periodic function with a "cancel" action. take(10) only generate un recurring function with 10 call – fvisticot Sep 01 '18 at 12:18
  • 1
    Sorry but I dont understand the question. Please elaborate. – Günter Zöchbauer Sep 01 '18 at 12:37
  • In my case, I need to have an "infinite" periodic function. This periodic function run indefinitely until it's stop by an "event" (Click on a button or exiting the page by exemple). In your case the take(10) argument allows only to run the function 10 times and can not be stop before (by example if we need to stop after 3 calls). – fvisticot Sep 02 '18 at 08:29
  • Search for rxdart. You can also use a StreamController to fully control a stream. – Günter Zöchbauer Sep 02 '18 at 08:31
8

You can also use Future.delayed and await to delay execution:

Future<Null> delay(int milliseconds) {
  return new Future.delayed(new Duration(milliseconds: milliseconds));
}


main() async {
  await delay(500);
  print('Delayed 500 milliseconds');
}
Matt S.
  • 9,902
  • 6
  • 29
  • 25
  • 1
    Thanks, but the question was about running a function over and over (repeatedly). – Seth Ladd Mar 04 '18 at 17:32
  • You could put it inside `while(true)` – Wahyu Jul 10 '19 at 04:28
  • 1
    while(true) would eventually block the main thread and application would not be able to respond to user gesture events – alex_z Aug 27 '19 at 08:33
  • I upvoted this because it made me realize this allows me to periodically execute some code and then do another delay. This ensures the "timer" doesn't keep executing while work is still being done (in case of heavy work that may or may not take more time than the scheduled delay). – Sipke Schoorstra Oct 31 '21 at 14:58
  • Like so: https://gist.github.com/sfmskywalker/24e9e30b38d0bf8a8c453e7b38051514 – Sipke Schoorstra Oct 31 '21 at 15:17
4

alternative;

import 'dart:async';

Timer interval(Duration duration, func) {
  Timer function() {
    Timer timer = new Timer(duration, function);

    func(timer);

    return timer;
  }

  return new Timer(duration, function);
}

void main() {
  int i = 0;

  interval(new Duration(seconds: 1), (timer) {
    print(i++);

    if (i > 5) timer.cancel();
  });
}
Ahmet Şimşek
  • 1,391
  • 1
  • 14
  • 24
  • 1
    Isn't it making recursive timer ? – Yunus Aug 05 '20 at 10:42
  • What's the point of returning the `Timer` from within `function`? The return value is never used, so it just seems to make the code harder to read? – Magnus Jun 20 '21 at 09:31
3

Opposite to Timer.periodic and Stream.periodic posting my favorite way to handle such a tasks. The advantages:

  • the first cycle run instantly
  • the callback can work longer than interval without any reentrance headache
Completer<bool> periodic(Duration interval, Function(int cycle) callback) {
  final done = Completer<bool>();
      () async {
    var cycle = 0;
    while (!done.isCompleted) {
      try {
        await callback(cycle);
      } catch (e, s) {
        log("$e", stackTrace: s);
      }
      cycle++;
      await done.future
          .timeout(interval)
          .onError((error, stackTrace) => null);
    }
  }();
  return done;
}

main() {
  final task = periodic(Duration(seconds: 10), (cycle) async {
    /// do the periodic tasks here
  });

  /// main code here
  /// and when going to stop the above periodic call
  task.complete(true);
}
Alexei Volkov
  • 851
  • 9
  • 11
0

Functionally identical code to JavaScript (setInterval, setTimeout, clearInterval and clearTimeout):

// ------------------------------
// Import:

import 'dart:async';

// ------------------------------
// Definitions:

void clearTimeout(Timer timer) {
  try {
    timer.cancel();
  } catch (e) {}
}

Timer setTimeout(VoidCallback fn, int millis) {
  Timer timer;
  if (millis > 0)
    timer = new Timer(new Duration(milliseconds: millis), fn);
  else
    fn();
  return timer;
}

void clearInterval(Timer timer) {
  try {
    timer.cancel();
  } catch (e) {}
}

Timer setInterval(VoidCallback fn, int millis) {
  Timer timer;
  if (millis > 0)
    timer = new Timer.periodic(new Duration(milliseconds: millis), (timer) {
      fn();
    });
  else
    fn(); // If millis input is too low, only run function once and stop
  return timer;
}

// ---------------------------------
// Example:

int myValue = 0;

Timer counter = setInterval((){ myValue++; }, 50);
setTimeout((){
    clearInterval(counter);
}, 5000);
Jože Vovk
  • 11
  • 1
  • 3