I'm experimenting some WebGL in Dart and I had created a class that loads shaders from separate files and I would like to throw an event (function) when the object is ready, so I can continue my application knowing that my shaders are properly loaded. Do someone knows an easy way to do this?
One approach is to use a Future pattern to accomplish this:
Future<SomeType> initMyObject(){
final c = new Completer();
// Do my object init stuff
// and when it is complete:
c.complete(instanceOfSomeType);
// Return the Future object to any subscribers.
return c.future;
}
Then elsewhere you can get notified like so:
initMyObject().then((SomeType t){
//executes when future completes
});
Related
I've always considered async/await more elegant/sexy over the Futures API, but now I'm faced with a situation where the Future API implementation is very short and concise and the async/await alternative seems verbose and ugly.
I marked my two question #1 and #2 in the comments:
class ItemsRepository
{
Future<dynamic> item_int2string;
ItemsRepository() {
// #1
item_int2string =
rootBundle.loadString('assets/data/item_int2string.json').then(jsonDecode);
}
Future<String> getItem(String id) async {
// #2
return await item_int2string[id];
}
}
#1: How do I use async/await here instead of Future.then()? What's the most elegant solution?
#2: Is this efficient if the method is called a lot? How much overhead does await add? Should I make the resolved future an instance variable, aka
completedFuture ??= await item_int2string;
return completedFuture[id];
1: How do I use async/await here instead of Future.then()? What's the most elegant solution?
async methods are contagious. That means your ItemsRepository method has to be async in order to use await inside. This also means you have to call it asynchronously from other places. See example:
Future<dynamic> ItemsRepository() async {
// #1
myString = await rootBundle.loadString('assets/data/item_int2string.json');
// do something with my string here, which is not in a Future anymore...
}
Note that using .then is absolutely the same as await in a async function. It is just syntactic sugar. Note that you would use .then differently than in your example though:
ItemsRepository() {
// #1
rootBundle.loadString('assets/data/item_int2string.json').then((String myString) {
// do something with myString here, which is not in a Future anymore...
});
}
And for #2 don't worry about a performance impact of async code. The code will be executed at the same speed as synchronous code, just later whenever the callback happens. The only reason async exists is for having an easy way of allowing code to continue running while the system waits for the return of the asynchronously called portion. For example not block the UI while waiting for the disk to load a file.
I recommend you read the basic docs about async in Dart.
then and await are different. await will stop the program there until the Future task is finished. However then will not block the program. The block within then will be executed when the Future task is finished afterwards.
If you want your program to wait for the Future task, then use await. If you want your program to continue running and the Future task do it things "in the background", then use then.
I tried to show a progress in angulardart, and thought that a Future would be good for this. But then i realized that a Future must be recursive to show a progress, since the Future returns immediately and the lengthy operation is executed afterwards.
If i create a Future that calls itself until the end condition is met it works with the progressbar. But i think this could not be a very good practice sind these calls will raise the memory on the stack with every recursion. Just consider a loop going through 1 billion datasets that could run a few hours and every loop calls a new Future within the current Future.
Is there a better way to create a loop that needs a certain amount of time to do work on every element (including calling a website that must be done asynchronous and evaluating the return value)? During the loop the user should see a progress that shows him "x/1000000 done".
I think it must be done with a Future since the UI needs to reload after initiating the loop, but a recursive Future seems like a bad idea to me.
You need the future to return back to you right away on the web because it is a single threaded platform. If an async action didn't return until it was complete then you would hang the browser and it wouldn't be a great experience to the user.
Instead you have a couple of options:
Dart has the ability to make the future look like it is synchronous with the await keyword. So you can do something like:
void performAction() async {
showProgress = true;
await expensiveRpc();
showProgress = false;
}
This would require the progress to be intermediate, as you aren't actually updating the progress bar as it goes along. That said if you don't really get progress events from your RPC this is probably the better solution.
Now if your RPC or action gives you some kind of feedback as it goes you can do something a bit nicer with a stream.
void performAction() {
showProgress = true;
expensiveRpc().listen((progress) {
if (progress.done) {
showProgress = false;
} else {
percentComplete = progress.value;
});
}
Really it depends more on the RPC or service you are interacting with on how you can update the progress nicely more than the progress itself.
Meanwhile i recognized that a Future-method returns immediately without executing anything in the method-body. So the solution is pretty easy:
Just declare the rpc with a Future, do whatever you need to do in the method and when calling it, use then(...) to do what you need to do after collecting the data.
int progress = 0;
int progressMax = 100;
bool progressCanceled = false;
Future rpc(var data)
async{
for(progress=0; progress<progressMax, progress++)
{
// do whatever you need to do with data
if(progressCanceled)
return;
}
}
rpc(data).then(
{
if(progressCanceled)
return;
// do whatever is needed after having received that data
});
rpc is executed and the calling process can continue while rpc does what rpc has to do. The main program can handle button clicks to set progressCanceled to true and the rpc-method will ask for the state and stop processing if it is set.
What's the benefit of having setState() accept a function just to immediately call it and then request rebuild? In particular, what's the advantage over having users explicitly call a "rebuild" type function?
When Flutter had a "markNeedsBuild" function, developers ended up just sort of calling it at random times. When the syntax switched to setState(() { ... }), developers were much more likely to use the API correctly. They are functionally equivalent from the machine's point of view, but they seem to evoke different code from developers.
If you follow the convention of only mutating member variables inside a setState closure, you'll avoid a situation you're refactoring some code and accidentally remove the call to setState, or call setState unnecessarily. And if your State is unmounted, Flutter can fail an assertion so you know something is wrong as soon as you begin trying to mutate members, instead of at the end.
Eventually there will probably be an analyzer warning enforcing that setState is always called when mutating members of a State, so any member variable mutation that happens outside of initState or a setState callback will be flagged as suspect.
If you're just getting started with state in Flutter, check out the Flutter widgets tour. I've found that a lot of cases where I was calling setState can be handled more elegantly with FutureBuilder, StreamBuilder, AnimatedWidget, or AnimatedBuilder, so don't forget to consider those alternatives if you find yourself calling setState a lot.
Adam Barth and Yaroslav Volovich contributed to this question/answer.
To complete Colin's answer, it also ensures that you call setState at the right moment when dealing with asynchronous function.
Mutating your state outside of the callback can lead to an easy mistake:
function() async {
setState(() {});
myState = await future;
}
This causes a problem because if your future doesn't finish synchronously, the build method will be called before the state is mutated.
By using the callback you are forced to do the following:
function() async {
final value = await future;
setState(() {
myState = value;
});
}
This time, it doesn't cause problems because the future is awaited before the setState.
Can't I make an async callback and still have the issue?
No. Because setState method internally check that the callback does not return a future. And if it does, it will throw.
So the following is impossible:
setState(() async {
myState = await future;
});
Universal App with MVVMLight.
So I started wondering why all the SDK examples were done from code behind rather than using a solid Wrapper class.
So I wanted to write a reusable wrapper class. No luck. Even tried adding that wrapper to a ViewModel, still no luck.
Works fine from MainView.xaml.cs
IBandInfo[] pairedBands = BandClientManager.Instance.GetBandsAsync().Result;
if (pairedBands.Length > 0)
{
using (IBandClient bandClient = await BandClientManager.Instance.ConnectAsync(pairedBands[0]))
{
}
}
The moment I move to any kind of OOP or View Model, ConnectAsync will never return or throw exception. I have tried this 20 different ways, is the SDK broken? What Is happening? No message, no throw, just never returns.
If I throw in Code behind, wallah it works just fine and returns the client in 1/2 second.
I have spend 5-6 hours so far on this. I wanted to create a solid wrapper class for the SDK so I could call easy calls from Model and do things like StartListener(MicrosoftBandSensor sensorToActivate).
Any suggestions?
-- For Phil's comment
I was trying to create backing variables for both client and bandinfo which would be held in a class that the VM uses. I wrote my class as IDisposable so I could dispose of both when I was done with my wrapper. I may be using this wrong to be honest.
MicrosoftBand.MicrosoftBandClient = BandClientManager.Instance.ConnectAsync(pairedBands[0]).Result;
Is what I wanted to call making it a sync call since I wanted to make the calls to bandinfo and client in the constructor then hold both until the class was destroyed and just recall the vars when needed.
My VM has :
public BandInformation MicrosoftBand
{
get { return _microsoftBand; }
set { Set(() => MicrosoftBand, ref _microsoftBand, value); }
}
If they didn't pass the bandclient in the constructor I would use:
private async Task InitBand(IBandInfo bandInfo)
{
if (bandInfo == null)
{
var allBands = await BandClientManager.Instance.GetBandsAsync();
if (allBands.Length > 0)
{
bandInfo = allBands[0];
}
}
var bandClient = await BandClientManager.Instance.ConnectAsync(bandInfo);
MicrosoftBandInfo = bandInfo;
MicrosoftBandClient = bandClient;
if (MicrosoftBandClient == null)
{
AddErrorMessage("This sample app requires a Microsoft Band paired to your device.Also make sure that you have the latest firmware installed on your Band, as provided by the latest Microsoft Health app.");
}
}
This seems fine working with BandInfo. I get back a solid seeming to work object For the client I get "thread exited" and nothing else.
Note: I had it in a try catch throwaway version at one point and nothing threw n exception either.
I assume you can do this like you would any other IDisposable where you handle the disposing yourself.
I can reinstantiate the BandClient each time, just figured I needed to detach the events at some point, meaning I had to keep ahold of the bandclient. I could keep it until done and would add and remove events as I needed each time.
It's likely your blocking call to .Result within your VM constructor is what was causing the hang. IBandClientManager.ConnectAsync() may implicitly display UI (a Windows Runtime dialog asking the user to confirm that she wants to use that specific Bluetooth device). If you've blocked the UI thread when it attempts to display UI, you've now gotten yourself into a deadlock.
Calling Task.Result is almost never a good idea, much less doing so within a constructor where you have little idea on which thread the constructor is executing. If you're working with an async API (such as the Band SDK) then your best bet is to keep that interaction async as well. Instead, defer calling ConnectAsync() until you actually need to, and do so from an async method in your VM. (Deferring the connection is a good idea anyway because you want to minimize the time connected to the Band to preserve battery life.) Then call Dispose() as early as possible to close the Bluetooth connection.
So I went and looked at a bunch of examples. Finally I landed on the GravityHeroUAP demo on the MSDN site. https://msdn.microsoft.com/en-us/magazine/mt573717.aspx?f=255&MSPPError=-2147217396
I looked at his code and the source: https://github.com/kevinash/GravityHeroUWP
He was essentially doing what I wanted to do.
However, I noticed something Bizarre. In his viewmodel everything was static!
public static IBandInfo SelectedBand
{
get { return BandModel._selectedBand; }
set { BandModel._selectedBand = value; }
}
private static IBandClient _bandClient;
public static IBandClient BandClient
{
get { return _bandClient; }
set
{
_bandClient = value;
}
}
I ended up copying this pattern (though had to throw away my favorite MVVM lib in the process, though I am sure I can get it back).
My common pattern in my VM's:
public string ExceptionOnStart {
get { return _exceptionOnStart; }
set { Set(() => ExceptionOnStart, ref _exceptionOnStart, value); }
}
It seems to be working now!
That and I got data way too fast for the
await Windows.Storage.FileIO.AppendLinesAsync(dataFile, new List<string> { toWrite });
Thank you for the help Phil, it got me looking in the right direction!
Thank you very, very much. Spent WAY to long on this. Mark
I would like to launch a fairly expensive operation in response to a user clicking on a canvas element.
mouseDown(MouseEvent e) {
print("entering event handler");
var future = new Future<int>(expensiveFunction);
future.then((int value) => redrawCanvas(value);
print("done event handler");
}
expensiveFunction() {
for(int i = 0; i < 1000000000; i++){
//do something insane here
}
}
redrawCanvas(int value) {
//do stuff here
print("redrawing canvas");
}
My understanding of M4 Dart, is that this future constructor should launch "expensiveFunction" asynchronously, aka on a different thread from the main one. And it does appear this way, as "done event handler" is immediately printed into my output window in the IDE, and then some time later "redrawing canvas" is printed. However, if I click on the element again nothing happens until my "expensiveFunction" is done running from the previous click.
How do I use futures to simply launch an compute intensive function on new thread such that I can have multiple of them queued up in response to multiple clicks, even if the first future is not complete yet?
Thanks.
As mentioned in a different answer, Futures are just a "placeholder for a value that is made available in the future". They don't necessarily imply concurrency.
Dart has a concept of isolates for concurrency. You can spawn an isolate to run some code in a parallel thread or process.
dart2js can compile isolates into Web Workers. A Web Worker can run in a separate thread.
Try something like this:
import 'dart:isolate';
expensiveOperation(SendPort replyTo) {
var result = doExpensiveThing(msg);
replyTo.send(result);
}
main() async {
var receive = new ReceivePort();
var isolate = await Isolate.spawn(expensiveOperation, receive.sendPort);
var result = await receive.first;
print(result);
}
(I haven't tested the above, but something like it should work.)
Event Loop & Event Queue
You should note that Futures are not threads. They do not run concurrently, and in fact, Dart is single-threaded. All Dart code runs in an event loop.
The event loop is a loop that runs as long as the current Dart isolate is alive. When you call main() to start a Dart application, the isolate is created, and it is no longer alive after the main method is completed and all items on the event queue are completed as well.
The event queue is the set of all functions that still need to finish executing. Because Dart is single threaded, all of these functions need to run one at a time. So when one item in the event queue is completed, another one begins. The exact timing and scheduling of the event queue is something that's way more complicated than I can explain myself.
Therefore, asynchronous processing is important to prevent the single thread from being blocked by some long running execution. In a UI, a long process can cause visual jankiness and hinder your app.
Futures
Futures represent a value that will be available sometime in the Future, hence the name. When a Future is created, it is returned immediately, and execution continues.
The callback associated with that Future (in your case, expensiveFunction) is "started" by being added to the event queue. When you return from the current isolate, the callback runs and as soon as it can, the code after then.
Streams
Because your Futures are by definition asynchronous, and you don't know when they return, you want to queue up your callbacks so that they remain in order.
A Stream is an object that emits events that can be subscribed to. When you write canvasElement.onClick.listen(...) you are asking for the onClick Stream of MouseEvents, which you then subscribe to with listen.
You can use Streams to queue up events and register a callback on those events to run the code you'd like.
What to Write
main() {
// Used to add events to a stream.
var controller = new StreamController<Future>();
// Pause when we get an event so that we take one value at a time.
var subscription = controller.stream.listen(
(_) => subscription.pause());
var canvas = new CanvasElement();
canvas.onClick.listen((MouseEvent e) {
print("entering event handler");
var future = new Future<int>(expensiveFunction);
// Resume subscription after our callback is called.
controller.add(future.then(redrawCanvas).then(subscription.resume()));
print("done event handler");
});
}
expensiveFunction() {
for(int i = 0; i < 1000000000; i++){
//do something insane here
}
}
redrawCanvas(int value) {
//do stuff here
print("redrawing canvas");
}
Here we are queuing up our redrawCanvas callbacks by pausing after each mouse click, and then resuming after redrawCanvas has been called.
More Information
See also this great answer to a similar question.
A great place to start reading about Dart's asynchrony is the first part of this article about the dart:io library and this article about the dart:async library.
For more information about Futures, see this article about Futures.
For Streams information, see this article about adding to Streams and this article about creating Streams.