profile pic

Precise Asteroid

Welcome to Amir's Blog 👋

Dart Async

2022-01-22

Dart works with event loop with a single thread called isolate and does not support multi threading, just like node.js. It is possible to work asyncrounsly with Dart does allowing the app to continue and render widgests while it is still waiting for other activities to be completed, such as waiting for the user to key in data or waiting for the server to return data.

Syncronous Operation

Item Class

Here we allow creating a class where the id comes from a generator

class IdGenerator{
  static int _itemId = 0;
  static get ItemId => _itemId++;
}
class Item{ int itemId;
  
  Item(): itemId = IdGenerator.ItemId {}
  @override
  String toString(){
    return "Item ID: $itemId";
  }
}

we then print the items one after the other

void main() async {
  print("loading items...sync");
  for(int i = 0; i < 10; i++){
    print(Repository.returnMyItem());  
  }
  print("Done \n");
  }

The result is an ordered list

loading items...sync
Item ID: 0
Item ID: 1
Item ID: 2
Item ID: 3
Item ID: 4
Item ID: 5
Item ID: 6
Item ID: 7
Item ID: 8
Item ID: 9
Done     

Asyncronous Operation

Item

Here we use the keyword FUTURE to denote an object that will be returned at some point. Just like PROMISE in JS.

The code randomises a wait time, to imitate unpredictable server latency.

The item is generated (and assigned with id) before the random wait

static Future<Item> returnMyItemFuture() async{
  var rng = Random();
  Item myItem = Item();
  int randSeconds = rng.nextInt(3);
  await Future.delayed(Duration(seconds:randSeconds));
  return myItem;
}

Calling this function without the await keyword will print the items as they are returned. Adding await, will make sure they are printed in order.

print("loading items...async Future");
  for(int i = 0; i < 10; i++){
    await Repository.returnMyItemFuture().then(
    (item){
      print(item);
    });  
  }
  print("Done \n");
  

returnMyItemFuture() returns a future. There is therefore a need to call .then() to access the Item

Here is the result with an without the await

loading items...async Future with `await`
Item ID: 0
Item ID: 1
Item ID: 2
Item ID: 3
Item ID: 4
Item ID: 5
Item ID: 6
Item ID: 7
Item ID: 8
Item ID: 9
Done 
loading items...async Future without `await` Done
Item ID: 1
Item ID: 3
Item ID: 2
Item ID: 4
Item ID: 7
Item ID: 8
Item ID: 0
Item ID: 5
Item ID: 6
Item ID: 9

Asyncronous Operation

Item

One can compare Stream to List. Notices how yield is used instead of return as we don't know how many items will be returned.

Note how the loop is now located within the function in order to imitate a return of a Stream or List from the server

An important detail is the use of async* that is eqvivalent to writing async async async async .. and again for the same reason. we do not know how many items will be returned.

static Stream<Item> returnMyItemStream() async* {
  for(int i = 0; i < 10; i++){
    var rng = Random();
    Item myItem = Item();
    int randSeconds = rng.nextInt(3);
    await Future.delayed(Duration(seconds:randSeconds));
    yield myItem;  
  };
}

On the caller side instead of using .then we use .listen as again we don't know when will the Stream end.

print("loading items...stream");
    await Repository.returnMyItemStream().listen(
    (item){
      print(item);
    });  
  print("Done \n");
  

Asyncronous Operation

Item calls a another Stream

Finally it is possible for one stream to initiate another. For example when getting posts and comments on thse posts

static Stream<Item> returnMyItemStreams() async* {
  for(int i = 0; i < 10; i++){
    var rng = Random();
    Item myItem = Item();
    int randSeconds = rng.nextInt(3);
    await Future.delayed(Duration(seconds:randSeconds));
    yield* returnMyItemStream();  
  };
  
}

Here we return yeild* as again many items will be returned

Made by Amir 💚