Hey there I am quite new to Dart Futures and I have the following situation.
Whenever a user types a letter in the UI the addressChanged() method in my ui_component is called. This method calls the method getProposals() in my maps componenet which does an asynchronous request to the google maps API. As soon as the results are here I want to return them to the UI Component which is going to populate the propasals dropdown in the UI.
I am stuck with the last step: How (and whats the best way) to return the results of an asynchronous callback function to a parent component (while keeping an reusable maps component?).
This is what I have tried:
1) UI_Component:
// I get called if a user typed a new letter
Future addressChanged(dynamic event) async {
String id = event.target.id;
String address = event.target.value;
if(id=="pickup") {
this.pickup = address;
} else if(id=="destination") {
this.destination = address;
}
// this is where I call the subcomponent and want to get the address propasals
String proposals = await googleMap.getProposals(address,id);
print(proposals);
populateProposalDropdown();
}
2) Google Map component:
Future getProposals(String address,String id) async {
await _getProposals(address,id);
}
Future _getProposals(String address,String id) async {
if(address != "") {
autocompleteService.getPlacePredictions(
new AutocompletionRequest()
..input = address
,
(predictions,status) {
List<String> result = [];
if(status == PlacesServiceStatus.OK) {
predictions.forEach(
(AutocompletePrediction prediction) =>
result.add(prediction.description)
);
}
// HERE is the problem: How do I return this result from the callback as a result of the getProposals method?
return result;
}
);
}
}
This method doesn't return any data
Future getProposals(String address,String id) async {
await _getProposals(address,id);
}
Change it to
Future getProposals(String address,String id) {
return _getProposals(address,id);
}
This would also work, but here async and await is redunant
Future getProposals(String address,String id) async {
return await _getProposals(address,id);
}
For _getProposals you can use a Completer
Future _getProposals(String address,String id) async {
if(address != "") {
Completer completer = new Completer();
autocompleteService.getPlacePredictions(
new AutocompletionRequest()
..input = address
,
(predictions,status) {
List<String> result = [];
if(status == PlacesServiceStatus.OK) {
predictions.forEach(
(AutocompletePrediction prediction) =>
result.add(prediction.description)
);
}
// HERE is the problem: How do I return this result from the callback as a result of the getProposals method?
completer.complete(result);
}
);
return completer.future;
}
return null;
}
Related
So I want to know how I can get all the entries where my sessionId equals to (for example: 4).
public async Task<IActionResult> Index(int? session)
{
var cardio = await _client.GetEntries<CardioModel>();
// ContentfulCollection<CardioModel> cardioModels = new ContentfulCollection<CardioModel>();
// foreach (var model in cardio)
// {
// if(model.Session == 4){
// cardioModels.add(model)
// }
// }
return View(cardio);
}
I tried this but the .add isn't a thing, I tried various ways but none of them is working and I can't find good docs about what I want. I thought probably something with .getEntriesRaw() but I don't know how to work with that.
public async Task<IActionResult> Index(int? session)
{
var cardio = await _client.GetEntries<CardioModel>();
// ContentfulCollection<CardioModel> cardioModels = new ContentfulCollection<CardioModel>();
// foreach (var model in cardio)
// {
// if(model.Session == 4){
// cardioModels.add(model)
// }
// }
return View(cardio);
}
How about just modifying just a little bit
public async Task<IActionResult> Index(int? session)
{
if (session == null || session != 4) return View(null);
var cardio = await _client.GetEntries<CardioModel>().FirstOrDefault(x => x.Session == 4);
return View(cardio);
}
My implementation:
public async void ListenRedisTask()
{
while (!Token.IsCancellationRequested)
{
var lastHandledElement = redisComsumer.WaitToGetNewElement();
if (lastHandledElement != null)
{
await channelProducer.Write(ParseResult(lastHandledElement));
}
}
}
public Dictionary<string, string>? WaitToGetNewElement()
{
var result = client.XRead(1, expiryTime, new (string key, string id)[] { new(streamName, "$") });
if (result != null)
{
return parse(result[0]);
}
return null;
}
In redis stream i have correct data like: insert,delete,insert,delete...
But in channel for storage current hadled item i have data like: delete, delete, delete, insert, delete..
It's wrong!
I think my error connected with using xread, maybe when xread method is called next invoke of this method ignore interstitial data from redis stream.
I want to check if the Firebase DB is connected or not, so I have to use a Future to return a boolean
Have a check at my code..
#override
Future<bool> isAvailable() async {
bool ret = false;
await firebaseInstance.reference().child('.info/connected').onValue.listen((event) {
ret = event.snapshot.value;
});
return ret;
}
the firebaseInstace.reference is a StreamSubscription type and does not wait for the future to return me a result.
please help.
If you only need to know the current value, use once().then instead of onValue.listen
#override
Future<bool> isAvailable() async {
var snapshot = await firebaseInstance.reference().child('.info/connected').once();
return snapshot.value;
}
Instead of awaiting the end of the stream subscription (it never ends), just take the first value:
#override
Future<bool> isAvailable() => firebaseInstance.reference().child('.info/connected').onValue.first;
You can put the StreamSubcription in a variable
StreamSubscription subscription = someDOMElement.onSubmit.listen((data) {
// you code here
if (someCondition == true) {
subscription.cancel();
}
});
More can be found here is there any way to cancel a dart Future?
You can do the following:
#override
Future<bool> isAvailable() async {
bool ret = false;
Stream<Event> events =
FirebaseDatabase.instance.reference().child('.info/connected').onValue;
await for (var value in events) {
ret = value.snapshot.value;
}
return ret;
}
onValue returns a Stream<Event> and then you can use await for to iterate inside the Stream and get the data, and then it will return.
I'm currently playing around with Futures in flutter. I've some async functions that return a Future object. I register a Listener on the Future object with then(), so that I can update the ui as soon as the value comes in.
But the result is empty, because then() returns before all notes are loaded from the file system.
Future<List<Note>> loadNotes() async {
NoteService().findAll().then((result) {
result.forEach((note) => print(note.title)); //not printing -> result is emtpty...
});
}
//NoteService class
Future<List<Note>> findAll() async {
return noteRepository.findAll();
}
//NoteRepository class
#override
Future<List<Note>> findAll() async {
final Directory dir = await directory;
dir.list().toList().then((List<FileSystemEntity> list) async {
List<String> paths = List();
list.forEach((entity) => paths.add(entity.path));
List<File> _files = List();
paths.forEach((path) => _files.add(File(path)));
List<Note> notes = await _extractNotes(_files);
return Future.value(notes);
});
return Future.value(List());
}
Future<List<Note>> _extractNotes(List<File> _files) async {
List<Note> notes = List();
_files.forEach((file) {
String content = file.readAsStringSync();
print('content: ' + content); //this is getting printed correctly to the console
Map<String, dynamic> a = jsonDecode(content);
if(a.containsKey('codeSnippets')) {
notes.add(SnippetNoteEntity.fromJson(jsonDecode(content)));
} else {
notes.add(MarkdownNoteEntity.fromJson(jsonDecode(content)));
}
});
return Future.value(notes);
}
In my class I'm loading some files, and for efficiency I wanted to make a thread safe cache. I see in the map class that there is a putIfAbsent method, but it doesn't accept async types. Also not sure if this structure in general is safe to use.
This is the style of what I'm trying to do:
final Map<String, String> _cache = new Map();
Future<String> parse(final String name) async {
_cache.putIfAbsent(name, () async { // this async is not allowed
return await new File(name).readAsString();
});
return _cache[name];
}
Since I can use async on the parameter I've opted to use locks instead, but it makes the code far more verbose..
final Lock _lock = new Lock();
final Map<String, String> _cache = new Map();
Future<String> parse(final String name) async {
if (!_cache.containsKey(name)) {
await _lock.synchronized(() async {
if (!_cache.containsKey(name)) {
_cache[name] = await new File(name).readAsString();
}
});
}
return _cache[name];
}
Does anyone know how I can simplify this code, or if there are better libraries I can use for thread safe cache?
What do you mean by "this async is not allowed"? I see no particular issue with the putIfAbsent code, and I believe it should work.
The one probelem I see is that the cache is not caching futures, but strings. Since your function is returning a future anyway, you might as well store the future in the cache.
I would write it as:
final Map<String, Future<String>> _cache = new Map();
Future<String> parse(final String name) =>
_cache.putIfAbsent(name, () => File(name).readAsString());
but apart from fixing the _cache map type, that is effectively the same, it's just avoiding creating and waiting for a couple of extra futures.
I've created an extension to support an asynchronous action for putIfAbsent:
extension MapUtils<K, V> on Map<K, V> {
Future<V> putIfAbsentAsync(K key, FutureOr<V> Function() action) async {
final V? previous = this[key];
final V current;
if (previous == null) {
current = await action();
this[key] = current;
} else {
current = previous;
}
return current;
}
}
You can use like this:
final Map<String, String> _cache = {};
Future<String> parse(final String name) async {
return await _cache.putIfAbsentAsync(
name,
() async => await File(name).readAsString(),
// ^^^^^ this `async` is now allowed
);
}