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.
Related
I have a function that might take a few seconds to execute, and it is synchronous. Does:
String slowFunction() { ... }
...
Future<String>(() => slowFunction());
change it to asynchronous?
If I need its result in the next step, does this code make sense?
Future<void> anotherFunction() async {
// other async calls with Futures and await
...
final result = await Future<String>(() => slowFunction());
print(result);
...
// do something else with result
}
Seems kind of strange to create a Future only to immediately await on it. Should I just call the function? I guess it kind of 'yields' and allows other code to be executed before, but does such code have any use?
There is no point in making a process that is inherently synchronous and dressing it up as an asynchronous one. This is due to how asynchronicity (more generally referred to as "concurrency") works, not just in Dart but in general. Concurrency is just a programming trick to make multiple operations run interleaved with each other in the same thread, giving the illusion of true parallelism (which is where different threads or processes run simultaneously). This allows processes that would normally block while waiting for a resource to be put off until later as the program carries on with other things.
If you were to take a synchronous process that blocks due to work being actively done, either the program will block anyway as the "async" code executes just as it would otherwise do or the program will block just as long but at a later time. Either way, you are still blocking your program with a long-running process.
Take the following for example, which is what you ask: take a long-running process and wrap it in a Future, thus making it "asynchronous":
String slowFunction() { ... }
...
String result = await Future(slowFunction);
In normal concurrency, this will put slowFunction in the async queue. The next time the program has some downtime (in between UI draw calls, for example) it will pull that function out of the queue and process it. And thats when it will block for 2-3 seconds while the function executes.
In Dart, though, it works slightly differently. Because slowFunction is not an async function and doesn't await anything, Dart will attempt to run it synchronously anyway, in which case you needn't have bothered wrapping it in a Future in the first place.
You have two options here if you want to break up the operation of your synchronous function. Either you have to break it up into distinct operations that you can await between (which is itself a somewhat complicated process, isn't always possible, and is generally a good source of code smell), or you offload the function to a separate thread altogether, employing parallelism rather than mere concurrency.
Dart is single-threaded, but it can be multi-processed through the use of isolates. (An isolate is Dart's name for a child process and is as close to true multithreading that you can get in Dart.) By wrapping your function in an Isolate, you can run the work on an entirely separate process. That way, if that process blocks for 2-3 seconds, it won't affect the bulk of your app at all.
There is a catch, though. Because isolates are completely different processes, there is no sharing of memory whatsoever. That means any data that the isolate has access to has to be manually passed in through the use of "ports", namely SendPort and ReceivePort. This naturally makes isolate programming a bit of a pain, but in exchange, you won't run into things like your program having race conditions or getting deadlocked. (Because of shared memory problems, at least. Strictly speaking, there are plenty of other ways to get deadlocks and race conditions.)
Using an Isolate works like so:
// main process
void createIsolate() async {
ReceivePort isolateToMain = ReceivePort();
isolateToMain.listen((data) {
// Listen for data passed back to the main process
});
Isolate myIsolateInstance = await Isolate.spawn(myIsolate, isolateToMain.sendPort);
}
// isolate process
void myIsolate(SendPort mainToIsolate) {
final result = slowFunction();
mainToIsolate.send(result);
}
I have a function that might take a few seconds to execute, and it is synchronous. Does:
String slowFunction() { ... }
...
Future<String>(() => slowFunction());
change it to asynchronous?
Simply returning a Future will not make your function asynchronous in the way that you probably want.
When you call an asynchronous function, as much of it as possible will be executed synchronously. That is, the function will execute synchronously until it reaches the first await (i.e., until it returns a Future). Since a Dart isolate is single-threaded, if you want other work to be able to occur concurrently with your long-running operation, slowFunction would internally need to use await (which is syntactic sugar for creating Future.then() callbacks) to allow execution to yield.
Consider the following code:
Future<void> longRunningOperation1() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation1: $i');
}
}
}
Future<void> longRunningOperation2() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation2: $i');
}
}
}
Future<void> main() async {
await Future.wait([longRunningOperation1(), longRunningOperation2()]);
}
You will see that longRunningOperation1 and longRunningOperation2 never overlap; one always runs to completion before the other one starts. To allow the operations to overlap with minimal changes, you could do:
Future<void> longRunningOperation1() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation1: $i');
await Future.delayed(Duration.zero);
}
}
}
Future<void> longRunningOperation2() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation2: $i');
await Future.delayed(Duration.zero);
}
}
}
I am using a wrapper to spawn slow operations into a separate Isolate and return aFuture. It also allows to pass the function to run and some arguments as well.
import 'dart:async';
import 'dart:isolate';
/// Example
///
/// ```
/// main() async {
/// String str;
/// str = await runAsync<String, String Function(String)>(sing, ["lalalala"]);
/// print(str);
///
/// str = await runAsync<String, Function>(song);
/// print(str);
/// }
///
/// String sing(String str) => "Singing: " + str;
/// String song() => "lololololo";
/// ```
Future<R> runAsync<R, F>(F func, [List<dynamic> parameters]) async {
final receivePort = ReceivePort();
await Isolate.spawn(asyncRunner, receivePort.sendPort);
// The 'asyncRunner' isolate sends it's SendPort as the first message
final sendPort = await receivePort.first;
final responsePort = ReceivePort();
sendPort.send([responsePort.sendPort, func, parameters ?? []]);
final res = await responsePort.first;
if (res is! R)
return Future.error(res);
else if (res == null) return null;
return res as R;
}
// Isolate entry point
void asyncRunner(SendPort sendPort) async {
// Open the ReceivePort for incoming messages
final port = ReceivePort();
// Notify our creator the port we listen to
sendPort.send(port.sendPort);
final msg = await port.first;
// Execute
final SendPort replyTo = msg[0];
final Function myFunc = msg[1];
final List<dynamic> parameters = msg[2] ?? [];
try {
switch (parameters.length) {
case 0:
replyTo.send(myFunc());
break;
case 1:
replyTo.send(myFunc(parameters[0]));
break;
case 2:
replyTo.send(myFunc(parameters[0], parameters[1]));
break;
case 3:
replyTo.send(myFunc(parameters[0], parameters[1], parameters[2]));
break;
case 4:
replyTo.send(
myFunc(parameters[0], parameters[1], parameters[2], parameters[3]));
break;
case 5:
replyTo.send(myFunc(parameters[0], parameters[1], parameters[2],
parameters[3], parameters[4]));
break;
default:
replyTo.send(Exception("Unsupported argument length"));
}
} catch (err) {
replyTo.send(Exception(err.toString()));
}
// Done
port.close();
Isolate.current.kill();
}
https://github.com/vocdoni/dvote-dart/blob/main/lib/util/asyncify.dart#L16
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 have used Task.WaitAll inside an async method in my HomeController, but it is not waiting to complete execution of the async methods. What did I do wrong here? Instead of waiting, it immediately goes to the next statement.
HomeController.cs:
private List<Task> taskList = new List<Task>();
public ActionResult Index()
{
for (i=0; i<5; i++)
{
SendMessages();
}
Task.WaitAll(taskList.ToArray());
// call demo method
}
private async Task SendMessages()
{
var task = Task.Factory.StartNew(() => ProcessMessages()
taskList.Add(task);
}
private async Task ProcessMessages()
{
while (run for 10 minutes)
{
// data save
}
}
I have added a Task into taskList and used Task.WaitAll(taskList.ToArray()); to wait for all tasks to complete, then call demo method. But instead of waiting and executing the whole loop, it immediately goes down after Task.WaitAll(taskList.ToArray()); and executes call demo method. Why is this?
Task.Factory.StartNew should not be used to launch asynchronous operations, since it does not unwrap the returned Task, and instead returns a new Task that completes as soon as the asynchronous operation launches (or, more precisely, completes its synchronous part). You should use Task.Run instead.
Additionally, SendMessages should not be async. However, Index should be, allowing you to use the asynchronous WhenAll instead of the blocking WaitAll.
private List<Task> taskList = new List<Task>();
public async Task<ActionResult> Index()
{
for (i = 0; i < 5; i++)
{
SendMessages();
}
await Task.WhenAll(taskList);
// call demo method
}
private void SendMessages()
{
var task = Task.Run(() => ProcessMessagesAsync()); // here
taskList.Add(task);
}
private async Task ProcessMessagesAsync()
{
while (run for 10 minutes)
{
// data save
}
}
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), () {});
}
}