“This is the 20th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

In today’s article we look at asynchronous programming in Dart;

Let’s start with a piece of code:

String _string = 'default';

void main() {
  getData();
  print('Other Business');
}

getData() {
  print('开始');
  for (int i = 0; i < 10000000000; i++) {
    _string = 'Time-consuming operation';
  }
  print('the end:$_string');
}
Copy the code

The output is as follows:

In this code, the time-consuming operation of the for loop blocks the execution of subsequent code;

So how do you want a for loop to become an asynchronous operation? This is where we need the Future;

Future

A Future, much like a Promise in JavaScript, represents the final completion of an asynchronous operation and a representation of its result. In a nutshell, a Future is designed to handle asynchronous operations. If an asynchronous operation succeeds, the operation succeeds, and if an asynchronous operation fails, an error is caught or subsequent operations are stopped. A Future can only correspond to one outcome, either success or failure;

Note that the return value of all Future apis is still a Future object, so we can easily make chain calls;

We modify the code as follows:

String _string = 'default';

void main() {
  getData();
  print('Other Business');
}

getData() {
  print('start:$_string');
  Future(() {
    for (int i = 0; i < 10000000000; i++) {
      _string = 'Time-consuming operation';
    }
    print('the end:$_string');
  });
}
Copy the code

Running results:

The results show that after putting a for loop into a Future, time-consuming operations do not block the execution of the code;

await/async

There is also a way of Flutter that allows us to perform asynchronous tasks as if we were writing synchronous code without using callbacks. This is async/await.

String _string = 'default';

void main() {
  getData();
  print('Other Business');
}

getData() async {
  print('start:$_string');
  await Future(() {
    for (int i = 0; i < 1000000000; i++) {
      _string = 'Network data';
    }
    print('Get data:$_string');
  });
  print('the end:$_string');
}
Copy the code

In this code, the getData method uses the async flag and the Future inside the method where the for loop resides is await.

As you can see, code outside the Future will wait until the code in the Future is finished executing;

So what do sync and await do?

  • asyncUsed to indicate that a function is asynchronous, and the defined function returns oneFutureObject that can be usedthenMethod to add a callback function;
  • awaitAnd then there’s aFuture, indicates that the asynchronous task will be executed only after the asynchronous task is complete.awaitMust be present atasyncInside the function;

As you can see, we represent an asynchronous stream call with synchronous code logic by using async and await;

Async and await are just syntactic sugar that the compiler or interpreter will eventually convert to a Future call chain;

then

So can we achieve the same effect without using await?

The return value of the Future method is still a Future object, which we use to receive the return value; Then print the returned data with future.then: We modify the getData method as follows:

getData() async {
  print('start:$_string');
  Future future = Future(() {
    for (int i = 0; i < 1000000000; i++) {
      _string = 'Network data';
    }
    print('Get data:$_string');
  });
  future.then((value) {
    print('then method:$_string');
  });
  print('the end:$_string');
}
Copy the code

Let’s have a look at the print:

We can see that asynchronous task data can still be obtained from the then method of the Future object, even though there is no await identifier.

But the value in the then method is empty:

This is because the asynchronous task captured in the Future has no return value, so we add a return value to the asynchronous task. We modify the getData method as follows:

getData() async {
  print('start:$_string');
  Future future = Future(() {
    for (int i = 0; i < 1000000000; i++) {
      _string = 'Network data';
    }
    print('Get data:$_string');
    return 'Return data';
  });
  future.then((value) {
    print('then method:$_string, value: $value');
  });
  print('the end:$_string');
}
Copy the code

Let’s look at the print again:

At this point, the value returned by the then method already has a value, which is the value returned by the return in the asynchronous task. The data returned in the Future is wrapped in the object of the Future, and the object of the Future is returned;

catchError

In many cases, when we do network processing, we will throw exceptions, so how should we handle exceptions in the Future?

Let’s look at the following code:

getData() async {
  print('start:$_string');
  Future future = Future(() {
    for (int i = 0; i < 1000000000; i++) {
      _string = 'Network data';
    }
    throw Exception('Network exception');
  });
  future.then((value) {
    print('then method:$_string, value: $value');
  });
  print('the end:$_string');
}
Copy the code

Inside the Future, an Exception is thrown by a throw. Let’s look at the result:

As you can see, the code has an error during execution, throwing a network exception error message; Catch Exception (catchError); catch Exception (catchError);

Running results:

We did catch an exception, but the project still reported an error. Why? After the then method, use the chained call directly to catch the exception as follows:

Running results:

onError

Then is defined as follows:

Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});
Copy the code

We find that there is an onError method in the then method, which can also be used to handle errors and exceptions:

Running results:

As you can see, catchError will not be called after an error is caught using the onError method in then;

The onError method here is defined inside the THEN method, so it needs to be called inside the THEN method, not horizontally;

CatchError is an exception caught in a chain call to the entire Future, whereas onError is only handled in the current THEN; We may have more than one THEN in a chain call;

We swap the order of catchError with the then method:

Running results:

If catchError precedes the THEN method, the then method will execute even if an exception is caught in the catchError.

whenComplete

Sometimes, when our asynchronous task succeeds or fails and needs to do something else, there are two ways to handle it *

  • inthenorcatchErrorIn the processing;
  • useFuturethewhenCompleteCallback processing;

We modify the code as follows:

Running results:

Although our whenComplete state is executed, the exception is still thrown; There are two ways to handle this:

  • whenCompleteUse chain call mode, do not useFutureObject call;
  • inwhenCompleteAfter that, the exception is caught again with the following code:

We typically use procedures to place the THEN method first, so that when an exception is caught, the THEN method will not execute:

When used in projects, we recommend the following:

It is recommended to extract the logical processing methods of each state as follows: