Aye Aye good people,
I'm experiencing a weird behavior
when using the top level function of an isolate asynchronously;
you can find example code HERE, but in short
as top level function of an isolate this works:
String _syncHandle(int data) {
return 'done';
}
and this doesn't:
Future<String> _syncHandle(int data) async {
return 'done';
}
can anybody explain me why?
(or if should work, why isn't doing so in my code?)
thank you in advance
Francesco
...
[edit: just noticed that a similar question has been asked,
nevertheless it is still unanswered
Call async function from Isolate function,
plus issue open on github ]
forgot to update this :/
if you look at the code linked in the question
isolates_logging/lib/provider/test_isolate.dart
Future<void> _handle(int _m) async {
final response = ReceivePort();
isolateTest = await Isolate.spawn(_isolate, response.sendPort);
final sendPort = await response.first as SendPort;
final answer = ReceivePort();
sendPort.send([_m, answer.sendPort]);
await answer.first.then((p) {
_outbound.sink.add(p);});
}
static void _isolate(SendPort _initialReplyTo) {
final port = ReceivePort();
_initialReplyTo.send(port.sendPort);
port.listen((message) {
final data = message[0] as int;
final send = message[1] as SendPort;
send.send(_syncHandle(data));
});
}
}
Future<String> _syncHandle(int data) async {
return 'done';
}
note the send.send(_syncHandle(data)); part
if you do so, you can send only primitives and not futures,
basically that's it
Related
I am learning about Isolate's. I read docs. And want to write minimal working example. Here is my code:
main() async
{
ReceivePort receivePort = ReceivePort();
Isolate.spawn(echo, receivePort.sendPort);
var sendPort = await receivePort.first;
}
echo(SendPort sendPort)
{
ReceivePort receivePort = ReceivePort();
sendPort.send(receivePort);
}
It almost ok, but I can't understand how I can send simple "Hello" message back. I looked few examples and there was some middle-ware like sendReceive(). Im I right understand that after:
var sendPort = await receivePort.first;
sendPort will store name/address of spawned function and I need sendPort.send("hello");?
You already stated how to send simple data using SendPort.send. In fact, you are only able to send primitive data types, i.e. null, num, bool, double, String as described in the documentation.
I will complete your example in the following.
import 'dart:isolate';
main() async {
final receivePort = ReceivePort();
await Isolate.spawn(echo, receivePort.sendPort);
final Stream receivePortStream = receivePort.asBroadcastStream();
receivePortStream.listen((message) {
if (message is String) {
print('Message from listener: $message');
} else if (message is num) {
print('Computation result: $message');
}
});
final firstMessage = await receivePortStream.first;
print(firstMessage);
}
echo(SendPort sendPort) {
sendPort.send('hello');
sendPort.send('another one');
final computationResult = 27 * 939;
sendPort.send(computationResult);
}
Note that you want to simply send 'hello' and not some other ReceivePort, which will not even work as it is not a primitive value.
In my example, I also setup another listener that will process further messages.
Additionally, I needed to create the receivePortStream variable as a broadcast stream in order to be able to both listen to it and get the first message. If you try to run ReceivePort.listen and ReceivePort.first on the same ReceivePort, you will get an exception.
I'm having the following lines in a Flutter app. _devicesRef refers to some node in a Firebase Realtime Database.
_devicesRef.child(deviceId).once().then((DataSnapshot data) async {
print(data.key);
var a = await ...
print(a);
}
These lines work fine. Now I want to use await instead of .then(). But somehow, once() never returns.
var data = await _devicesRef.child(deviceId).once();
print(data.key);
var a = await ...
print (a);
So print(data.key) is never called.
What's wrong here?
It could be explained by the code following your snippet. Perhaps the future completion is trigger by something after your code and transforming your code with await will wait until a completion that never happens.
For instance, the following code works:
main() async {
final c = Completer<String>();
final future = c.future;
future.then((message) => print(message));
c.complete('hello');
}
but not this async/await version:
main() async {
final c = Completer<String>();
final future = c.future;
final message = await future;
print(message);
c.complete('hello');
}
If you intend to use await as a replacement of .then() in your snippet, this is how you can accomplish it:
() async {
var data = await _devicesRef.child(deviceId).once();
print(data.key);
var a = await ...
print(a);
}();
By placing the code in the asynchronous closure () async {}(), we are not preventing execution of the code that comes after, in a similar fashion to using .then().
it should be encased in an async function like this to use await
Furtre<T> myFunction() async {
var data = await _devicesRef.child(deviceId).once();
return data;
}
from the flutter doc:
class CounterStorage {
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/counter.txt');
}
Future<int> readCounter() async {
try {
final file = await _localFile;
// Read the file
String contents = await file.readAsString();
return int.parse(contents);
} catch (e) {
// If we encounter an error, return 0
return 0;
}
}
Future<File> writeCounter(int counter) async {
final file = await _localFile;
// Write the file
return file.writeAsString('$counter');
}
}
Both readCounter() and writeCounter() call the _localPath getter each time they're called.
My question is :
isn't this a little wasteful? Wouldn't it be better to wait for the _localFile in the constructor of CounterStorage, and store it in a class member, as opposed to getting the _localPath and _localPath each and every time?
Can someone please suggest such an implementation?
It depends what you mean by wasteful, and the contract of getApplicationDocumentsDirectory.
For example, if it is possible for getApplicationDocumentsDirectory() to return a different path the next time it is called (for example, if a new user logs in, possibly - I'm not sure of the details) then this is completely correct.
If it is guaranteed this value will never change, it is possible to optimize further, but showing optimizations is probably not the goal of sample documentation. If you're interested, two ideas I can think of are:
Create a static final field:
class CounterStorage {
// Static fields in Dart are lazy; this won't get sent until used.
static final _localPath = getApplicationDocumentsDirectory().then((p) => p.path);
// ...
}
This is my preference if CounterStorage has other methods or fields that are uesful without waiting for _localPath to be resolved. In the above example, there are none, so I would prefer:
Create a static async method to create CounterStorage
import 'package:meta/meta.dart';
class CounterStorage {
// You could even combine this with the above example, and make this a
// static final field.
static Future<CounterStorage> resolve() async {
final localPath = await getApplicationDocumentsDirectory();
return new CounterStorage(new File(this.localPath));
}
final File _file;
// In a test you might want to use a temporary directory instead.
#visibleForTesting
CounterStorage(this._file);
Future<int> readCount() async {
try {
final contents = await _file.readAsString();
return int.parse(contents);
} catch (_) {
return 0;
}
}
}
This makes the process of retrieving the File happen potentially once per app.
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.
I'm working with two functions, both of them should return a future. A third function gets called when both of them are done. Right now the future is returned too early, so that my third function is called before my second function is completed.
Function1:
static var getObjectDataCompleter = new Completer();
static var fillObjectCompleter = new Completer();
static Future getObjectData(List jsonMap) {
for (int i = 0; i < jsonMap.length; i++) {
fillObjectCompleter = new Completer();
var request = buildRequest("GET", resourceUrl);
request.send();
request.onLoadEnd.listen((event) => fillObject(request));
}
if(fillObjectCompleter.isCompleted) {
getObjectDataCompleter.complete(null);
}
return getObjectDataCompleter.future;
}
Function2:
static Future fillObject(HttpRequest request) {
String responseText = request.response;
List stringJson = JSON.decode(responseText);
fillObjectCompleter.complete(null);
return fillObjectCompleter.future;
}
Function1 is returning the future before the call "fillObject()" is completed.
What am I doing wrong?
The function1-future should be returned when the "for-loop" is done and all "fillObject-calls" are completed.
Async code is just scheduled for later execution and the sync code continues executing without waiting for the async code. The method you pass ot Future.then(...) is executed when the scheduled async code is finished. You find a lot of such questions and examples tagged [:dart-async:] here on StackOverflow.
I have a hard time figuring out what you actually try to accomplish. Can you please explain in prosa what you actually try to accomplish, then I can try to fix your code example to do what you want it to do.
Usually there is no need to use a Completer in custom async functions. You just have to ensure that nested async calls are properly chained by always returning the future of the call.
See these two lines of the following code as example. The returns are important for the example to work.
return async.Future.forEach(jsonMap, (item) {
return request.onLoadEnd.first.then((event) => fillObject(event.target));
The Future returned from getObjectData completes after the response of all requests are processed.
import 'dart:html' as dom;
import 'dart:async' as async;
import 'dart:convert' show JSON;
class Xxx {
static async.Future getObjectData(List jsonMap) {
return async.Future.forEach(jsonMap, (item) {
//var request = new dom.HttpRequest();
//request.open("GET", "https://www.googleapis.com/discovery/v1/apis?fields=");
var request = buildRequest("GET", resourceUrl);
request.send();
return request.onLoadEnd.first.then((event) => fillObject(event.target));
});
}
static fillObject(dom.HttpRequest request) {
print('fillObject');
String responseText = request.response;
List stringJson = JSON.decode(responseText);
}
}
void main() {
var json = ['a', 'b', 'c'];
Xxx.getObjectData(json).then((_) => print('done'));
}
See https://www.dartlang.org/articles/event-loop for more details about async execution.