This question already has answers here:
What is a Future and how do I use it?
(6 answers)
Closed 10 months ago.
I need to know whether or not the file exists in the folder, but I get an instance of Future<bool>, how do I get the false or true?
for (String curFile in splittedList){
Future<bool> t2 = Future.value(File(curFile).exists());
print("$curFile - $t2");
}
result
I/flutter (16852): assets/images/Symbol/4.svg - Instance of 'Future<bool>'
var t2 = await File('...').exists()
hope it helpful.
EDIT -----
try this
Future<bool> checkAssets(String key) async {
bool result = true;
try {
await rootBundle.load(key);
} catch (e) {
result = false;
}
return result;
}
Related
This question already has answers here:
chrome.runtime.onMessage response with async await
(7 answers)
Closed 1 year ago.
I am having an issue of asynchronicity (I believe). sendResponse() in contentscript.js does not wait for getThumbnails() to return.
I am sending a message in popup.js:
chrome.tabs.sendMessage(tabs[0].id, {message: "get_thumbnails", tabUrl: tabs[0].url},
function (respThumbnails) {
const thumbUrl = respThumbnails.payload;
console.log("payload", thumbUrl)
}
);
Then, in contentscript.js I listen for this message:
chrome.runtime.onMessage.addListener(async function(request,sender,sendResponse) {
if(request.message === "get_thumbnails") {
const payload = await getThumbnails();
console.log("thumbPayload after function:", payload)
sendResponse({payload:payload});
}
});
async function getThumbnails() {
let tUrl = null;
var potentialLocations = [
{sel: "meta[property='og:image:secure_url']", attr: "content" },
{sel: "meta[property='og:image']", attr: "content" },
];
for(s of potentialLocations) {
if(tUrl) return
const el = document.querySelector(s.sel);
if(el) {
tUrl = el.getAttribute(s.attr) || null;
}
}
return tUrl;
};
But it is also possible that the problem is coming from my getThumnails() function, because most of the times, payload is null and not undefined. So getThumbnails() might return before it is completely executed.
If this is the case, I have no idea why...
I also tried this code for getThubnails():
async function getThumbnails() {
let x = await function() {
let tUrl = null;
var potentialLocations = [
{sel: "meta[property='og:image:secure_url']", attr: "content" },
{sel: "meta[property='og:image']", attr: "content" },
];
for(s of potentialLocations) {
if(tUrl) return
const el = document.querySelector(s.sel);
if(el) {
tUrl = el.getAttribute(s.attr) || null;
}
}
return tUrl;
}
return x;
};
But this does not work, it seems to break my code...
The callback of onMessage should return a literal true value (documentation) in order to keep the internal messaging channel open so that sendResponse can work asynchronously.
Problem
Your callback is declared with async keyword, so it returns a Promise, not a literal true value. Chrome extensions API doesn't support Promise in the returned value of onMessage callback until https://crbug.com/1185241 is fixed so it's just ignored, the port is immediately closed, and the caller receives undefined in response.
Solutions
Remove the async keyword from before (request, sender, sendResponse), then...
Solution 1
Call an async function that can be embedded as an IIFE:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.message === "get_thumbnails") {
(async () => {
const payload = await getThumbnails();
console.log("thumbPayload after function:", payload)
sendResponse({payload});
})();
return true; // keep the messaging channel open for sendResponse
}
});
Solution 2
Declare a separate async function and call it from the onMessage listener:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.message === "get_thumbnails") {
processMessage(msg).then(sendResponse);
return true; // keep the messaging channel open for sendResponse
}
});
async function processMessage(msg) {
console.log('Processing message', msg);
// .................
return 'foo';
}
I'm trying to transform a Stream of a list of one type into a Stream of a list of another type, and having an issue with this.
I have this list of Habits that I'm streaming from Firebase, and I want to accept that stream in a function, and return a new stream that is a list of ViewModels of another type from it. But my function is returning a stream of the wrong type.
Here is my code:
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.map((habitsList) {
habitsList.map(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) => completion.date
.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
I am getting a compile error because the result variable is showing as type Stream<Null> when I hover over it, where I would expect it to be Stream<List<HabitCompletionViewModel>>. Any idea what I'm doing wrong?
Your outer .map call does not have a return statement which is why you are getting a Stream<Null>.
So add a return statement like so:
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.map((habitsList) {
// added return statement here
return habitsList.map(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) =>
completion.date.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
However the above code still has an error because it is now returning a Stream<List<Future<HabitCompletionViewModel>>> instead of the desired Stream<List<HabitCompletionViewModel>>. To solve this you can use .asyncMap instead of .map.
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.asyncMap((habitsList) {
return Stream.fromIterable(habitsList).asyncMap(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) =>
completion.date.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
I have DocumentReference locationDocumentRef; in my state.
I'm changing locationDocumentRef based on the references, whether I gather by querying or by adding new document.
So I have this function to check the documents, if there is one set its reference to the locationDocumentRef, or add a new one and set its ref to the locationDocumentRef. I'm resetting its value everytime by setting it to null, since I didn't want to get previous result. But it prints null.
So my question is, how can I resolve them and get the value? I think I'm resolving too early in my code, so I can't await a non-future value. How can I fix it?
void firestoreCheckAndPush() async {
setState(() {
locationDocumentRef = null;
});
bool nameExists = await doesNameAlreadyExist(placeDetail.name);
if (nameExists) {
print('name exist');
} else {
print('name will be pushed on firestore');
pushNameToFirestore(placeDetail);
}
var resolvedRef = await locationDocumentRef;
print(resolvedRef.documentID); // I get null here
}
These are the functions that I have used
Future<bool> doesNameAlreadyExist(String name) async {
QuerySnapshot queryDb = await Firestore.instance
.collection('locations')
.where("city", isEqualTo: '${name}')
.limit(1)
.getDocuments();
if (queryDb.documents.length == 1) {
setState(() {
locationDocumentRef = queryDb.documents[0].reference;
});
return true;
} else {
return false;
}
}
And the other
void pushNameToFirestore(PlaceDetails pd) async {
DocumentReference justAddedRef =
await Firestore.instance.collection('locations').add(<String, String>{
'city': '${pd.name}',
'image': '${buildPhotoURL(pd.photos[0].photoReference)}',
});
setState(() {
locationDocumentRef = justAddedRef;
});
}
there is two mistakes i saw first here
var resolvedRef = await locationDocumentRef;
why you await for locationDocumentRef,
second you dont wait for pushNameToFirestore(PlaceDetails pd) firestoreCheckAndPush() function which is weird since pushNameToFirestore(String) is sync and this means you wouldnt wait for it to finish so if you are adding a new name it would print null.
correct me if i am wrong.
you can find more about sync and future here https://www.dartlang.org/tutorials/language/futures
look at the graph at the middle of the page
Try this
Future<List<DocumentSnapshot>> doesNameAlreadyExist(String name) async {
QuerySnapshot data = await Firestore.instance
.collection('locations')
.where("city", isEqualTo: name)
.limit(1)
.getDocuments();
return data.documents;
}
void firestoreCheckAndPush() async {
var data = await doesNameAlreadyExist('yourname');
if (data.length > 0) {
print('name exist');;
print('Document id '+ data[0].documentID);
} else {
print('name will be pushed on firestore');
}
}
Take a look into following code.
void firestoreCheckAndPush() async {
DocumentReference documentReference;
var data = await doesNameAlreadyExist('yourname');
var dataRef = await doesNameAlreadyExist('yourname');
if (data.length > 0) {
print('name exist');
documentReference = dataRef[0].reference;
print('Document id ' + data[0].documentID);
documentReference = dataRef[0].reference;
print('Document reference ');
print(documentReference);
} else {
print('name will be pushed on firestore');
}
}
I need to load a string from a file. The following code always returns null:
static String l( String name ) {
String contents;
rootBundle
.loadString( 'i10n/de.yaml' )
.then( (String r) { contents = 'found'; print( 'then()' ); })
.catchError( (e) { contents = '#Error#'; print( 'catchError()' ); })
.whenComplete(() { contents = 'dd'; print( 'whenComplete()' ); })
;
print( 'after' );
if ( null == contents ) {
return '#null#';
}
String doc = loadYaml( contents );
return doc;
}
I have added this to the flutter: section in pupspec.yaml section:
assets:
- i10n/de.yaml
- i10n/en.yaml
The file i10n/de.yaml exists.
I'm aware, that rootBundle.loadString() is async. Therefore I appended the then() call - assuming that
(String r) { contents = 'found'; }
only gets executed if the Future returned by rootBundle.loadString() is able to return a value.
Actually, the method always returns '#null#'. Therefore, I added the print() statements, which output this:
I/flutter (22382): after
I/flutter (22382): then()
I/flutter (22382): whenComplete()
OK, obviously the future of loadString() executes later than the final print() statement.
Q: But how do I force the future to execute, so that I may retrieve its value?
In other words: How do I wrap some async stuff in certain code to retrieve its value immediately?
PS: First day of flutter/dart. Probably a trivial question...
the .then() is getting executed, but after the rest of the body. As you mention loadString() returns a Future, so completes in the future. To wait for a Future to complete use await. (Note that when you mark the function as async, the function must now return a Future itself - as it has to wait for loadString to complete in the future, so it itself has to complete in the future...) When you call l('something') you will have to await the result.
Future<String> l(String name) async {
try {
String contents = await rootBundle.loadString('i10n/de.yaml');
return contents == null ? '#null#' : loadYaml(contents);
} catch (e) {
return 'oops $e';
}
}
It's no big deal that lots of your utility functions become async as a result of having to wait for things (there's a lot of waiting - for files to read, http requests to complete, etc). You'll eventually end up at the top with something like (call this from initState)
refresh(String s) {
l(s).then((r) {
setState(() {
i18nStuff = r;
});
});
}
to set the state of your Widget when the i18nStuff is ready, and a Widget that has this in its build to switch between a dummy ui for the few milliseconds until it's ready, and then the real UI.
Widget build() {
if (i18nStuff == null) {
return new Container();
}
return new Column(
// build the real UI here
);
}
I would like to test a method which execute a POST on another server :
Future executePost() {
_client.post("http://localhost/path", body : '${data}').then((response) {
_logger.info("Response status : ${response.statusCode}");
_logger.info("Response body : ${response.body}");
Completer completer = new Completer();
completer.complete(true);
return completer.future;
}).catchError((error, stackTrace) {
_logger.info(error);
_logger.info(stackTrace);
});
}
The problem I'm dealing with is that my testing method ends before the future returned by "_client.post" is executed.
My testing method :
test('should be true', () {
try {
Future ok = new MyClient().executePost();
expect(ok, completion(equals(true)));
} catch(e, s) {
_logger.severe(e);
_logger.severe(s);
}
});
Thanks for your help !
Your executePost() method doesn't even return a future, it returns null.
client.post() returns a future but this return value isn't used.
Try to change it to:
Future executePost() {
return _client.post("http://localhost/path", body : '${data}').then((response) {
_logger.info("Response status : ${response.statusCode}");
_logger.info("Response body : ${response.body}");
return true;
}).catchError((error, stackTrace) {
_logger.info(error);
_logger.info(stackTrace);
});
}