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;
}
Related
void main() async {
check();
print('end');
}
Future check() async {
var version = lookUpVersion();
print(version);
}
int lookUpVersion() {
return 12;
}
void main() async {
check();
print('end');
}
Future check() async {
var verion = await lookUpVersion();
print(version);
}
int lookUpVersion() {
return 12;
}
These two code only have one difference, await keyword.
I wonder that why not did they wait for main function code? even I used Future+async keyword at first code.
Can you explain about this?
The async and await keywords provide a declarative way to define asynchronous functions and use their results.
For first one - result will be
//12
//end
For second one - result will be
//end
//12
Which means if you add await it will become asynchronous.
How can I run a map function inside a Future method?
This is my code:
Future deactivateLogs(List<String> members) async {
return members.map((member) async {
return await logDataCollection
.document('log-$member')
.updateData({
'active': false,
});
});
}
When I call this method, the map function is not being executed. How can I change the syntax so that it runs?
The goal is that the data is being updated in the database (backend) for each of the individual 'member' strings.
The problem can also be made easier:
Future deactivateTribeLogs() {
List<int> list = [1, 2, 3];
list.map((e) => print(e));}
The map function does not run in this case either.
Many thanks in advance!
You have two problems:
You're using an asynchronous callback with Iterable.map, but nothing will wait for the callbacks to complete.
Iterable.map is lazy; if you never attempt to use the result of a callback, it won't bother executing that callback at all. (Iterable.map normally is meant for functional-style programming where the callbacks are "pure"; you usually don't want to use it with callbacks that have side-effects.)
When you call Iterable.map with an asynchronous callback, it returns an iterable of Futures. If you don't care about execution order of callbacks, you could use Future.wait on that directly:
Future<void> deactivateLogs(List<String> members) async {
var waitList = members.map((member) async {
return await logDataCollection.document('log-$member').updateData({
'active': false,
});
});
await Future.wait(waitList);
}
Doing so also will evaluate all elements of the returned list of Futures and will avoid your issue with Iterable.map's lazy evaluation.
You can also use for loop for that :
Future deactivateLogs(List<String> members) async {
for(var i = 0; i< members.length; i++) {
await logDataCollection
.document('log-' + members[i])
.updateData({
'active': false,
});
}
return ;
}
If you look at the definition of List::map, you will see that it returns an Iterable, which is not evaluated.
Another way of saying it is that List::map is lazy and will not run until the iterable is either explicitly iterated or converted to a list.
Actually from the documentation, it says:
Returns a new lazy Iterable with elements that are created by calling f on each element of this Iterable in iteration order.
To fix your problem, just append .toList() at the end of the .map call, and it will force the iterable to be evaluated.
Since you are not looking to return anything from the function, you can just use Future::forEach:
Future<void> deactivateLogs(List<String> members) =>
Future.forEach(members, (member) => logDataCollection
.document('log-$member')
.updateData({
'active': false,
});
);
Full example
void main() async {
print('start');
await deactivateLogs(["Foo", "Bar", "Hello", "World"]);
print('end');
}
Future<void> deactivateLogs(List<String> members) =>
Future.forEach(members, (member) {
final v = await Future(() => member);
print(v);
});
Output
start
Foo
Bar
Hello
World
end
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();
}
}
I have a list List<Item> list and a function Future<bool> myFilter(Item).
Is there a way to filter my list using the Future returning function myFilter()?
The idea is to be able to do something like this:
final result = list.where((item) => myFilter(item)).toList();
But this is not possible since where expects bool and not Future<bool>
Since the iteration involves async operation, you need to use a Future to perform the iteration.
final result = <Item>[];
await Future.forEach(list, (Item item) async {
if (await myFilter(item)) {
result.add(item);
}
});
You can iterate over your collection and asynchronously map your value to the nullable version of itself. In asyncMap method of Stream class you can call async methods and get an unwrapped Future value downstream.
final filteredList = await Stream.fromIterable(list).asyncMap((item) async {
if (await myFilter(item)) {
return item;
} else {
return null;
}
}).where((item) => item != null).toList()
You can try bellow:
1, Convert List => Stream:
example:
Stream.fromIterable([12, 23, 45, 40])
2, Create Future List with this function
Future<List<int>> whereAsync(Stream<int> stream) async {
List<int> results = [];
await for (var data in stream) {
bool valid = await myFilter(data);
if (valid) {
results.add(data);
}
}
return results;
}
Here's a complete solution to create a whereAsync() extension function using ideas from the accepted answer above. No need to convert to streams.
extension IterableExtension<E> on Iterable<E> {
Future<Iterable<E>> whereAsync(Future<bool> Function(E element) test) async {
final result = <E>[];
await Future.forEach(this, (E item) async {
if (await test(item)) {
result.add(item);
}
});
return result;
}
}
You can now use it in fluent-style on any iterable type. (Assume the function validate() is an async function defined elsewhere):
final validItems = await [1, 2, 3]
.map((i) => 'Test $i')
.whereAsync((s) async => await validate(s));
Try this:
final result = turnOffTime.map((item) {
if(myFilter(item)) {
return item;
}
}).toList();
I have following class.
class Element {
Future<Element> findById(var id)async {
await networkRequest();
return this;
}
Futute<Element> click() async {
await networkRequest();
return this;
}
}
I want to achieve the something like.
main() async {
var element = Element();
await element.findyById("something").click();
}
But I'm not able to do so because element.findById() returns future. How can I chain these async methods.
While there's no special syntax to chain futures, there are two semantically equivalent ways to do what you want:
1) Two separate await calls:
await element.findById("something");
await click();
2) Chaining with then:
await element.findById("something").then(() => click());
Use this,
await (await Element().findById("1")).click();
final el = await element.findyById("something");
await el.click();