SoFunction
Updated on 2025-04-12

Detailed explanation of the usage of Dart asynchronous programming generator and custom types

Asynchronous support

Today I will introduce the last section of Dart learning, including the use of asynchronously, generator syntax, and type alias.

There are a lot of returns in the Dart class libraryFutureorStreamFunctions of the object. These functions are calledAsynchronous functions: They will only return after setting up some time-consuming operations, such as IO operations. Instead of waiting for this operation to be completed.

at the same time,asyncandawaitKeywords support asynchronous programming, allowing you to write asynchronous code that is much like synchronous code.

Future

FutureWith JavaScriptPromiseVery similar, representing the final completion (or failure) of an asynchronous operation and its result value. Simply put, it is used to handle asynchronous operations. If the asynchronous processing is successful, it will be successful. If the asynchronous processing fails, it will catch errors or stop subsequent operations.A Future will only correspond to one result, either success or failure.

Note: FutureThe return value of all APIs is still oneFutureObject, so it can be easily chained calls.

For the convenience of the example, use it in this exampleCreated a delay task (the actual scenario will be a real time-consuming task, such as a network request), that is, return the result string after 2 secondshi world!, and then we are inthenReceive asynchronous results and print the results. The code is as follows:

 (new Duration(seconds: 2),(){
    return "hi world!";
 }).then((data){
    print(data);
 });

If an error occurs in an asynchronous task, you cancatchErrorCatch the error in the above example to:

 (new Duration(seconds: 2),(){
    //return "hi world!";
    throw AssertionError("Error");  
 }).then((data){
    //The execution will come here after success    print("success");
 }).catchError((e){
    //The execution failure will come here    print(e);
 });

thenThere is also an optional parameter for the methodonError, it can also catch exceptions:

 (new Duration(seconds: 2), () {
     //return "hi world!";
     throw AssertionError("Error");
 }).then((data) {
     print("success");
 }, onError: (e) {
     print(e);
 });

Sometimes, we encounter scenarios where we need to do something regardless of whether the asynchronous task execution is successful or failed, such as popping up a load dialog before a network request and closing the dialog box after the request is over. There are two ways to do this scenario. The first is tothenorcatchClose the dialog box, the second method is to use itFutureofwhenCompleteFor callback, we will change the above example:

(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //The execution will come here after success   print(data);
}).catchError((e){
   //The execution failure will come here   print(e);
}).whenComplete((){
   //It will come here regardless of success or failure});

useCan do multipleFutureThe subsequent operations will be performed only when departure is done at the same time, the same as in JavaScript()method.

Accept oneFutureArray parameters, only all in the arrayFutureIt will only be triggered after all execution is successfulthenSuccessful callback, as long as there is oneFutureIf the execution fails, an error callback will be triggered. Below, we pass the simulationTo simulate the asynchronous tasks that obtain two data. When both asynchronous tasks are successfully executed, the results of the two asynchronous tasks are spliced ​​and printed out. The code is as follows:

([
  //Return the result after 2 seconds  (new Duration(seconds: 2), () {
    return "hello";
  }),
  //Return the result after 4 seconds  (new Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});
/*
	 Finally, the result will be obtained in 4 seconds
 */

For more Future APIs, please check the documentation yourself.

async and await

Asynchronous functions are usedasyncFunction marked by modifier. Add to the functionasyncThe keyword will return it to a Future.

String lookUpVersion() => '1.0.0'; // Return to String
Future&lt;String&gt; lookUpVersion() async =&gt; '1.0.0'; // Return to Future<String>

Then we can useawaitKeywords directly accept a Future'sthenThe successful callback is just like in JavaScript:

task() async {
    try{
        String id = await login("alice","******");
        String userInfo = await getUserInfo(id);
        await saveUserInfo(userInfo);
        // Perform the next operation    } catch(e){
        // Error handling        print(e);   
    }  
}
  • asyncUsed to indicate that the function is asynchronous, the defined function will return aFutureObject, you can use the then method to add callback functions.
  • awaitBehind is oneFuture, means that the asynchronous task will be completed and will not go down after it is completed asynchronously. Notice,awaitMust appear inasyncinside the function.

Notice:The body of the function does not need to use Future's API. Dart will create if requiredFutureobject. If no useful value is returned, return itFuture<void>type.

Processing Stream

StreamIt is also used to receive asynchronous event data, andFutureThe difference is that it can receive the results of multiple asynchronous operations (success or failure). That is, when executing an asynchronous task, the result data or error exception can be passed by multiple triggering success or failure events.StreamIt is often used in asynchronous task scenarios that read data multiple times, such as network content download, file reading and writing, etc.

When you need to get the value from the Stream, there are two options:

useasyncand asynchronousforcycle(await for

Note:In useawait forBefore, make sure it makes the code clearer and really want to wait for all results of the stream. For example, it should not be used for UI eventsawait, because the UI framework sends endless streams of events.

asynchronousforThe loop takes the following form:

 await for (varOrType identifier in expression) {
   // Executes each time the stream emits a value.
 }

The value of the expression must have a Stream type. The execution process is as follows:

Wait for the stream to emit value.

implementforThe body of the loop and set the variable to the emitted value.

Repeat 1 and 2 until the stream is closed.

To stop listening to streams, you can usebreakorreturnstatement, the statement will jump outforLoop and unsubscribe from the stream.

If asynchronous is implementedforCompile-time error occurred while looping, please make sureawaitIn an asynchronous function. For example, to be in the applicationmain()Use asynchronous in functionsforcycle,main()The subject must be marked asasync

 Future main() async {
   // ...
   await for (var request in requestServer) {
     handleRequest(request);
   }
   // ...
 }

Use Stream API, as described in [Bootdown of Library]

 ([
   //Return the result after 1 second   (new Duration(seconds: 1), () {
     return "hello 1";
   }),
   // Throw an exception   (new Duration(seconds: 2),(){
     throw AssertionError("Error");
   }),
   //Return the result after 3 seconds   (new Duration(seconds: 3), () {
     return "hello 3";
   })
 ]).listen((data){
    print(data);
 }, onError: (e){
    print();
 }, onDone: (){
 
 });
 /*
  The above code will output in turn:
  I/flutter (17666): hello 1
  I/flutter (17666): Error
  I/flutter (17666): hello 3
  */

