How we can merge multiple FutureTask so that we can get a callback for all at the same response.
We use Future<T> like
Future<String> getData(int duration) async {
await Future.delayed(Duration(seconds: duration)); //Mock delay
return "This a test data for duration $duration";
}
Call above method like getData(2).then((value) => print(value));
If we want to call multiple Future Task, then how can we do that?
To execute all futures concurrently, use Future.wait This takes a list of futures and returns a future of lists:
Suppose you have these futures.
class CovidAPI {
Future<int> getCases() => Future.value(1000);
Future<int> getRecovered() => Future.value(100);
Future<int> getDeaths() => Future.value(10);
}
You can get all the futures together using Future.wait([list of futures])
final api = CovidAPI();
final values = await Future.wait([
api.getCases(),
api.getRecovered(),
api.getDeaths(),
]);
print(values); // [1000, 100, 10]
This is ideal when the futures are independent, and they don't need to execute sequentially.
Source : https://codewithandrea.com/videos/top-dart-tips-and-tricks-for-flutter-devs/
For it, FutureGroup can be used to combine multiple streams
FutureGroup provides us the functionality of combining multiple
futures into one single group, which will give the callback at the end
when all future tasks work gets completed.
Dependency:
dependencies:
async: ^2.4.1
How to implement FutureGroup?
FutureGroup futureGroup = FutureGroup();
futureGroup.add(future1);
futureGroup.add(future2);
futureGroup.add(future3);
Use:
void main() {
Future<String> future1 = getData(2);
Future<String> future2 = getData(4);
Future<String> future3 = getData(6);
FutureGroup futureGroup = FutureGroup();
futureGroup.add(future1);
futureGroup.add(future2);
futureGroup.add(future3);
futureGroup.close();
futureGroup.future.then((value) => {print(value)});
}
Future<String> getData(int duration) async {
await Future.delayed(Duration(seconds: duration)); //Mock delay
return "This a test data";
}
Output:
I/flutter ( 5866): [This a test data, This a test data, This a test
data] // Called after 6 seconds.
Note: This will be called only once when all the Future Task gets completed, here it will run after 6 seconds.
Related
Given that I have the following data:
var uniqueIds = [1, 2, 3, 4];
I want to perform async requests in order based on the input above:
List<Future<void>> futures = uniqueIds.map((e) {
return APIService.shared
.networkingRequest(input: e)
.then((value) => update())
.onError((error, stackTrace) => {
handleError(error)});
}).toList();
Where I want to trigger something like futures.waitInOrder(...) with whenComplete handler.
Because Dart begins running a Future's computation immediately upon the creation of that Future, the only practical way to do this is to create each Future immediately prior to awaiting their result, rather than mapping all the values to Futures up-front.
What that will look like is something like this:
void foo(List<int> uniqueIds) async {
for (final uniqueId in uniqueIds) {
await APIService.shared
.networkingRequest(input: uniqueId)
.then((value) => update())
.onError((error, stackTrace) {
handleError(error);
});
}
}
I know how Future.wait works
await Future.wait([
functionA(),
functionB()
]).then((data) {
});
but what I want to know is
What if the number of functions is not fixed and the number of functions to be called varies depending on the situation? (the functions are all the same.)
Elaborating on my comments, you can dynamically build a List of Futures and use Future.wait on that. For example:
Future<void> doAsynchronousStuff() async {
var waitList = <Future<void>>[];
if (someCondition) {
waitList.add(someAsynchronousFunction(someArgument));
}
if (someOtherCondition) {
waitList.add(someOtherAsynchronousFunction(someOtherArgument));
}
// ... and so on...
await Future.wait(waitList);
}
If you need to handle different return values from your asynchronous functions, you can use anonymous functions that set local variables. See Dart Future.wait for multiple futures and get back results of different types.
As long as you have an iterable reference, you can map that and return the Future function.
Future<void> main() async {
final List<int> resultFromApi = <int>[1, 2, 3, 4, 5];
final List<int> finalResult = await Future.wait(resultFromApi.map(
(int data) => complexProcess(data), // return the Future
));
print(finalResult);
}
Future<int> complexProcess(int data) async {
await Future<void>.delayed(const Duration(seconds: 1));
return data * 10;
}
I have found answers how to wait for 2 different futures when building widgets, but these were of the same type. Example from here:
But is there also a possibility if firstFuture() and secondFuture() return different value types - eg int and String (or different classes as in my use case)? The bool in AsyncSnapshot<List<bool>> snapshot is a challenge to me...
FutureBuilder(
future: Future.wait([
firstFuture(), // Future<bool> firstFuture() async {...}
secondFuture(),// Future<bool> secondFuture() async {...}
//... More futures
]),
builder: (
context,
// List of booleans(results of all futures above)
AsyncSnapshot<List<bool>> snapshot,
){
// Check hasData once for all futures.
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
// Access first Future's data:
// snapshot.data[0]
// Access second Future's data:
// snapshot.data[1]
return Container();
}
);
I also found another answer with different types, but this apples to functions not classes
List<Foo> foos;
List<Bar> bars;
List<FooBars> foobars;
await Future.wait<void>([
downloader.getFoos().then((result) => foos = result),
downloader.getBars().then((result) => bars = result),
downloader.getFooBars().then((result) => foobars = result),
]);
processData(foos, bars, foobars);
Well, you can always use Object as the lowest common return value of those two methods:
Future.wait<Object>([firstFuture(), secondFuture()])
But you will have to cast the entries of the resulting List<Object> back to what you think they should be. Not perfect, but it would work.
Personally, I like the method you already discovered a bit more, return only the bare minimum future, a Future<void> and have it write to the respective variables inside the method. That is not perfect either, but it keeps the type safety.
Below answer might help you
Use FutureGroup inside the package
dependencies: async: ^2.4.1
Code:
void main() {
Future<String> future1 = getData(2);
Future<String> future2 = getData(4);
Future<String> future3 = getData(6);
FutureGroup futureGroup = FutureGroup();
futureGroup.add(future1);
futureGroup.add(future2);
futureGroup.add(future3);
futureGroup.close();
futureGroup.future.then((value) => {print(value)});
}
Future<String> getData(int duration) async {
await Future.delayed(Duration(seconds: duration)); //Mock delay
return "This a test data";
}
Output:
I/flutter ( 5866): [This a test data, This a test data, This a test data] // Called after 6 seconds.
For more reference:
https://medium.com/flutterworld/flutter-futuregroup-d79b0414eaa7
I'm trying to write a test using FakeAsync but it seems to hang on my awaits. Here's a stripped down example:
test('danny', () async {
await FakeAsync().run((FakeAsync async) async {
print('1');
final a = Future<bool>.delayed(const Duration(seconds: 5))
.then((_) => print('Delayed future completed!'))
.then((_) => true);
print('2');
async.elapse(const Duration(seconds: 30));
// Tried all this too...
// async.flushMicrotasks();
// async.flushTimers();
// async.elapse(const Duration(seconds: 30));
// async.flushMicrotasks();
// async.flushTimers();
// async.elapseBlocking(const Duration(seconds: 30));
print('3');
await a;
print('4');
expect(1, 2);
});
});
This code outputs:
1
2
Delayed future completed!
3
// hangs and never prints '4'
The async.elapse call is allowing the future to be completed, but it still hangs on await a. Why?
This seems to occur because although the Future is completed, the await call requires the microtask queue to be processed in order to continue (but it can't, since nobody is calling async.elapse after the await).
As a workaround, contiually pumping the microstask queue while the function is running seems to work - for example calling this function in place of FakeAsync.run:
/// Runs a callback using FakeAsync.run while continually pumping the
/// microtask queue. This avoids a deadlock when tests `await` a Future
/// which queues a microtask that will not be processed unless the queue
/// is flushed.
Future<T> runFakeAsync<T>(Future<T> Function(FakeAsync time) f) async {
return FakeAsync().run((FakeAsync time) async {
bool pump = true;
final Future<T> future = f(time).whenComplete(() => pump = false);
while (pump) {
time.flushMicrotasks();
}
return future;
}) as Future<T>;
}
It seems that await does not work inside fakeAsync, so you are stuck with Future.then() and friends.
However you can very easily wait for all Futures to complete by calling time.elapse()and time.flushMicrotasks().
test('Completion', () {
fakeAsync((FakeAsync time) {
final future = Future(() => 42);
time.elapse(Duration.zero);
expect(future, completion(equals(42)));
time.flushMicrotasks();
});
});
See this code:
class SomeClass{
String someVariable;
SomeClass();
Future<String> getData () async {
Response response = await get('http://somewebsite.com/api/content');
Map map = jsonDecode(response.body); // do not worry about statuscode, trying to keep it minimal
someVariable = map['firstName'];
return 'This is the first name : $someVariable';
}
}
Now look at main:
void main(){
String someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData().then((value) => value);
return firstNameDeclaration;
}
}
When working with Future, like in the case of firstNameDeclaration why do I have to use .then() method to access the string object, since I am waiting for the function to finish?
When searching on the web, some people use .then() others don't, I am confused.
Kindly help me have a clearer understanding of how Futures and async functions overall work.
Background
Asynchronous operations let your program complete work while waiting for another operation to finish. Here are some common asynchronous operations:
Fetching data over a network.
Writing to a database.
Reading data from a file.
To perform asynchronous operations in Dart, you can use the Future class and the async and await keywords.
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
Futures vs async-await
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
async-await is just a a declarative way to define asynchronous functions and use their results into Future and it provides syntactic sugar that help you write clean code involving Futures.
Consider this dart code snipped involving Futures -
Future<String> getData(int number) {
return Future.delayed(Duration(seconds: 1), () {
return 'this is a future string $number.';
});
}
main(){
getData(10).then((data) => {
print(data)
});
}
As you can see when you use Futures, you can use then callback when the function return a future value. This is easy to manage if there is single "then" callback but the situation escalates quickly as soon as there are many nested "then" callbacks for example -
Future<String> getProductCostForUser() {
return getUser().then((user) => {
var uid = user.id;
return getOrder(uid).then((order) => {
var pid = order.productId;
return getProduct(pid).then((product) => {
return product.totalCost;
});
});
});
}
main(){
getProductCostForUser().then((cost) => {
print(cost);
});
}
As you can when there multiple chained "then" callback the code become very hard to read and manage. This problem is solved by "async-await". Above chained "then" callbacks can be simplified by using "async-await" like so -
Future<String> getProductCostForUser() async {
var user = await getUser();
var order = await getOrder(user.uid);
var product = await getProduct(order.productId);
return product.totalCost;
}
main() async {
var cost = await getProductCostForUser();
print(cost);
}
As you can above code is much more readable and easy to understand when there are chained "then" callbacks.
I hope this explains some basic concepts and understanding regarding the "async-await" and Futures.
You can further read about topic and examples here
Basically, you should either use await OR then(). However, Dart guidelines advocates that you should prefer use await over then() :
This code :
Future<int> countActivePlayers(String teamName) {
return downloadTeam(teamName).then((team) {
if (team == null) return Future.value(0);
return team.roster.then((players) {
return players.where((player) => player.isActive).length;
});
}).catchError((e) {
log.error(e);
return 0;
});
}
should be replaced by :
Future<int> countActivePlayers(String teamName) async {
try {
var team = await downloadTeam(teamName);
if (team == null) return 0;
var players = await team.roster;
return players.where((player) => player.isActive).length;
} catch (e) {
log.error(e);
return 0;
}
}
In your case, you should write :
void main(){
Future<String> someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData();
return firstNameDeclaration;
// Or directly : return await instance.getData();
// Or : return instance.getData();
}
}