How to return from then of a Future in dart - dart

I have a function which does some asynchronous operations and I want to return the status of the operation back to the caller. How can I achieve this?
Future<bool> setData() async {
Firestore.instance.collection("test").document('$id').setData({
'id': 'test'
}).then((onValue) {
print('Data set success');
return true;
}).catchError((onError) {
print('Data set Error!!!');
return false;
});
}
//Calling
final status = await setData();
if(status){
//do success
}
But this function complains that it doesn't end with a return statement. What is the logical mistake I'm making here?

You miss a return in your setData function
return Firestore.instance....

Related

Mapping a Stream<List> to another type is returning a Stream<Null>

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

Method running before previous method is finished, Future Async Dart

My method processData() is executing before pullAllData() is finished but I need processData() to wait until pullAllData() is completely finished before running. This is causing my isDownloadSuccessful bool to be Null when processData() is ran.
Future getCoinData() async {
calculateNumberOfDataPoints();
pullTimesAndPrices();
return timesAndPrices;
}
Future pullTimesAndPrices() async {
for (String cryptoCurrency in cryptoAbbreviation) {
pullAllData(cryptoCurrency);
processData(cryptoCurrency);
}
}
Future pullAllData(cryptoCurrency) async {
String historicalRequestURL =
'$cryptoAPIURL$cryptoCurrency$currency/ohlc?periods=$periodValue&apikey=$apiKey';
http.Response historicalResponse = await http.get(historicalRequestURL);
isPullSuccessful = (historicalResponse.statusCode == 200);
}
void processData(cryptoCurrency) {
if (isPullSuccessful) {
...
} else {
throw 'Problem pulling data';
}
}
You are marking your function pullTimesAndPrices as async but not using await. Use the await keyword before calling the pullAllData function.

Return value from subscribe in Ionic

So I want to return a value from a subscribe function like this:
async obtenerListadoClases(categoria) {
var clasesDB = await this.getClases(categoria)
.subscribe((data: any) => {
clasesDB = data.clasesDB // **Want to return this**
console.log(clasesDB,'clasesDB'); // **Getting Value**
})
console.log(clasesDB, 'outside'); // **Not Getting Value**
return clasesDB;
}
Also, I want to use this function in another place like this:
var listaClases = await this.claseServicio.obtenerListadoClases(categoria); // Not getting the correct info
// console.log(listaClases , 'listado clases');
What Im doing wrong? Or how can I fix it? Thanks in advance!
You can only subscribe to observables.
The Observable way
getClases(categoria): Observable<any> {
return new Observable(observer => {
// logic to return data
observer.next(data);
observer.complete()
// logic when error
observer.error(error);
});
}
Return the getClases() function
obtenerListadoClases(categoria): Observable<any>{
return this.getClases(categoria);
}
Use the function where you want:
this.obtenerListadoClases(categoria)
.subscribe(
result => {
// what you want to do with the result
},
error => {
// what you want to do with the error
});
The Promise way
getClases(categoria): Promise<any> {
return new Promise((resolve, reject) => {
// logic to return data
resolve(data);
// logic when error
reject(error);
});
}
Return the getClases() function
obtenerListadoClases(categoria): Promise<any>{
return this.getClases(categoria);
}
Use the function where you want:
this.obtenerListadoClases(categoria)
.then(result => {
// what you want to do with the result
})
.catch(error => {
// what you want to do with the error
});
You should be using promises with the .subscribe(). Only observables use .subcribe()
Also, stay away from promises in the angular world. Time to think reactive.
Is this returning an observable? this.getClases(categoria) post the code please.

How to test a method using a Future in Dart?

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

Execute Futures until a parameter becomes true

I launch a request to a server with a future "requestServer".
I would like to poll a system for a specific value (passed from false to true, when request is done) and return when finished.
Code could be like that, but "while" synchronous and "checkOperation" is asynchronous?
return requestServer().then((operation) {
var done = false;
while (done)
return checkOperation(operation).then((result) {
done = (result == true);
});
sleep(10);
}
});
Any ideas ?
I guess this is not exactly what you want but as far as I know there is no way to block execution so you have to use callbacks.
void main(List<String> args) {
// polling
new Timer.periodic(new Duration(microseconds: 100), (t) {
if(isDone) {
t.cancel();
someCallback();
}
});
// set isDone to true sometimes in the future
new Future.delayed(new Duration(seconds: 10), () => isDone = true);
}
bool isDone = false;
void someCallback() {
print('isDone: $isDone');
// continue processing
}
You can of course pass the callback as parameter instead of hardcode it, because functions are first class members in Dart.
Polling doesn't work very well for async. It is better to wait for a signal from the thing that must complete.
Günter Zöchbauer's answer shows you how to poll anyway, by sampling with a timer.
As an alternative, it would be better to not have a boolean done, but instead complete another future when you are ready. This is busy-polling, which polls again as soon as a result comes back, which may be more intensive than you need. Using timer based polling can be more efficient if you don't need the result as soon as possible.
return requestServer().then((operation) {
var completer = new Completer();
void poll(result) {
if (!result) {
operation.then(poll, onError: completer.completeError);
} else {
completer.complete();
}
}
poll(false);
return completer.future;
});
(Code not really tested, since I don't have your requestServer).
When you want build functions that return Futures, it is sometimes useful to use Completers. Think that requestServer() is living in the Future too, so you will have threat the result as a Future.
return requestServer().then((operation) {
// This is necessary then you want to control async
// funcions.
Completer completer = new Completer();
//
new Timer.periodic(const Duration(seconds: 10), (_) {
checkOperation(operation).then((result) {
// Only when the result is true, you pass the signal
// that the operation has finished.
// You can alse use `completer.complete(result)` if you want
// to pass data inside of the future.
if (result == true) completer.complete();
});
});
// You return the future straight away.
// It will be returned by requestServer();
return completer.future;
});
I use a function like this in a TestUtil library:
static Future<bool> waitUntilTrue(bool Function() callback,
{Duration timeout: const Duration(seconds: 2),
Duration pollInterval: const Duration(milliseconds: 50)}) {
var completer = new Completer<bool>();
var started = DateTime.now();
poll() {
var now = DateTime.now();
if (now.difference(started) >= timeout) {
completer.completeError(Exception('timed out in waitUntilTrue'));
return;
}
if (callback()) {
completer.complete(true);
} else {
new Timer(Duration(milliseconds: 100), () {
poll();
});
}
}
poll();
return completer.future;
}
And then in my test code I'll do something like:
await TestUtil.waitUntilTrue(() => someObj.isDone);
Edit:
Note that if you're using this in a testWidgets test, you have to do a little extra, since it relies on real async work happening:
await tester.runAsync<bool>(
() => TestUtil.waitUntilTrue(() => myObj.isLoaded),
additionalTime: Duration(seconds: 5));

Resources