Iterating asynchronous operations in dart(using forEach and for..in loop)
Tags: dart, programming, mobile, flutter
Published: 2020-12-15 13:17:03

Most hight-level programming languages ships with iteration statements, dart is not an exception.
For one to handle highly scalable and efficient business logic using dart, a detailed knowledge of asynchronous operations, futures, loops among others is a can’t-do-without.

> In this article, We’ll focus specifically on the `for..in` and `forEach` loops as used in asynchronous operations.

Generally, both the `for-in` and `forEach` constructs are used to loop over iterables(List, Set, etc). Both have a return type of void. .i.e(a new value can not be returned from these methods), neither do they expose the current index of the iteration unlike the typical `for..loop`.

Enough of the general notes, let’s see how these constructs handle asynchronous operations in dart.

Take a closer look at the examples below:

```
List<Person> persons = [
  Person(
    name: "Emeruche U.K",  
    images:["ImageProfile", "My Image2"],
  ),
];


class Person{
   String name;
   List<String> images;
    
   Person({this.name, this.images});
}
```

```
void main() {
  Future uploadImages({images, name})async{
    return ["Image 1", "image 2"];
  }
  
  Future updateProfile(Person person) async{
    List<String> newImageUrls = [];
    person.images.forEach((image) async{
       if (image.runtimeType == String) {
        dynamic url = await uploadImages(images: [image], name: person.name);
        print("forEach URL: $url");
        newImageUrls.addAll(url);
      } else {
        newImageUrls.add(image);
      }
    });
    print("Profile Images f1: $newImageUrls");

    person..images = newImageUrls;
  }
  
  updateProfile(persons[0]);
  
}
```
From the above snippet, using the **`for..Each`** construct might seem like a natural choice when looping over a list using async/await. Typically, we expect the synchronous-reading magic of `async/await` to do what it says and actually `await` the promise. Often times this gives an unexpected output or even throw a **`ReferenceError`** if the result of the operation is being used somewhere else in the application (as seen in above code).

To fix this, we can either switch to the `for..in` construct or use the `Future.forEach` method. This should do the trick


```
 Future updateProfile2(Person person) async {
    List<String> newImageUrls = [];
    for (var image in person.images) {
      if (image.runtimeType == String) {
        dynamic url = await uploadImages(images: [image], name: person.name);
        print("for..in URL: $url");
        newImageUrls.addAll(url);
      } else {
        newImageUrls.add(image);
      }
    }
    print("Profile Images f2: ${newImageUrls}");
  
    person..images = newImageUrls;
  }
```
First, we changed the for..Each block to a for..in construct…why? Switching to a for..in block solved the problem because the `for..each` returns the `empty result set` first before processing the await function inside the forEach. It just applies the function on each element and calls next without awaiting for the asynchronous operation to return. But the for..in construct on the other hand, actually awaits the result of the execution of the function before calling next

```
Future updateProfile3(Person person) async {
    List<String> newImageUrls = [];
      
    await Future.forEach(person.images, (image)async{
       if (image.runtimeType == String) {
        dynamic url = await uploadImages(images: [image], name: person.name);
        print("Future.forEach URL: $url");
        newImageUrls.addAll(url);
      } else {
        newImageUrls.add(image);
      }
    });
    
    print("Profile Images f3: $newImageUrls");

    person..images = newImageUrls;
  }
```

The  `Future.forEach()` waits for each Future to be completed before moving to the next element.

>When you need to wait for multiple Futures to complete irrespective of their order, you can use Future.wait() method like this: 
	
```
Future.wait(person.images.map(functionCall));
```
 or
```
Future.wait([futureOne(), futureTwo(), …]).then((res) => functionToCallAfterResult(res));
```


**Error handling in asynchronous code**
Errors from your asynchronous code can be handled using the `try..catch` block in dart. Essentially, if an error is thrown within the try block, such can be received and handled with the catch block.