The dart:async library delivers core capabilities for asynchronous programming in Dart: Future, Stream.
- Using
Future / Streamdoes not always require manually importing dart:async; these APIs are already exported via dart:core.- You must import the library explicitly to work with types such as
StreamController,StreamSubscription,ParallelWaitError:
import 'dart:async';Code language: JavaScript (javascript)
Two foundational concepts:
- Future: Represents a single-shot asynchronous result (emits exactly one value or one error at some point in the future)
- Stream: Represents a continuous sequence of asynchronous data (produces multiple values over time, similar to an event stream)
Future
1. async / await
Recommended by convention; offers better readability compared to .then() chaining
- Any function marked
asyncreturns a Future; awaitpauses execution until the Future completes and yields its result;- Use
try-catchblocks to handle asynchronous errors.
// async functions return Future
Future<String> findEntryPoint() async => "main.dart";
Future<int> runExecutable(String entry, List args) async => 0;
Future<void> flushThenExit(int code) async {}
// await syntax (preferred)
Future<void> runUsingAsyncAwait() async {
try {
var entryPoint = await findEntryPoint();
var exitCode = await runExecutable(entryPoint, []);
await flushThenExit(exitCode);
} catch (e) {
// Catch all errors uniformly
print("Error: $e");
}
}Code language: JavaScript (javascript)
2. Traditional chaining: .then() / .catchError()
.then(): Runs a callback once the Future resolves successfully. It returns a new Future to support continuous chaining.catchError(): Catches thrown errors; must be chained after the return value of then(), otherwise errors inside then() cannot be captured
void runUsingFuture() {
findEntryPoint()
.then((entryPoint) {
return runExecutable(entryPoint, []);
})
.then(flushThenExit)
.catchError((e) {
print("Exception: $e");
});
}Code language: JavaScript (javascript)
3. Awaiting multiple concurrent Futures
① Future.wait()
Waits for every task to finish; the whole operation fails immediately if any single Future throws an error
Future<void> task1() async {}
Future<void> task2() async {}
Future<void> task3() async {}
void testWait() async {
await Future.wait([
task1(),
task2(),
task3(),
]);
print("All tasks completed");
}Code language: JavaScript (javascript)
② List.wait / Record.wait (new feature, fine-grained concurrent error handling)
Throws a ParallelWaitError, letting you identify which operations succeeded and which failed without mutual interruption.
Future<int> delete() async => 1;
Future<String> copy() async => "ok";
Future<bool> errorTask() async {
throw Exception("Task failed");
}
void main() async {
try {
// List variant
var results = await [delete(), copy(), errorTask()].wait;
} on ParallelWaitError<List<Object?>, List<AsyncError?>> catch (e) {
print(e.values); // Contains values for successful tasks, null for failures
print(e.errors); // Null for successes, stores error details for failures
}
// Record tuple variant: supports Futures of different types
try {
final (resInt, resStr, resBool) = await (delete(), copy(), errorTask()).wait;
} on ParallelWaitError<(int?, String?, bool?), (AsyncError?, AsyncError?, AsyncError?)> catch(e) {
}
}Code language: JavaScript (javascript)
Stream
A Stream represents a continuous sequence of asynchronous data (examples: line-by-line file reading, button click events, network data streams).
Two common usage patterns
1: await for asynchronous for-loop (concise, similar to synchronous iteration)
Suitable for finite data streams; avoid for unbounded ongoing event streams (such as UI click events) as it blocks subsequent code execution.
import 'dart:io';
Future<void> readFileByAwaitFor() async {
var file = File("config.txt");
Stream<List<int>> input = file.openRead();
var lines = input
.transform(utf8.decoder)
.transform(const LineSplitter());
try {
await for (final line in lines) {
print("Read line: $line");
}
print("File reading finished");
} catch (e) {
print("Read error $e");
}
}Code language: PHP (php)
2: .listen() subscription listening (for unbounded event streams)
inputStream.listen(
(data) {
// Callback triggered on incoming data
},
onError: (err) {
// Triggered when an error occurs
},
onDone: () {
// Runs when the stream closes normally
},
);Code language: JavaScript (javascript)
listen() returns a
StreamSubscription, which you can terminate by calling.cancel().
Frequently used Stream operators
- Filter:
where() - Take elements:
take(),takeWhile() - Skip elements:
skip(),skipWhile() - Fetch single element:
first,last,single,firstWhere()
Stream data transformation with transform()
Use transformers to alter the data type of a stream (typical use case: convert binary streams to text strings)
var lines = inputStream
.transform(utf8.decoder) // List<int> → String
.transform(const LineSplitter()); // String → individual text linesCode language: JavaScript (javascript)
Choosing between listen() and await for
- File I/O, finite data sequences →
await for - UI events, continuous push messages and other unbounded streams →
.listen() - Once started, await for waits indefinitely until stream termination; unbounded event streams cause code to hang
dart:async library