inside my flutter app I want to check my api every 10 seconds. I found this post to run a function every x amount of time and did the following:
class _MainPage extends State<MainPage> {
int starter = 0;
void checkForNewSharedLists(){
// do request here
setState((){
// change state according to result of request
});
}
Widget build(BuildContext context) {
Timer.periodic(Duration(seconds: 15), (Timer t) => checkForNewSharedLists());
}
}
Unfortunately the requests pile up: after restarting the app on the first "round" there are two request to the api, the second round it's four requests, the third it's eight and so on...
Does anybody know how to fix this?
build() can and usually will be called more than once and every time a new Timer.periodic is created.
You need to move that code out of build() like
Timer? timer;
#override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 15), (Timer t) => checkForNewSharedLists());
}
#override
void dispose() {
timer?.cancel();
super.dispose();
}
Even better would be to move out such code from widgets entirely in an API layer or similar and use a StreamBuilder to have the view updated in case of updated data.
Use Cron lib which will be run periodically, but there is a difference between Timer and Cron,
Timer: It's running a task on given specific time intervals whether it is seconds, minutes, or hours.
Cron: It's used for more complex time intervals, eg: if a task needs to be run on a specific time of an hour. let's see the diagram
The above diagram has an asterisk that represents a number that appears in a specific position.
import 'package:cron/cron.dart';
main() {
var cron = new Cron();
cron.schedule(new Schedule.parse('*/3 * * * *'), () async {
print('every three minutes');
});
cron.schedule(new Schedule.parse('8-11 * * * *'), () async {
print('between every 8 and 11 minutes');
});
}
The above examples are taken from the repository which pretty well explains that the first '*' represents minutes, similar for the hour and so on as shown in the diagram.
Another example of the hour would be Schedule.parse(* 1,2,3,4 * * *), This schedule will run every minute every day during the hours of 1 AM, 2 AM, 3 AM, and 4 AM.
for more reference
https://code.tutsplus.com/tutorials/scheduling-tasks-with-cron-jobs--net-8800
Timer works fine, but you can also use Stream to execute a function periodicly:
final Stream _myStream =
Stream.periodic(const Duration(seconds: x), (int count) {
// Do something and return something here
});
see: https://api.flutter.dev/flutter/dart-async/Stream/Stream.periodic.html
I was looking for code to run a function every n amount of seconds for x seconds in total. Also I added functionality to cancel the timer if the periodic Function returns a success before the total timer elapsed. So here is how to solve that:
bool success = false;
bool done = false;
Future.delayed(const Duration(seconds: n), () {
done = true;
print("Timeout");
});
await Stream.periodic(const Duration(seconds: x)).takeWhile((_) => !done).forEach((_) async
{
success = await FunctionYouWantToExecutePeriodicly();
done = success; // only if you want to finish the function earlier
});
Related
I'm making a Flutter app that using asynchronous a lot but it not working like how I understand about it. So I have some question about async and await in dart. Here is an example:
Future<int> someFunction() async {
int count = 0;
for (int i=0; i< 1000000000;i ++) {
count+= i;
}
print("done");
return count;
}
Future<void> test2() async {
print("begin");
var a = await someFunction();
print('end');
}
void _incrementCounter() {
print("above");
test2();
print("below");
}
test2() function will take a lot of time to done. right? So what i want is when test2 keep his work running until done, everything will keep running and not wait for test2().
When i run the function _incrementCounter(), it show the result:
above begin done below end
The problem is it didn't show "below" right away but it wait until someFunction() done.
This is result i want:
above begin below done end
This is the expected behavior since this change in Dart 2.0 which can be found in the changelog:
(Breaking) Functions marked async now run synchronously until the first await statement. Previously, they would return to the event loop once at the top of the function body before any code runs (issue 30345).
Before I give the solution I want to note you that async code are not running in another thread so the concept of:
keep his work running until done, everything will keep running and not
wait for test2()
Is fine but at some point your application are going to wait for test2() to finish since it is spawned as a task on the job queue where it will not leave other jobs to run before it is done. If you want the experience of no slowdown you want to either split the job into multiple smaller jobs or spawn an isolate (which are running in another thread) to run the calculation and later return the result.
Here is the solution go get your example to work:
Future<int> someFunction() async {
int count = 0;
for (int i=0; i< 1000000000;i ++) {
count+= i;
}
print("done");
return count;
}
Future<void> test2() async {
print("begin");
var a = await Future.microtask(someFunction);
print('end');
}
void _incrementCounter() {
print("above");
test2();
print("below");
}
main() {
_incrementCounter();
}
By using the Future.microtask constructor we are scheduling the someFunction() to be running as another task. This makes it so the "await" are going to wait since it will be the first true instance of an async call.
I've spent many hours looking for the solution, but since I am Dart begginer, I couldn't find it out by myself.
What I want to achieve is to create something like queue for some of the async functions that are called randomly (let's say, when user is tapping a button in my app) from different points in code while the app is running. I want them to be executed in the order they were called, so basically I have async methods such as updateDate() and updatePoints() and when the user is tapping button X the updateDate() is going to be called (added to queue), and similar with Y and updatePoints(). When the user taps i. e. X, X, Y I want to run updateDate(), updateDate(), updatePoints() in this exact order. When one task is complete, another one is starting. I guess I can't use await to achieve that. Any hints would be appreciated!
import 'dart:async';
import 'dart:collection';
import 'dart:math';
Future<void> main() async {
_simulateRealWork();
}
Scheduler _scheduler = Scheduler();
class Scheduler {
bool _scheduled = false;
Queue<Future Function()> _queue = Queue<Future Function()>();
void schedule(Future Function() task) {
_queue.add(task);
if (!_scheduled) {
_scheduled = true;
Timer(Duration(seconds: 0), _execute);
}
}
Future _execute() async {
while (true) {
if (_queue.isEmpty) {
_scheduled = false;
return;
}
var first = _queue.removeFirst();
await first();
}
}
}
void _simulateRealWork() {
var maxPeriod = 5;
var count = 5;
for (var i = 0; i < count; i++) {
print('Timer $i');
var random = Random();
Timer(Duration(seconds: random.nextInt(maxPeriod)), () {
print('Scheduled work $i');
Future work() async {
print('Started work $i');
await Future.delayed(Duration(seconds: random.nextInt(maxPeriod)));
print('Ended work $i');
}
_scheduler.schedule(work);
});
}
}
Result:
Timer 0
Timer 1
Timer 2
Timer 3
Timer 4
Scheduled work 2
Started work 2
Scheduled work 0
Scheduled work 3
Ended work 2
Started work 0
Scheduled work 1
Scheduled work 4
Ended work 0
Started work 3
Ended work 3
Started work 1
Ended work 1
Started work 4
Ended work 4
The following code might be a bad practice when used in large queue of tasks, but if you are sure that the array of tasks won't exceed an adequate size - this might work just fine:
Future<List<T>> runOneByOne<T>(List<T Function()> list) {
if (list.isEmpty) {
return Future.value(null);
}
Future task = Future<T>.microtask(list.first);
final List<T> results = [];
for (var i = 1; i < list.length; i++) {
final func = list[i];
task = task.then((res) { results.add(res); return Future<T>.microtask(func); });
}
return task.then((res) { results.add(res); return results; });
}
It executes functions one-by-one in the original order by wrapping one Future into another. results array is used to store returned values, returning all of the values in the end.
Execution stops and throws if stumbled upon an error. Results array is lost in that case. You can add try {...} closure to every microtask wrapper to ignore errors and return null in that one particular task, preserving other values in results array.
Usage example:
runOneByOne<int>([
() { print("First"); return 1; },
() { print("Second"); return 2; },
() { print("Third"); return 3; },
]).then((results) {
print(results); // List<int> [ 1, 2, 3 ]
});
I am making an application using flutter framework .
During this I came across with the keywords in Dart async and async*.
Can anybody tell me what's the difference between them?
Short answer
async gives you a Future
async* gives you a Stream.
async
You add the async keyword to a function that does some work that might take a long time. It returns the result wrapped in a Future.
Future<int> doSomeLongTask() async {
await Future.delayed(const Duration(seconds: 1));
return 42;
}
You can get that result by awaiting the Future:
main() async {
int result = await doSomeLongTask();
print(result); // prints '42' after waiting 1 second
}
async*
You add the async* keyword to make a function that returns a bunch of future values one at a time. The results are wrapped in a Stream.
Stream<int> countForOneMinute() async* {
for (int i = 1; i <= 60; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
The technical term for this is asynchronous generator function. You use yield to return a value instead of return because you aren't leaving the function.
You can use await for to wait for each value emitted by the Stream.
main() async {
await for (int i in countForOneMinute()) {
print(i); // prints 1 to 60, one integer per second
}
}
Going on
Watch these videos to learn more, especially the one on Generators:
Isolates and Event Loops
Futures
Streams
async / await
Generators
Marking a function as async or async* allows it to use the async/await for a Future.
The difference between both is that async* will always return a Stream and offer some syntactical sugar to emit a value through the yield keyword.
We can therefore do the following:
Stream<int> foo() async* {
for (int i = 0; i < 42; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
This function emits a value every second, which increments every time.
Solution, Origins and Insights
This answer includes simplified and easy to understand examples
async
The async computation cannot provide a result immediately when it is started because the program may need to wait for an external response like:
Reading a file
Querying a database
Fetching data from an API
Instead of blocking all computation until the result is available, the asynchronous computation immediately returns a Future object which will eventually "complete" with the result.
Example (This type of async call can be used only without returning a response):
void main() async {
// The next line awaits 5 seconds
await Future.delayed(Duration(seconds: 5));
// Pseudo API call that takes some time
await fetchStocks();
}
Future
A Future represents a computation that doesn’t complete immediately. Whereas a normal function returns the result, an asynchronous function returns a Future, which will
eventually contain the result. The Future will tell you when the result is ready.
Future is appended when the async function returns a value
Represents the result of a single computation (in contrast to a Stream)
Example:
Future<String> fetchUserOrder() =>
// Imagine that this function is more complex and slow.
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);
void main(List<String> arguments) async {
var order = await fetchUserOrder();
// App awaits 2 seconds
print('Your $order is ready');
}
Stream
A source of asynchronous data events.
A Stream provides a way to receive a sequence of events. Each event is either a data event, also called an element of the stream.
Stream is a sequence of results
From stream you get notified for results (stream elements)
async* (streams)
async* is an asynchronous generator that returns a Stream object. Made to create streams.
An example of using a stream and async*:
// Creating a new stream with async*
// Each iteration, this stream yields a number
Stream<int> createNumberStream(int number) async* {
for (int i = 1; i <= number; i++) {
yield i;
}
}
void main(List<String> arguments) {
// Calling the stream generation
var stream = createNumberStream(5);
// Listening to Stream yielding each number
stream.listen((s) => print(s));
}
Result:
1
2
3
4
5
Bonus: Transforming an Existing Stream
If you already have a stream, you can transform it to a new stream based on the original stream’s events.
Example (same code as before but with a twist):
Stream<int> createNumberStream(int number) async* {
for (int i = 1; i <= number; i++) {
yield i;
}
}
// This part is taking a previous stream through itself and outputs updated values
// This code multiplies each number from the stream
Stream<int> createNumberDoubling(Stream<int> chunk) async* {
await for (final number in chunk) {
yield number*2;
}
}
void main(List<String> arguments) {
// Here we are Transforming the first stream through createNumberDoubling stream generator
var stream = createNumberDoubling(createNumberStream(5));
stream.listen((s) => print(s));
}
Result:
2
4
6
8
10
Solution
The async and async* are close relatives, they are even from the same library dart:async
The async represent a Future and a one-time exchange while the async* represents a Stream, a stream of multiple events
Async functions execute synchronously until they reach the await keyword. Therefore, all synchronous code within an async function body executes immediately.
Future<int> foo() async {
await Future.delayed(Duration(seconds: 1));
return 0;
}
Async* is used to create a function that returns a bunch of future values one at a time. Each result is wrapped in a Stream.
Stream<int> foo() async* {
for (var i = 0; i < 10; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
async* will always return a Stream
Stream<int> mainStream(int value) async* {
for (int i = 1; i <= value; i++) {
yield i;
}
}
async returns the result wrapped in the Future. So it might take longer time. See the below example:
void main() async {
// The next line awaits 10 seconds
await Future.delayed(Duration(seconds: 10));
}
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), () {});
}
}
I want to do the ill-advised and place both an onClick and onDoubleClick on the same element with each type of event resulting in a different action. Specifically on an image, click to advance to the next image, double-click to toggle fullscreen.
Naturally I get two clicks followed by a double-click (though I understand that some browsers only fire one click before the double-click).
I had thought to make it easy on myself and place each event into a buffer (List) - or rather to add the event.type string to a list, then, after a suitable elapse of time, say 250 or 300 milliseconds examine the last item in the buffer and if doubleclick then go fullscreen else advance the length of the list.
I have found that the list only ever has one item, and I have not worked out how to get the timer to work..
Amongst my attempts was this one:
void catchClickEvents(Event e) {
var eventTypes = new List<String>();
eventTypes.add(e.type);
Duration duration = const Duration(milliseconds: 300);
var timeout = new Timer(duration, () => processEvents(eventTypes));
}
void processEvents(List eTypes) {
// just to see what is going on...
print(eTypes);
}
this results in this output printed to the console:
[click]
[click]
[dblclick]
rather than
[click, click, dblclick]
If I slow it down there is a clear delay before those three event types are printed together
So...
The bigger question is
'What is the darty way to distiguish between single and double-click and perform a different action for each?'
The other questions are:
How do I fill a buffer with successive events (and later clear it down) - or even how do I use Dart's Stream of events as a buffer...
What is the real way timeout before examining the contents of the buffer?
(and I guess the final question is 'should I abandon the effort and settle for a conventional set of buttons with glyph-icons?'!)
I'm not sure if IE still has the event sequence explained here (no 2nd click event)
https://stackoverflow.com/a/5511527/217408
If yes you can use a slightly deviated variant of Roberts solution:
library app_element;
import 'dart:html' as dom;
import 'dart:async' as async;
Duration dblClickDelay = new Duration(milliseconds: 500);
async.Timer clickTimer;
void clickHandler(dom.MouseEvent e, [bool forReal = false]) {
if(clickTimer == null) {
clickTimer = new async.Timer(dblClickDelay, () {
clickHandler(e, true);
clickTimer = null;
});
} else if(forReal){
print('click');
}
}
void dblClickHandler(dom.MouseEvent e) {
if(clickTimer != null) {
clickTimer.cancel();
clickTimer = null;
}
print('doubleClick');
}
void main() {
dom.querySelector('button')
..onClick.listen(clickHandler)
..onDoubleClick.listen(dblClickHandler);
}
or just use Roberts solution with the mouseUp event instead of the click event.
The problem is that your variable is not global.
var eventTypes = new List<String>();
void catchClickEvents(Event e) {
eventTypes.add(e.type);
Duration duration = const Duration(milliseconds: 300);
var timeout = new Timer(duration, () => processEvents(eventTypes));
}
void processEvents(List eTypes) {
print(eTypes);
}
There also is e.detail that should return the number of the click. You can use that, if you don't need the Internet Explorer. The problem with your list is that it grows and never gets cleared.
Let's take into account what we know: You get click events and at somepoint you have doubleclicks.
You could use a click counter here. (Or use e.detail) to skip the second click event. So you only have click and dblclick.
Now when you get a click event, you create a new timer like you did before and run the click action. If you get the dblclick event you simply run you action. This could like this:
DivElement div = querySelector('#div');
Timer timeout = null;
div.onClick.listen((MouseEvent e) {
if(e.detail >= 2) {
e.preventDefault();
} else {
if(timeout != null) {
timeout.cancel();
}
timeout = new Timer(new Duration(milliseconds: 150), () => print('click'));
}
});
div.onDoubleClick.listen((MouseEvent e) {
if(timeout != null) {
timeout.cancel();
timeout = null;
}
print('dblclick');
});
This is the example code that works for me. If you can't rely on e.detail just us a counter and reset it after some ms after a click event.
I hope this helps you.
Regards, Robert
Your page should react on user inputs as fast as possible. If you wait to confirm double click - your onClick will become much less responsive. You can hide the problem by changing visual state of the element(for example, playing animation) after first click but it can confuse user. And it gets even worse with handheld. Also if element has to react only on onClick event, you can "cheat" and listen to onmousedown instead - it will make your UI much more responsive.
On top of all this, double click, from client to client, may have noticeably different trigger time interval and it isn't intuitive, for user, that you can double click something. You will have to bloat your interface with unnecessary hints.
edit: Added my solution. It should be fairly extensible and future proof.
import 'dart:html';
import 'dart:async';
import 'dart:math';
//enum. Kinda... https://code.google.com/p/dart/issues/detail?id=88
class UIAction {
static const NEXT = const UIAction._(0);
static const FULLSCREEN = const UIAction._(1);
static const ERROR = const UIAction._(2);
final int value;
const UIAction._(this.value);
}
/**
*[UIEventToUIAction] transforms UIEvents into corresponding UIActions.
*/
class UIEventToUIAction implements StreamTransformer<UIEvent, UIAction> {
/**
* I use "guesstimate" value for [doubleClickInterval] but you can calculate
* comfortable value for the user from his/her previous activity.
*/
final Duration doubleClickInterval = const Duration(milliseconds: 400);
final StreamController<UIAction> st = new StreamController();
Stream<UIAction> bind(Stream<UIEvent> originalStream) {
int t1 = 0,
t2 = 0;
bool isOdd = true;
Duration deltaTime;
originalStream.timeout(doubleClickInterval, onTimeout: (_) {
if ((deltaTime != null) && (deltaTime >= doubleClickInterval)) {
st.add(UIAction.NEXT);
}
}).forEach((UIEvent uiEvent) {
isOdd ? t1 = uiEvent.timeStamp : t2 = uiEvent.timeStamp;
deltaTime = new Duration(milliseconds: (t1 - t2).abs());
if (deltaTime < doubleClickInterval) st.add(UIAction.FULLSCREEN);
isOdd = !isOdd;
});
return st.stream;
}
}
void main() {
//Eagerly perform time consuming tasks to decrease the input latency.
Future NextImageLoaded;
Future LargeImageLoaded;
element.onMouseDown.forEach((_) {
NextImageLoaded = asyncActionStub(
"load next image. Returns completed future if already loaded");
LargeImageLoaded = asyncActionStub(
"load large version of active image to show in fullscreen mode."
"Returns completed future if already loaded");
});
Stream<UIEvent> userInputs = element.onClick as Stream<UIEvent>;
userInputs.transform(new UIEventToUIAction()).forEach((action) {
switch (action) {
case UIAction.FULLSCREEN:
LargeImageLoaded.then( (_) =>asyncActionStub("fullscreen mode") )
.then((_) => print("'full screen' finished"));
break;
case UIAction.NEXT:
NextImageLoaded.then( (_) =>asyncActionStub("next image") )
.then((_) => print("'next image' finished"));
break;
default:
asyncActionStub("???");
}
});
}
final DivElement element = querySelector("#element");
final Random rng = new Random();
final Set performed = new Set();
/**
*[asyncActionStub] Pretends to asynchronously do something usefull.
* Also pretends to use cache.
*/
Future asyncActionStub(String msg) {
if (performed.contains(msg)) {
return new Future.delayed(const Duration(milliseconds: 0));
}
print(msg);
return new Future.delayed(
new Duration(milliseconds: rng.nextInt(300)),
() => performed.add(msg));
}