DEV Community

Cover image for Asynchronous Functions in Dart
Mathieu K
Mathieu K

Posted on

Asynchronous Functions in Dart

In a previous post synchronous functions and iterators have been explained. Those functions are blocking, and in our daily busy life, we don't want our programs to block, we want a smooth experience. Well, if you are coding in C or C++, you should probably know pthreads, a library used to create threads, a way to create independent computation concurrently. The same exist in many other languages with sometimes different name, but doing mostly the same with just an interface and cool features.

Dart is working a bit like JavaScript, it uses only one thread to do all the computation by default, but this "thread" can be split in many small part allowing the illusion of concurrent or parallel computation. To make that possible, one need to create asynchronous functions, marked with async or async* keywords.

A function is asynchronous if its body is marked with the async or async* modifier. Otherwise the function is synchronous.

-- Dart Language Specification, Chapter 9, page 23

What does it really mean? Well, like the very great Dart documentation part about Concurrency explains, when a function is asynchronous, the event loop will listen for some changes in "background". When the function returns some values, the event loop receive it and return it to the caller. Let have a look on async keyword.

If f is marked async, then a fresh instance is associated with the invocation, where the dynamic type of o implements Future<T>, where T is the actual type corresponding to the future value type of f.

-- Dart Language Specification, Chapter 17, Section 15, page 141

In short, adding async when declaring a new function will mark it as an asynchronous function returning a Future object.

Future<int> async_int_addition(int x, int y) async {
  return x + y;
}

void main() async {
  print(async_int_addition(1, 2);
  print(await async_int_addition(1, 2));
}
Enter fullscreen mode Exit fullscreen mode
$ dart run bin/async.dart 
Instance of 'Future<int>'
3
Enter fullscreen mode Exit fullscreen mode

If f is marked async*, then a fresh instances implementing Stream<U> is immediately returned, where U is the actual type corresponding to the element type of f.

-- Dart Language Specification, Chapter 17, Section 15, page 141

Stream<int> async_int_generator(int x, int y) async* {
  if (y<x) throw "y<y"; 
  while (x <= y) {
    yield x;
    x++;
  }
}

Future<int> main() async {
  await for (var i in async_int_generator(0, 10)) {
    print(i);
  }
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

The previous piece of codes are also using a specific keyword: await. Let check the specification to see what does this one is doing.

An await expression allows code to yield control until an asynchronous operation completes. [...] An await expression can only occur in a function which is declared asynchronous. The await identifier has has no special meaning in the context of a normal function [...] await(e) can be a valid function invocation in non asynchronous functions.

-- Dart Language Specification, Chapter 17, Section 34, page 182

This means if we are using asynchronous function and we want to get the result returned by it, one can use await to get it. Bonus point: the value returned will not be a Future anymore but the generic type defined. Here an example:

Future<int> t() async {
  return 1;
}

void main() async {
  print(t());
  print(await t());
}
Enter fullscreen mode Exit fullscreen mode

t() function is straightforward, it returns simply an integer from an asynchronous call. The main() function will print the returned value of this without await and with it.

$ dart run bin/await.dart
Instance of '_Future<int>'
1
Enter fullscreen mode Exit fullscreen mode

As you can see, the first one returns an instance of Future, the "reference" of the execution, while the second would wait until the asynchronous function is done and return an integer.

Asynchronous functions are more than critical in Dart environment and are the foundation of most of the ecosystem. Mastering this part is vital, and if the information provided in this article was not enough, one should probably wait for the next ones on Future and Stream classes. I underestimated the complexity of this part of the language and thought it could fit in one big post... It was not the case.

Anyway, here more links I can highly recommend to understand how concurrency programming is working with Dart:


Image Cover by Jose Ruales on Unsplash

Top comments (0)