目录
[1. Future 和 async/await](#1. Future 和 async/await)
[2.已完成(Completed with a value)](#2.已完成(Completed with a value))
[3.已失败(Completed with an error)](#3.已失败(Completed with an error))
[3.使用 async/await](#3.使用 async/await)
[2. Stream](#2. Stream)
前言
在Flutter应用程序开发中,异步编程是必不可少的。异步编程使应用程序能够在不阻塞主线程的情况下执行耗时操作,如网络请求、文件读取和数据库访问。本文将介绍Flutter中常用的异步编程方法及其最佳实践。
1. Future 和 async/await
1.Future
1.什么是Future?
在Flutter中,Future是一个用于表示异步操作结果的类。它代表一个在将来某个时刻会完成的计算,类似于其他编程语言中的Promise。Future非常适合处理那些需要一些时间才能完成的操作,例如网络请求、文件读取或其他耗时任务。
2.Flutter的三种状态
1.未完成(Uncompleted)
1.定义
表示异步操作尚未完成。在未完成状态的时候,Future尚未提供结果,也没有抛出错误。
当你调用一个异步函数时,它会返回一个未完成的 Future。该 Future 正在等待函数的异步操作完成或抛出错误。
以下是一个创建和使用Future的示例,展示Future在未完成状态时的行为:
Dart
Future<String> fetchData() {
// 创建一个延迟两秒后完成的Future
return Future.delayed(Duration(seconds: 2), () => 'Data fetched');
}
void main() {
print('Fetching data...');
fetchData().then((result) {
print(result);
}).catchError((error) {
print('Error: $error');
});
print('Waiting for data...');
}
控制台输入信息如下:
Fetching data...
Waiting for data...
Data fetched
这里可以看到,在调用fetchData之后和Future完成之前,控制台输出了"Waiting for data..."。这表示在这段时间内,Future处于未完成状态。
2.处理未完成的Future
通常,我们使用then方法来处理Future完成时的结果,使用catchError方法来处理Future失败时的错误。
此外,可以使用await关键字在异步函数中等待Future完成:
Dart
Future<void> fetchDataAndPrint() async {
print('Fetching data...');
try {
String result = await fetchData();
print(result);
} catch (error) {
print('Error: $error');
}
print('Data fetching completed.');
}
void main() {
fetchDataAndPrint();
}
执行这个代码时,控制台的输出如下:
Fetching data...
Data fetched
Data fetching completed.
2.已完成(Completed with a value)
1.概念
已完成状态表示异步操作成功完成,并返回了结果。
2.处理已完成的Future
当Future进入已完成状态时,我们可以使用then方法来处理成功的结果,使用catchError方法来处理失败的错误。另外,可以使用async/await来简化异步代码。
以下是一个创建和使用Future的示例,展示如何处理已完成状态的Future:
Future<String> fetchData() {
// 创建一个延迟两秒后完成的Future
return Future.delayed(Duration(seconds: 2), () => 'Data fetched');
}
void main() {
print('Fetching data...');
fetchData().then((result) {
// 当Future完成时,处理结果
print(result);
}).catchError((error) {
// 当Future失败时,处理错误
print('Error: $error');
});
print('Waiting for data...');
}
执行这段代码时,控制台的输出如下:
Fetching data...
Waiting for data...
Data fetched
在上面的实例中:
1.fetchData函数返回一个在两秒后完成的Future,并返回字符串"Data fetched"。
2.使用then方法处理Future完成时的结果,输出"Data fetched"。
3.使用catchError方法处理Future失败时的错误(示例中未发生错误)。
3.使用async/await
async/await语法使得处理异步操作更加直观和简洁。以下是同样功能的代码,使用async/await:
Future<void> fetchDataAndPrint() async {
print('Fetching data...');
try {
// 使用await等待Future完成
String result = await fetchData();
print(result);
} catch (error) {
print('Error: $error');
}
print('Data fetching completed.');
}
void main() {
fetchDataAndPrint();
}
执行这个代码时,控制台的输出如下:
Fetching data...
Data fetched
Data fetching completed.
在这个示例中:
1.使用await关键字等待Future完成,并获取其结果。
2.try块用于捕获可能的错误,并在catch块中处理错误。
4.Future的已完成状态的处理
在Flutter的完成状态中,有失败和成功两种情况。
1.对于成功完成的Future,使用then方法处理完成的Future,或者在async函数中是用await。
2.对于失败完成的Future,使用catchError方法处理失败完成的Future,或者在async函数中使用try/catch。
3.已失败(Completed with an error)
1.概念
Future的失败状态表示该异步操作已经完成,但由于某种原因失败,并抛出了一个错误。
2.处理失败状态的Future
当Future进入失败状态时,可以使用catchError方法来处理失败的错误,或者在async/await语法中使用try/catch块来捕获和处理错误。
以下是一个创建和使用Future的示例,展示如何处理失败状态的Future:
Future<String> fetchData() {
// 创建一个延迟两秒后失败的Future
return Future.delayed(Duration(seconds: 2), () => throw 'Failed to fetch data');
}
void main() {
print('Fetching data...');
fetchData().then((result) {
// 当Future成功完成时,处理结果
print(result);
}).catchError((error) {
// 当Future失败时,处理错误
print('Error: $error');
});
print('Waiting for data...');
}
执行这段代码时,控制台的输出如下:
Fetching data...
Waiting for data...
Error: Failed to fetch data
在上面这个示例中:
-
fetchData函数返回一个在两秒后失败的Future,并抛出字符串"Failed to fetch data"。
-
使用then方法处理Future成功完成时的结果(在此示例中不会被调用)。
-
使用catchError方法处理Future失败时的错误,输出"Error: Failed to fetch data"。
3.使用 async/await
async/await语法使得处理异步操作的失败更加直观和简洁。以下是同样功能的代码,使用async/await:
Future<void> fetchDataAndPrint() async {
print('Fetching data...');
try {
// 使用await等待Future完成
String result = await fetchData();
print(result);
} catch (error) {
// 捕获并处理错误
print('Error: $error');
}
print('Data fetching completed.');
}
void main() {
fetchDataAndPrint();
}
执行这个代码时,控制台的输出如下:
Fetching data...
Error: Failed to fetch data
Data fetching completed.
在这个示例中:
-
使用await关键字等待Future完成,并获取其结果。
-
try块用于捕获可能的错误,并在catch块中处理错误。
4.捕获和处理错误的最佳实践
理解和处理Future的失败状态是Flutter异步编程的重要部分。通过使用catchError和try/catch语法,可以有效地捕获和处理异步操作中的错误,从而编写更健壮和稳定的Flutter应用程序。
1.始终捕获错误
无论使用then和catchError还是async/await,始终确保捕获并处理Future中的错误,以避免未处理的异常。
2.提供用户反馈
在用户界面上提供适当的反馈,如显示错误消息或提示用户重试操作。
3.记录错误
在开发和调试过程中,记录错误信息有助于查找和修复问题。
3.创建Future
创建一个Future非常简单,可以使用Future构造函数、Future.delayed、Future.value和Future.error等方法。
1.使用Future构造函数
Dart
Future<String> fetchData() {
return Future(() {
return 'Data fetched';
});
}
void main() {
fetchData().then((data) {
print(data);
});
}
2.使用Future.delayed
Dart
Future<String> fetchData() {
return Future.delayed(Duration(seconds: 2), () {
return 'Data fetched';
});
}
void main() {
fetchData().then((data) {
print(data);
});
}
3.使用Future.value和Future.error
Dart
void main() {
Future.value('Immediate data').then((data) {
print(data);
});
Future.error('An error occurred').catchError((error) {
print(error);
});
}
4.处理Future
处理Future通常有两种方式:使用then和catchError方法,或者使用async/await语法。
1.使用then和catchError
Dart
Future<String> fetchData() {
return Future.delayed(Duration(seconds: 2), () {
return 'Data fetched';
});
}
void main() {
fetchData().then((data) {
print(data);
}).catchError((error) {
print('Error: $error');
});
}
2.使用async/await
Dart
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return 'Data fetched';
}
void main() async {
try {
String data = await fetchData();
print(data);
} catch (error) {
print('Error: $error');
}
}
3.Future的链式调用
多个Future可以通过链式调用连接在一起,从而依次处理多个异步操作。
Future<void> fetchData() {
return Future.delayed(Duration(seconds: 2), () {
print('Step 1: Data fetched');
}).then((_) {
return Future.delayed(Duration(seconds: 1), () {
print('Step 2: Additional data fetched');
});
}).then((_) {
print('Step 3: All steps completed');
});
}
void main() {
fetchData();
}
2. Stream
在Flutter中,Stream用于表示一系列异步数据事件。与Future不同,Stream可以提供多个值而不是单个值。它非常适合处理诸如用户输入、实时数据更新和传感器数据等连续数据流。本文将详细介绍Flutter中`Stream`的基本概念和用法。
1.Stream的两种类型
1.单订阅Stream(Single-subscription Stream):只能被单个监听器(listener)监听。这种Stream适用于大多数情况。
2.广播Stream(Broadcast Stream):可以被多个监听器同时监听。这种Stream适用于需要多个订阅者的情况,如事件总线(Event Bus)。
2.创建Stream
创建Stream有多种方式,常见的包括使用StreamController和异步生成器函数。
1.使用StreamController
StreamController用于创建和管理Stream。
Dart
void main() {
// 创建一个StreamController
final controller = StreamController<int>();
// 获取Stream
final stream = controller.stream;
// 监听Stream
stream.listen((data) {
print('Data: $data');
}, onDone: () {
print('Stream closed');
});
// 添加数据到Stream
controller.add(1);
controller.add(2);
controller.add(3);
// 关闭Stream
controller.close();
}
执行这个代码时,控制台的输出如下:
Data: 1
Data: 2
Data: 3
Stream closed
2.使用异步生成器函数
可以使用async*和yield关键字创建Stream。
Dart
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() {
final stream = countStream(5);
stream.listen((data) {
print('Data: $data');
}, onDone: () {
print('Stream closed');
});
}
执行这个代码时,控制台的输出如下:
Data: 1
Data: 2
Data: 3
Data: 4
Data: 5
Stream closed
3.监听Stream
使用listen方法可以监听Stream,并处理接收到的数据和错误。
Dart
stream.listen(
(data) {
print('Data: $data');
},
onError: (error) {
print('Error: $error');
},
onDone: () {
print('Stream closed');
},
cancelOnError: false, // 当为true时,接收到错误后将取消订阅
);
4.使用StreamBuilder
在Flutter中,StreamBuilder小部件用于根据Stream构建UI。它会监听Stream并在每次接收到数据时重新构建UI。
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('StreamBuilder Example'),
),
body: Center(
child: StreamBuilder<int>(
stream: countStream(5),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else if (!snapshot.hasData) {
return Text('No Data');
} else {
return Text('Count: ${snapshot.data}');
}
},
),
),
);
}
}
void main() => runApp(MaterialApp(home: MyHomePage()));
在这个示例中,StreamBuilder监听countStream,并根据Stream的状态更新UI。
5.Stream的高级用法
1.转换Stream
使用Stream的转换方法可以方便地处理数据,例如使用map方法转换数据:
Dart
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() {
final stream = countStream(5).map((data) => data * 2);
stream.listen((data) {
print('Transformed Data: $data');
});
}
2.合并Stream
可以使用StreamGroup来合并多个Stream:
Dart
void main() {
final stream1 = Stream.fromIterable([1, 2, 3]);
final stream2 = Stream.fromIterable([4, 5, 6]);
final mergedStream = StreamGroup.merge([stream1, stream2]);
mergedStream.listen((data) {
print('Merged Data: $data');
});
}
6.总结
Stream在Flutter中是处理异步数据事件的强大工具。通过理解和使用StreamController、异步生成器函数以及StreamBuilder,可以有效地处理和显示连续的数据流。希望本文能帮助你更好地理解和使用Flutter中的Stream。
Future 表示不会立即完成的计算。普通函数会返回结果,而异步函数则会返回 Future,后者最终会包含结果。Future 会告诉您结果何时准备就绪。
Stream(流)是一系列异步事件。它类似于异步 Iterable --- 流不会在您请求时获取下一个事件,而是在事件准备就绪时告诉您有事件发生。
3.FutureBuilder和StreamBuilder
FutureBuilder和StreamBuilder是Flutter中用于处理异步数据的两个常用小部件。
1.FutureBuilder
FutureBuilder用于构建依赖于Future的widget。
Dart
FutureBuilder<String>(
future: fetchUserOrder(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text('Order: ${snapshot.data}');
}
},
);
2.StreamBuilder
StreamBuilder用于构建依赖于Stream的widget。
Dart
StreamBuilder<int>(
stream: countStream(5),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else if (!snapshot.hasData) {
return Text('No Data');
} else {
return Text('Count: ${snapshot.data}');
}
},
);