How to use setInterval/setTimeout in Dart SDK 0.4+ - dart

I realised that in current Dart SDK version 0.4.1.0_r19425 methods like setTimeout, setInterval, clearTimeout, clearInterval aren't part of Window class any more and they all moved to WorkerContext.
Is there any documentation on how to use them now? Do I need to create a new instance of WorkerContext every time I want to use them?

In addition to Timer mentioned by Chris, there is a Future-based API:
var future = new Future.delayed(const Duration(milliseconds: 10), doStuffCallback);
There is not yet direct support for cancelling a Future callback, but this works pretty well:
var future = new Future.delayed(const Duration(milliseconds: 10));
var subscription = future.asStream().listen(doStuffCallback);
// ...
subscription.cancel();
Hopefully, there will soon be a Stream version of Timer.repeating as well.

You can use:
1) SetInterval
_timer = new Timer.periodic(const Duration(seconds: 2), functionBack);
Where: `functionBack(Timer timer) {
print('again');
}
2) SetTimeOut
_timer = Timer(Duration(seconds: 5), () => print('done'));
Where _time is type Time

From this post on the group (Feb 14th 2013).
// Old Version
window.setTimeout(() { doStuff(); }, 0);
// New Version
import 'dart:async';
Timer.run(doStuffCallback);
And another example (copied from the same post)
// Old version:
var id = window.setTimeout(doStuffCallback, 10);
.... some time later....
window.clearTimeout(id);
id = window.setInterval(doStuffCallback, 1000);
window.clearInterval(id);
// New version:
var timer = new Timer(const Duration(milliseconds: 10), doStuffCallback);
... some time later ---
timer.cancel();
timer = new Timer.repeating(const Duration(seconds: 1), doStuffCallback);
timer.cancel();
Specifically, they are now part of the Timer class in the dart:async library (rather than WorkerContext, which seems to be IndexedDb specific). API docs here

Related

How to handle "onCancel" of Timer.periodic or Stream.periodic

Whenever a user wants to play some audio, first I want to wait 5 seconds and every second perform an action. Whenever the user pushes play button, I call onPlay() method. My first attempt of it was this:
Timer? _timer; // I might need to cancel timer also when the user pauses playback by pause button or something
void onPlay() async {
int _secondsLeft = 5;
_timer = await Timer.periodic(const Duration(seconds: 1), (_) {
if (_secondsLeft == 0) _timer?.cancel();
_textToSpeech.speak(_secondsLeft.toString());
--_secondsLeft;
});
audio.play(_curSong);
}
This attempt was not working correctly as audio.play() run immediately after Timer was initialized, not finished. I found How to make Timer.periodic cancel itself when a condition is reached? and used one of the answers to improve my code like this:
StreamSubscription? _timer;
void onPlay() async {
int _secondsLeft = 5;
_timer = await Stream.periodic(const Duration(seconds: 1))
.takeWhile((_) => _secondsLeft > 0)
.forEach((_) {
_textToSpeech.speak(_secondsLeft.toString());
--_secondsLeft;
});
audio.play(_curSong);
}
This worked better, but if the user switches to a different song or pauses playback during the intial phase, _timer?.cancel() will only cancel the stream, so everything after it (audio.play(_curSong)) is still being run. But I would like to cancel not only the stream, but also skip the audio.play() function. So I would like to run audio.play() only after the Timer/Stream finishes properly without cancellation.
Is there any option to handle Stream.periodic or Timer.periodic cancel action ("onCancel")? Or is there a better way to handle this specific task?
Almost there. Your await on .forEach actually returns null. So there is no StreamSubscription as far as I can see.
The callback that might be the simple solution for your problem is the optional onDone in the listen method. This won't be called when Stream is cancelled.
import 'dart:async';
void main() async {
onPlay();
await Future.delayed(Duration(seconds: 2));
// Uncomment me to test it
// _timer?.cancel();
}
StreamSubscription? _timer;
void onPlay() {
int _secondsLeft = 5;
_timer = Stream.periodic(const Duration(seconds: 1))
.takeWhile((_) => _secondsLeft > 0)
.listen((event) {
print(_secondsLeft.toString());
--_secondsLeft;
}, onDone: () {
print("It's show time!");
});
}
You are very close, you just need to only play the song when the timer has counted down, at the same time you cancel the timer.
So:
Timer? _timer;
void onPlay() async {
int _secondsLeft = 5;
_timer = await Timer.periodic(const Duration(seconds: 1), (_) {
if (_secondsLeft == 0) {
_timer?.cancel();
audio.play(_curSong);
} else {
_textToSpeech.speak(_secondsLeft.toString());
--_secondsLeft;
}
});
}
(Notice that this doesn't say zero, where your original did. Instead it plays the music. If you want it to say zero, change the == 0 to < 0.)
With this way of writing it, canceling the timer stops you from playing the song.

Add delay constructing a Future

Playing with Dart, is it possible to create a delay constructing a Future?:
Future<String>.value("Hello").then((newsDigest) {
print(newsDigest);
}) // .delayed(Duration(seconds: 5))
Yes, this is possible:
factory Future.delayed(Duration duration, [FutureOr<T> computation()]) {
_Future<T> result = new _Future<T>();
new Timer(duration, () {
try {
result._complete(computation?.call());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
});
return result;
}
As you have already discovered Future.delayed constructor creates a future that runs after a delay:
From the docs:
Future<T>.delayed(
Duration duration,
[ FutureOr<T> computation()
])
The computation will be executed after the given duration has passed, and the future is completed with the result of the computation.
If computation returns a future, the future returned by this constructor will complete with the value or error of that future.
For the sake of simplicity, taking a future that complete immediately with a value, this snippet creates a delayed future that complete after 3 seconds:
import 'dart:async';
main() {
var future = Future<String>.value("Hello");
var delayedFuture = Future.delayed(Duration(seconds: 3), () => future);
delayedFuture.then((value) {
print("Done: $value");
});
}

How to catch errors thrown in timer function calls?

I have code similar to:
timer = new Timer(new Duration(milliseconds: 1000), () => (throw new TimeoutException('Callback not invoked!')));
while (timer.isActive){
await new Future.delayed(const Duration(milliseconds: 1), () => "1");
}
print('this should not be reached if the exception is raised');
elsewhere I have an async callback which calls:
timer.cancel();
In the case where the callback is invoked it works fine because the callback cancels the timer.
However, I'm not really sure how to actually catch the TimeoutException in this case if it is not canceled, because it seems the exception is raised in a different scope than my main function. This means program execution continues even though
Is there a way to do some sort of try/catch or somehow handle the above timeout exception? Or a better way to do what I am trying to do?
Using dart 1.19.1.
You get different behavior depending on whether the timeout is 500ms or 1500ms:
final timer = new Future.delayed(const Duration(milliseconds: 1000),
() => (throw new Exception('Callback not invoked!')))
.timeout(const Duration(milliseconds: 500));
try {
await timer;
} on TimeoutException catch(e) {
print('this should not be reached if the exception is raised');
} on Exception catch(e) {
print('exception: $e');
}
DartPad

How to timeout a future computation after a timeLimit?

When defining a Future as follows:
Future<HttpRequest> httpRequest = HttpRequest.request(url,
method: method, requestHeaders: requestHeaders);
I want to handle a timeout after 5 secondes. I'm writing my code like this :
httpRequest.timeout(const Duration (seconds:5),onTimeout : _onTimeout());
Where my timeout function is :
_onTimeout() => print("Time Out occurs");
According to the Future timeout() method documentation , If onTimeout is omitted, a timeout will cause the returned future to complete with a TimeoutException. But With my code , my method _onTimeout() is properly called (but immediately, not after 5 seconds) and I always get a
TimeException after 5 seconds... (TimeoutException after 0:00:05.000000: Future not completed )
Am I missing something ?
Change this line
httpRequest.timeout(const Duration (seconds:5),onTimeout : _onTimeout());
to
httpRequest.timeout(const Duration (seconds:5),onTimeout : () => _onTimeout());
or just pass a reference to the function (without the ())
httpRequest.timeout(const Duration (seconds:5),onTimeout : _onTimeout);
This way the closure that calls _onTimeout() will be passed to timeout().
In the former code the result of the _onTimeout() call will be passed to timeout()
Future.await[_doSome].then((data){
print(data);
}).timeout(Duration(seconds: 10));
Using async/await style. You can add .timeout to any Future you are awaiting.
final result = await InternetAddress
.lookup('example.com')
.timeout(
Duration(seconds: 10),
onTimeout: () => throw TimeoutException('Can\'t connect in 10 seconds.'),
);
In order to stop any Future by timeout one can use timeout(). There are two examples:
Throw exception after timeout
final someHardTaskFuture = Future.delayed(const Duration(hours: 1), () => 42);
final newFutureWithTimeoutAndException = someHardTaskFuture.timeout(const Duration(seconds: 3));
Returns default value (11) on timeout
final someHardTaskFuture = Future.delayed(const Duration(hours: 1), () => 42);
final newFutureWithTimeoutAndDefaultValue = someHardTaskFuture
.timeout(const Duration(seconds: 3), onTimeout: () => 11);
print(await newFutureWithTimeoutAndDefaultValue);

Execute Futures until a parameter becomes true

I launch a request to a server with a future "requestServer".
I would like to poll a system for a specific value (passed from false to true, when request is done) and return when finished.
Code could be like that, but "while" synchronous and "checkOperation" is asynchronous?
return requestServer().then((operation) {
var done = false;
while (done)
return checkOperation(operation).then((result) {
done = (result == true);
});
sleep(10);
}
});
Any ideas ?
I guess this is not exactly what you want but as far as I know there is no way to block execution so you have to use callbacks.
void main(List<String> args) {
// polling
new Timer.periodic(new Duration(microseconds: 100), (t) {
if(isDone) {
t.cancel();
someCallback();
}
});
// set isDone to true sometimes in the future
new Future.delayed(new Duration(seconds: 10), () => isDone = true);
}
bool isDone = false;
void someCallback() {
print('isDone: $isDone');
// continue processing
}
You can of course pass the callback as parameter instead of hardcode it, because functions are first class members in Dart.
Polling doesn't work very well for async. It is better to wait for a signal from the thing that must complete.
Günter Zöchbauer's answer shows you how to poll anyway, by sampling with a timer.
As an alternative, it would be better to not have a boolean done, but instead complete another future when you are ready. This is busy-polling, which polls again as soon as a result comes back, which may be more intensive than you need. Using timer based polling can be more efficient if you don't need the result as soon as possible.
return requestServer().then((operation) {
var completer = new Completer();
void poll(result) {
if (!result) {
operation.then(poll, onError: completer.completeError);
} else {
completer.complete();
}
}
poll(false);
return completer.future;
});
(Code not really tested, since I don't have your requestServer).
When you want build functions that return Futures, it is sometimes useful to use Completers. Think that requestServer() is living in the Future too, so you will have threat the result as a Future.
return requestServer().then((operation) {
// This is necessary then you want to control async
// funcions.
Completer completer = new Completer();
//
new Timer.periodic(const Duration(seconds: 10), (_) {
checkOperation(operation).then((result) {
// Only when the result is true, you pass the signal
// that the operation has finished.
// You can alse use `completer.complete(result)` if you want
// to pass data inside of the future.
if (result == true) completer.complete();
});
});
// You return the future straight away.
// It will be returned by requestServer();
return completer.future;
});
I use a function like this in a TestUtil library:
static Future<bool> waitUntilTrue(bool Function() callback,
{Duration timeout: const Duration(seconds: 2),
Duration pollInterval: const Duration(milliseconds: 50)}) {
var completer = new Completer<bool>();
var started = DateTime.now();
poll() {
var now = DateTime.now();
if (now.difference(started) >= timeout) {
completer.completeError(Exception('timed out in waitUntilTrue'));
return;
}
if (callback()) {
completer.complete(true);
} else {
new Timer(Duration(milliseconds: 100), () {
poll();
});
}
}
poll();
return completer.future;
}
And then in my test code I'll do something like:
await TestUtil.waitUntilTrue(() => someObj.isDone);
Edit:
Note that if you're using this in a testWidgets test, you have to do a little extra, since it relies on real async work happening:
await tester.runAsync<bool>(
() => TestUtil.waitUntilTrue(() => myObj.isLoaded),
additionalTime: Duration(seconds: 5));

Resources