Generator

Consider using generator functions when it is necessary to generate a sequence of values ​​in a delay.

Dart supports two generator functions in built-in:

  • Synchronous generator: Returns Iterable object
  • Asynchronous generator: Returns Stream object

To implement a synchronous generator function, mark the function body assync*and useyieldStatement passes value:

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

To implement an asynchronous generator function, mark the function body asasync*and useyieldStatement passes value:

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

If the generator is recursive, you can useyield*To improve its performance:

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

Type definition

In Dart, functions are objects, just as strings and numbers are objects.typedeforfunction-typeProvides a type alias for the function that can be used when declaring fields and return types. When the function type is assigned to a variable,typedefKeep type information.

The following code is not usedtypedef

class SortedCollection {
  Function compare;
  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}
// Initial, broken implementation.
int sort(Object a, Object b) => 0;
void main() {
  SortedCollection coll = SortedCollection(sort);
  // All we know is that compare is a function,
  // but what type of function?
  assert( is Function);
}

In the above code,compareType information is lost when f is allocated.fThe type is(Object, Object)->int(int represents the return value type),certainly,compareThe type isFunction. If we change the code to use explicit names and retain type information, both developers and tools can use this information.

typedef Compare = int Function(Object a, Object b);
class SortedCollection {
  Compare compare;
  SortedCollection();
}
// Initial, broken implementation.
int sort(Object a, Object b) => 0;
void main() {
  SortedCollection coll = SortedCollection(sort);
  assert( is Function);
  assert( is Compare);
}

Notice:at present,typedefsOnly function types may change later.

becausetypedefJust alias, so they provide a way to check any function type. For example:

typedef Compare<T> = int Function(T a, T b);
int sort(int a, int b) => a - b;
void main() {
  assert(sort is Compare<int>); // True!
}

Metadata

Use metadata to provide additional information about the code. Metadata comments in characters@Start with a compilation constant (e.g.deprecated) reference or call to a constant constructor.

All dart codes can use two comments:@deprecated(Deprecated comments) and@override. There is a use here@deprecatedExample of comments:

class Television {
  /// _Deprecated: Use [turnOn] instead._
  @deprecated
  void activate() {
    turnOn();
  }
  /// Turns the TV's power on.
  void turnOn() {...}
}

You can define your own metadata annotations (that is, the effect similar to the decorator in JavaScript). Here is an example defining the @todo annotation with two parameters:

 library todo;
 class Todo {
   final String who;
   final String what;
   const Todo(, );
 }

There is a use here@todoExamples of comments:

 import '';
 
 @Todo('seth', 'make this do something')
 void doSomething() {
   print('do something');
 }

Metadata can appear before a library, class, type definition, type parameter, constructor, factory, function, field, parameter, or variable declaration, or before importing or exporting instructions. Metadata can be retrieved at runtime using reflection.

Use of core library

You can refer to the introduction in the official documentation:A tour of the core libraries

The above is a detailed explanation of the usage of Dart asynchronous programming generator and custom types. For more information about Dart asynchronous programming generator, please pay attention to my other related articles!