NimreyCode

About

Dart Asynchronous programming — Futures

Asynchronous programming is a form of parallel programming that allows a unit of work to run separately from the primary application thread. When the work is complete, it notifies the main thread (as well as whether the work was completed or failed). There are numerous benefits to using it, such as improved application performance and enhanced responsiveness.

TLDR;

Problem

Dart code runs in a single thread of excution, if it’s blocked the entire program freezes.

Example

//..

var data = fetchDataFromSomeApi(); // Takes time while fetching data

doStuffWithData(data); // Manipulate the data

doOtherStuff(); // Do other stuff unrelated to the data

//..

Since fetchDataFromSomeApi() blocks, the remaining code runs only after fetchDataFromSomeApi() returns with the data, however long that takes.

This code runs line by line causing the code to halt and doOtherStuff() to run after the data fetching operation is finished (which is probably not what you want).

Solution

Asynchronous operations lets your program complete other work while waiting for other operation to finish.

Same example above but with an asynchronous approach

fetchDataFromSomeApi().then((data) {
 print('Fetched data');
 doStuffWithData(data);
});

print('Working on other stuff...');
doOtherStuff();

// Output:
// Working on other stuff...
// Fetched data

fetchDataFromSomeApi is non-blocking, meaning it lets other code run while fetching data. Thats why the top level print statement runs before the print statement in the callback function.

Futures

A Future represents a computation that doesn’t complete immediately. Where a normal function returns the result, an asynchronous function returns a Future, which will eventually contain the result. The future will tell you when the result is ready.

Future objects (futures) represent the results of asynchronous operations — processing or I/O to be completed later.

We can create a future simply like this

Future future = Future();

let’s define a function called f

String f() => 'work is done!';

and pass it to the future

Future<String> future = Future(f);

notice that the future takes the same type of the function _f return type _String.

for the purposes of this tutorial we passed a function that just returns a string.

This creates a future containing the result of calling f asynchronously with Timer.run.

If the result of executing f throws, the returned future is completed with the error.

If the returned value is itself a Future, completion of the created future will wait until the returned future completes, and will then complete with the same result.

If a non-future value is returned, the returned future is completed with that value.

then

let’s call then on the future and pass a function that takes the output of the asynchronous operation as an argument

future.then((String val) => print(val)); // work is done

we can simplify it by passing the print function only because it takes a string

future.then(print); // work is done

Error handling

To catch errors, use try-catch expressions in async functions (or use catchError()).

catchError

let’s imagine that our future throws an error at some point

Future future = Future(() => throw Error());

if we call then on the future without handling the error it will throw the error and stop the excution of our program

future.then(print); // Error: Error: Instance of 'Error'

let’s define a function that takes an error and handles it

void handleError(err) {
 print(‘$err was handled’);
}

then append catchError() to the future and pass handleError

future.then(print).catchError(handleError); // Error: Error: Instance of 'Error' was handled

this way the error is handled and the program keeps excuting.

Async, Await

To suspend execution until a future completes, use await in an async function (or use then()).

to use the await keyword it has to be inside an async function like this:

main() async {
 Future future = Future(() => ‘work is done’);
 String res = await future;
 print(res); // work is done
}

Notice that the main function is marked with the async keyword.

Any function marked with async is called asynchronously.

when we call a future with the await keyword the function excution is halted until the future returns a value or throws an error.

we can handle future errors using a try-catch expression:

main() async {
 Future future = Future(() => throw Error());
 try {
   print(await future);
 
 } catch (e) {
   print(‘$e was handled’); // Instance of 'Error' was handled
 }
}

Conclusion

  • Dart code runs in a single “thread” of execution.
  • Code that blocks the thread of execution can make your program freeze.
  • Future objects (futures) represent the results of asynchronous operations — processing or I/O to be completed later.
  • To suspend execution until a future completes, use await in an async function (or use then()).
  • To catch errors, use try-catch expressions in async functions (or use catchError()).

If you feel that I have missed something here please let me know, or if you have any question at all you can reach me on twitter.

Other useful resources

Asynchronous Programming: Futures

Subscribe to my newsletter!