Dart Future async not working as expected - dart

I'm getting an
'await' can only be used in 'async' or 'async*' methods.
In popup error:
The await expression can only be used in an async function. Try
marking the function body with either 'async' or
'async*'.dart(await_in_wrong_context)
firestore.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:workout/main.dart';
class FirestoreViewModel {
Future<List<int>> getDocuments(
String type, int numValues, bool isDescending) async {
List<int> _list;
QuerySnapshot documents = await FirebaseFirestore.instance
.collection('workouts')
.where('id', isEqualTo: googleSignIn.currentUser.id)
.orderBy('date', descending: isDescending)
.limit(numValues)
.get();
_list = documents.docs.map((snapshot) => snapshot['water'] as int).toList();
return _list;
}
}
file.dart
import 'package:workout/services/firestore_view_model.dart';
List<int> _waters = await FirestoreViewModel().getDocuments("water", 7, true);
I just can't seem to get a list with all the Futures, awaits, and asyncs going around. I've looked at other solutions on here but they are set up a bit differently. Either way, I don't know how to create a method that returns a list, when the firestore is full of futures. Any ideas would be appreciated.

Related

FutureOr can await void functions?

As far as I understand it should not be possible to await functions like void foo() async {}, because they return no future that could be awaited. But I noticed when I assign them to a FutureOr<void> Function() field it is possible to await them. The following example prints: PRE, FUNC, POST
import 'dart:async';
void func() async {
await Future.delayed(const Duration(seconds: 2));
print('FUNC');
}
Future<void> main() async {
final FutureOr<void> Function() t = func;
print('PRE');
await t();
print('POST');
}
I would expect it to print: PRE, POST, FUNC
Because the function func cannot be await (because it returns void) and therefore the await t() should not wait for the completion of the func function.
I observed the same behavior with final dynamic Function() t = func;
I'm not sure if this behavior is intended or if I just don't understand void async functions?
The await operator can be used on any object and on an expression of any type except void (and that's only because the type void is specifically disallowed in most contexts, not something special for await).
An async function always returns a future. Even if its declared return type is void. Declaring the return type as void is just a signal to users that they shouldn't expect a useful value, and it makes it harder to actually get to the value (because of the above-mentioned restrictions on using void typed expressions).
In fact, all void return-type functions return a value (unless they throw). That value is usually null, but it's not required to be.
Since you can override a void foo() method with int foo() in a subclass, and assign an int Function() to a variable of type void Function(), the compiler can't actually know for sure that a function with static type void does not return anything. So, it doesn't even try to enforce it for functions that are themselves typed as returning void.
The type FutureOr<void> is a supertype of void (and of Future<void>), so a void Function() function object is assignable to the type FutureOr<void> Function().
All in all, that conspires to allow your code to work the way you see.
The await t() performs an await on an expression with static type FutureOr<void> and an actual value which is a Future<void>, which was returned by a call to func. So, the await t() waits for FUNC to be printed.
Don't use await if you want skip task. Use Future.sync() or Future.microtask() in you case. Documentation
import 'dart:async';
void func() async {
await Future.delayed(const Duration(seconds: 2));
print('FUNC');
}
Future<void> main() async {
final FutureOr<void> Function() t = func;
print('PRE');
Future.microtask(t);
print('POST');
}
By using await you telling to compilator that you want to wait until some task will be completed and no mater it's void or not.

How to use await instead of .then() in Dart

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;
}

What are the pros and cons of async, when to and when not to use it and what other alternatives to callback are there?

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.

Why don't set the var (Dart)

I'm trying this in Dart:
import 'dart:convert';
import 'dart:html';
class testHandler {
Map parsedJSON;
testHandler();
void Initialize(){
String rawJSON = "core/testConfiguration.json";
HttpRequest.getString(rawJSON)
.then((String f) => parsedJSON.from(JSON.decode(f)))
.catchError((Error e) => print(e.toString()));
print(parsedJSON);
}
}
If you see I'm setting parsedJSON in .then() but when I'm trying to get the var, it returns null.
print(parsedJSON); is executed before getString() returns. getString() is async and the callback passed to then() will be executed sometimes later after getString() returned the result but print(parsedJSON); will be executed immediately.
Using async/await makes this quite easy:
import 'dart:convert';
import 'dart:html';
class testHandler {
Map parsedJSON;
testHandler();
Future Initialize() async {
String rawJSON = "core/testConfiguration.json";
try {
String f = await HttpRequest.getString(rawJSON);
parsedJSON = JSON.decode(f);
} catch(Error e) {
print(e.toString());
}
print(parsedJSON);
}
}
Async is contagious therefore code calling Initialize() has to wait for it to finish as well.
No, you are not setting parsedJSON in .then(). You are trying to call method from null object. Before use parsedJSON you should set it with = operator, like
parsedJSON = new Map.from(JSON.decode(f));
In other words, you mixed up parsedJSON's methods and Map's constructors.
P.S.
And, as Gunter denoted it, you may write it shortly:
parsedJSON = JSON.decode(f);

Future is completed before function-call is done

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.

Resources