I'm porting some JavaScript to Dart. I have code that uses window.setTimeout to run a callback after a period of time. In some situations, that callback gets canceled via window.clearTimeout.
What is the equivalent of this in Dart? I can use new Future.delayed to replace setTimeout, but I can't see a way to cancel this. Nor can I find away to call clearTimeout from Dart.
You can use the Timer class
import 'dart:async';
var timer = Timer(Duration(seconds: 1), () => print('done'));
timer.cancel();
If you want to mimic the JavaScript API:
import 'dart:async';
Timer setTimeout(callback, [int duration = 1000]) {
return Timer(Duration(milliseconds: duration), callback);
}
void clearTimeout(Timer t) {
t.cancel();
}
Related
I'm having trouble figuring out a way to do the equivalent of the following Javascript code in Dart:
async function main(){
await new Promise((resolve) => {
setTimeout(resolve, 200);
});
// stuff after 200ms happens here
}
It's nice to be able to create a Promise and resolve it on the fly... can you also do this in dart? I've read about completers, but the examples use "regular" functions, rather than lambda/higher-order ones.
I am not entirely sure what you want but you can write your own example as this in Dart:
import 'dart:async';
Future<void> main() async {
print(DateTime.now().millisecond); // 417
await Future<void>.delayed(const Duration(milliseconds: 200));
print(DateTime.now().millisecond); // 625
}
Maybe a more directly conversion would be:
import 'dart:async';
Future<void> main() async {
print(DateTime.now().millisecond); // 417
await () async {
await Future<void>.delayed(const Duration(milliseconds: 200));
}();
print(DateTime.now().millisecond); // 625
}
The given ECMAScript snippet has an error, setTimeout is supposed to be called on a function, not undefined.
Also, it doesn't really need to be a lambda at all.
function delay_200(resolve) {
setTimeout(resolve, 200);
};
// statically allocated delay function
async function main() {
await new Promise(delay_200);
// stuff after 200ms happens here
}
Now, I haven't used Dart in quite some time, but I think it would be equivalent to the following:
Future<void> main() async {
await Future.delayed(
Duration(milliseconds: 200)
);
// stuff after 200ms happens here
}
Now, I'm honestly not sure if you can construct a Future like you can a Promise, so I didn't really answer the primary question.
I'm searching for the source of async/await implementations.
I would like to know how do they truly works in order to hack into future.then() to detect if there is code awaiting for execution or not.
Edit
This is what I'm willing to accomplish:
TrackingCompleter completer = new TrackingCompleter();
TrackingFuture future = completer.future;
print("isAwaited: ${future.isAwaited} (F)");
new Future.delayed(new Duration(milliseconds: 500), () {
future.then((_) {
print("executing sorping");
print("isThenExecuted: ${future.thenExecuted} (F)");
new Future.delayed(new Duration(milliseconds: 500), () {
print("isThenExecuted: ${future.thenExecuted} (T)");
exit(1);
});
});
print("isAwaited: ${future.isAwaited} (T)");
print("isThenExecuted: ${future.thenExecuted} (F)");
completer.complete();
});
As far, that's working. What I'd like to do now is to detect if future.then is called manually in the code or automatically with an await statement.
The async/await implementation is based on futures.
Basically, await creates a function that contains the rest of the current function (the "continuation" of the await expression), and then calls then on the future you await with that function as argument.
There are more details needed to handle errors, but that's basically it.
In your case, if you want to know if future.then is called, I recommend just wrapping that particular future. Example:
import "package:async/async.dart";
class ThenWrapper<T> extends DelegatingFuture<T> {
void Function(S Function(T), S Function(Object, StackTrace)) _callback;
ThenWrapper(Future<T> future, this._callback): super(future);
Future<S> then<S>(S onValue(T), {S onError(error, StackTrace st)}) {
_callback(onValue, onError);
return super.super(onValue, onError);
}
}
...
TrackingFuture future = new ThenWrapper(completer.future, ...);
You can change the callback to do whatever you want.
I am stuck trying to do something simple. I want to be able to count upwards until I click on the screen at which point i want the counting to stop. In reality the code itself will be carrying out complex AI calculations for a game, but first I want to understand how to control the while loop. In android this would be trivial.
Here is what my code looks like
bool OK = true;
main() async{
html.querySelector('#content').onMouseUp.listen((e){
OK = false;
});
await for(int i in naturals){
print(i);
await sleep();
}
}
Stream get naturals async* {
int k = 0; while (OK) { yield await k++; }
}
Future sleep() {
return new Future.delayed(const Duration(milliseconds: 1), () => "1");
}
I put the sleep() method in as a way to ensure that control is passed to the event loop.
Is it possible to control a while loop without the sleep() method?
update
Just enqueue to allow the event queue to process outstanding events without additional delay use
Future sleep() {
return new Future.delayed(Duration.ZERO);
}
original
JavaScript and therefore can't Dart have threads in the browser and you need to pass control back to the event queue to allow it to process other events like you do with sleep().
Another approach would be to use webworkers and run the heavy calculation in a background task (webworker) to keep the UI thread free to process user events. This way even additional CPU cores can be utilized which isn't possible when all code runs in the browsers UI thread.
I don't know how to create webworkers in Dart though except with Angular2 Dart Web workers in Angular 2 Dart where Angular does the initialization.
I don't think it's too difficult but I don't know any docs.
To provide a more general answer - instead of a loop, you want to schedule a sequence of future tasks that each executes one iteration or step of your AI code (or whatever background process you want to have running).
You can have the step task recursively schedule itself:
dynamic doSomething(_) {
print('Doing something ...');
if(!stop) {
new Future.delayed(delay, (){}).then(doSomething);
}
return null;
}
main() async {
doSomething(null);
}
Although I don't recommend doing this. It's awkward to control - the step code has to check a flag variable to see if it should continue or stop and it's free running.
Alternatively you could use a Timer:
void doSomething(Timer timer) {
print('Doing something ...');
}
main() async {
new Timer.periodic(delay, doSomething);
}
This is throttled at a constant rate and has a uniform time step, and is easier to stop (call cancel() on the timer).
Another approach might be to synchronize with the browser's draw refresh cycle by requesting future animation frames:
import 'dart:html';
doSomething(num delta) {
print('Doing something ...');
window.animationFrame.then(doSomething);
}
void main() {
window.animationFrame.then(doSomething);
}
Time steps are not constant but you get the time delta. The advantage of this approach is that animation frame futures will not be scheduled if the browser window is hidden.
See How do I drive an animation loop at 60fps with Dart?
Those are very simple examples. Setting up proper background processes for physics simulation and AI in web games is actually surprisingly (to me at least) non-trivial. Here are two resources I found helpful.
http://gameprogrammingpatterns.com/ - a nice free online book of game programming patterns. http://gameprogrammingpatterns.com/game-loop.html - chapter on game loops.
http://gafferongames.com/game-physics/fix-your-timestep/ - part of a sequence of articles on physics simulation in games.
Following all suggestions this is the code I have ended up with, putting the heavy lifting in a worker, with controllable stopping and starting. I have used simple counting to get this working, but this is being replaced with my complex AI game calculations.
This uses time slices of 100ms within a worker isolate, which can only be interrupted after it has finished the batch. This allows me complete freedom to have complex animations on the screen while all this hard work is being done.
import 'dart:html' as html;
import 'dart:isolate';
import 'dart:math';
SendPort worker;
bool working = false;
main() async{
await startWorker();
html.querySelector('#stage').onMouseUp.listen((e){
if(working)worker.send('stop');
else worker.send('go');
});
}
startWorker() async{
var response = new ReceivePort();
await Isolate.spawnUri(Uri.parse("worker.dart"), null ,response.sendPort)
.then((_) => response.listen((msg){
if(msg is SendPort) {
worker = msg;
}
else {
messageReceived(msg);
}
}));
}
messageReceived(String message){
switch (message){
case 'working': working = true;
break;
case 'idle': working = false;
break;
default : print(message);
break;
}
}
worker.dart
import 'dart:isolate';
import 'dart:async';
SendPort replyTo;
bool OK = false;
const timeSlice = 100;
main(List<String> args, SendPort reply) async{
var response = new ReceivePort();
reply.send(response.sendPort);
replyTo = reply;
response.listen((msg) => messageReceived(msg));
replyTo.send("Hello from worker. Click to start and stop me");
}
messageReceived(String message){
switch(message){
case 'stop':
replyTo.send('idle');
OK = false;
break;
case 'go':
replyTo.send('working');
go();
break;
}
}
go()async {
OK = true;
int i = 0;
batchJob(){
int startTime = new DateTime.now().millisecondsSinceEpoch;
int elapsed = 0;
while(elapsed < timeSlice){
i ++; // the central routine
elapsed = new DateTime.now().millisecondsSinceEpoch - startTime;
}
}
while(OK){
batchJob();
replyTo.send(i.toString());
await new Future.delayed(const Duration(milliseconds: 0), () {});
}
}
callbacks or asynchronous methods or other options
A solution to the callback plague is "await" and "async" or more specifacally 'dart:async' library.
Now, what is the cost of asynchrony?
When should we not use them?
What are the other alternatives?
The below is a badly coded non-polymer custom element that acts like a messageBox in desktop environment. It gives me less braces and parenthesis-es but requires the caller to be also async or use "show().then((v){print(v);});" pattern. Should I avoid the pattern like this?
Is callback better? Or there is an even smarter way?
Polling version
import 'dart:html';
import 'dart:async';
void init(){
document.registerElement('list-modal',ListModal);
}
class ListModal extends HtmlElement{
ListModal.created():super.created();
String _modal_returns="";
void set modal_returns(String v){
///use the modal_returns setter to
///implement a custom behaviour for
///the return value of the show method
///within the callback you can pass on calling append .
_modal_returns=v;
}
factory ListModal(){
var e = new Element.tag('list-modal');
e.style..backgroundColor="olive"
..position="absolute"
..margin="auto"
..top="50%"
..verticalAlign="middle";
var close_b = new DivElement();
close_b.text = "X";
close_b.style..right="0"
..top="0"
..margin="0"
..verticalAlign="none"
..backgroundColor="blue"
..position="absolute";
close_b.onClick.listen((_){
e.hide();
});
e.append(close_b,(_)=>e.hide());
e.hide();
return e;
}
#override
ListModal append(
HtmlElement e,
[Function clickHandler=null]
){
super.append(e);
if(clickHandler!=null) {
e.onClick.listen(clickHandler);
}else{
e.onClick.listen((_){
this.hide();
_modal_returns = e.text;
});
}
return this;
}
Future<String> show() async{
_modal_returns = '';
this.hidden=false;
await wait_for_input();
print(_modal_returns);
return _modal_returns;
}
wait_for_input() async{
while(_modal_returns=="" && !this.hidden){
await delay();
}
}
void hide(){
this.hidden=true;
}
Future delay() async{
return new Future.delayed(
new Duration(milliseconds: 100));
}
}
Non-polling version
In response to Günter Zöchbauer's wisdom(avoid polling), posting a version that uses a completer. Thanks you as always Günter Zöchbauer:
import 'dart:html';
import 'dart:async';
void init(){
document.registerElement('list-modal',ListModal);
}
class ListModal extends HtmlElement{
ListModal.created():super.created();
String _modal_returns="";
Completer _completer;
void set modal_returns(String v){
///use the modal_returns setter to
///implement a custom behaviour for
///the return value of the show method.
///Use this setter within the callback for
///append. Always call hide() after
///setting modal_returns.
_modal_returns=v;
}
factory ListModal(){
var e = new Element.tag('list-modal');
e.style..backgroundColor="olive"
..position="absolute"
..margin="auto"
..top="50%"
..verticalAlign="middle";
var close_b = new DivElement();
close_b.text = "X";
close_b.style..right="0"
..top="0"
..margin="0"
..verticalAlign="none"
..backgroundColor="blue"
..position="absolute";
close_b.onClick.listen((_){
e.hide();
});
e.append(close_b,(_){e.hide();});
e.hide();
return e;
}
#override
ListModal append(
HtmlElement e,
[Function clickHandler=null]
){
super.append(e);
if(clickHandler!=null) {
e.onClick.listen(clickHandler);
}else{
e.onClick.listen((_){
_modal_returns = e.text;
this.hide();
});
}
return this;
}
Future<String> show() async{
_modal_returns = '';
_completer = new Completer();
this.hidden=false;
return _completer.future;
}
void hide(){
hidden=true;
_completer?.complete(_modal_returns);
_completer=null;
}
}
Usually there is no question whether async should be used or not. Usually one would try to avoid it. As soon as you call an async API your code goes async without a possibility to choose if you want that or not.
There are situations where async execution is intentionally made async. For example to split up large computation in smaller chunks to not starve the event queue from being processed.
On the server side there are several API functions that allow to choose between sync and async versions. There was an extensive discussion about when to use which. I'll look it up and add the link.
The disadvantages of using async / await instead of .then() should be minimal.
minimal Dart SDK version with async / await support is 1.9.1
the VM needs to do some additional rewriting before the code is executed the first time, but this is usually neglectable.
Your code seems to do polling.
wait_for_input() async {
while(_modal_returns=="" && !this.hidden){
await delay();
}
}
This should be avoided if possible.
It would be better to let the modal manage its hidden state itself (by adding a hide() method for example), then it doesn't have to poll whether it was hidden from the outside.
This is a very simplified version of the problem I have encountered when trying to unit tests streams.
The test checks that the correct event has been added to the stream - it appears to work fine - for example, change the value add( 'test') to add( 'test2') will fail the test.
But when you comment out the line fireKeepAliveMessage(message); so that the event does not throw, the unit test will simply run forever.
How can I add some sort of timeout to the test? Or is there a better approach to this problem?
library stream_test;
import "package:unittest/unittest.dart";
import "dart:async";
void main() {
test("aa", () {
StreamController streamController = new StreamController();
streamController.add( "test");
Stream underTest = streamController.stream;
underTest.first.then(expectAsync((e){
expect( e, equals( "test"));
}));
});
}
I would do it like:
library stream_test;
import "package:unittest/unittest.dart";
import "dart:async";
void main() {
test("aa", () {
StreamController streamController = new StreamController();
Timer t;
Stream underTest = streamController.stream;
underTest.first.then(expectAsync((e) {
expect(e, equals("test"));
if (t != null) {
t.cancel();
}
}));
t = new Timer(new Duration(seconds: 3), () {
fail('event not fired in time');
});
streamController.add("test");
});
}
Stream class has method for this.
Stream timeout(Duration timeLimit, {Function void onTimeout(EventSinksink)})
Creates a new stream with the same events as this stream.
Whenever more than timeLimit passes between two events from this
stream, the onTimeout function is called.
The countdown doesn't start until the returned stream is listened to.
The countdown is reset every time an event is forwarded from this
stream, or when the stream is paused and resumed.
The onTimeout function is called with one argument: an
dart-async.EventSink that allows putting events into the returned
stream. This EventSink is only valid during the call to onTimeout.
If onTimeout is omitted, a timeout will just put a
dart-async.TimeoutException into the error channel of the returned
stream.
The returned stream is not a broadcast stream, even if this stream is.