什么'异步和异步*在DART中的区别是什么?

我正在使用Flutter框架制作一个应用程序。 在此期间,我遇到了DART中的关键字asyncasync*。 谁能告诉我它们之间有什么区别?

38798 次浏览

Marking a function as async or async* allows it to use the async/await for a Future.

The difference between both is that async* will always return a Stream and offer some syntactical sugar to emit a value through the yield keyword.

We can therefore do the following:

Stream<int> foo() async* {
for (int i = 0; i < 42; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}

This function emits a value every second, which increments every time.

Short answer

  • async gives you a Future
  • async* gives you a Stream.

async

You add the async keyword to a function that does some work that might take a long time. It returns the result wrapped in a Future.

Future<int> doSomeLongTask() async {
await Future.delayed(const Duration(seconds: 1));
return 42;
}

You can get that result by awaiting the Future:

main() async {
int result = await doSomeLongTask();
print(result); // prints '42' after waiting 1 second
}

async*

You add the async* keyword to make a function that returns a bunch of future values one at a time. The results are wrapped in a Stream.

Stream<int> countForOneMinute() async* {
for (int i = 1; i <= 60; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}

The technical term for this is asynchronous generator function. You use yield to return a value instead of return because you aren't leaving the function.

You can use await for to wait for each value emitted by the Stream.

main() async {
await for (int i in countForOneMinute()) {
print(i); // prints 1 to 60, one integer per second
}
}

Going on

Watch these videos to learn more, especially the one on Generators:

Async functions execute synchronously until they reach the await keyword. Therefore, all synchronous code within an async function body executes immediately.

Future<int> foo() async {
await Future.delayed(Duration(seconds: 1));
return 0;
}

Async* is used to create a function that returns a bunch of future values one at a time. Each result is wrapped in a Stream.

Stream<int> foo() async* {
for (var i = 0; i < 10; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}

}

Solution, Origins and Insights

This answer includes simplified and easy to understand examples

async

The async computation cannot provide a result immediately when it is started because the program may need to wait for an external response like:

  • Reading a file
  • Querying a database
  • Fetching data from an API

Instead of blocking all computation until the result is available, the asynchronous computation immediately returns a Future object which will eventually "complete" with the result.

Example (This type of async call can be used only without returning a response):

void main() async {
// The next line awaits 5 seconds
await Future.delayed(Duration(seconds: 5));
// Pseudo API call that takes some time
await fetchStocks();
}

Future

A Future represents a computation that doesn’t complete immediately. Whereas 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 is appended when the async function returns a value
  • Represents the result of a single computation (in contrast to a Stream)

Example:

Future<String> fetchUserOrder() =>
// Imagine that this function is more complex and slow.
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);


void main(List<String> arguments) async {
var order = await fetchUserOrder();
// App awaits 2 seconds
print('Your $order is ready');
}

Stream

A source of asynchronous data events. A Stream provides a way to receive a sequence of events. Each event is either a data event, also called an element of the stream.

  • Stream is a sequence of results
  • From stream you get notified for results (stream elements)

async* (streams)

async* is an asynchronous generator that returns a Stream object. Made to create streams.

An example of using a stream and async*:

// Creating a new stream with async*
// Each iteration, this stream yields a number
Stream<int> createNumberStream(int number) async* {
for (int i = 1; i <= number; i++) {
yield i;
}
}


void main(List<String> arguments) {
// Calling the stream generation
var stream = createNumberStream(5);
// Listening to Stream yielding each number
stream.listen((s) => print(s));
}

Result:

1
2
3
4
5

Bonus: Transforming an Existing Stream

If you already have a stream, you can transform it to a new stream based on the original stream’s events.

Example (same code as before but with a twist):

Stream<int> createNumberStream(int number) async* {
for (int i = 1; i <= number; i++) {
yield i;
}
}


// This part is taking a previous stream through itself and outputs updated values
// This code multiplies each number from the stream
Stream<int> createNumberDoubling(Stream<int> chunk) async* {
await for (final number in chunk) {
yield number*2;
}
}


void main(List<String> arguments) {
// Here we are Transforming the first stream through createNumberDoubling stream generator
var stream = createNumberDoubling(createNumberStream(5));
stream.listen((s) => print(s));
}

Result:

2
4
6
8
10

Solution

The async and async* are close relatives, they are even from the same library dart:async The async represent a Future and a one-time exchange while the async* represents a Stream, a stream of multiple events

async* will always return a Stream

Stream<int> mainStream(int value) async* {
for (int i = 1; i <= value; i++) {
yield i;
}
}

async returns the result wrapped in the Future. So it might take longer time. See the below example:

void main() async {
// The next line awaits 10 seconds
await Future.delayed(Duration(seconds: 10));
}