织梦网站tag自定义插件,制作apk的软件,石家庄网站建设哪里好,有哪些建设网站的大公司在 Flutter 中#xff0c;Future 和 Stream 都是用于处理异步操作的类#xff0c;它们都基于 Dart 的异步编程模型#xff0c;但是它们的使用场景和工作方式有所不同。以下是它们的区别以及各自适用的场景。 目录 一、Future1、基本使用2、异常处理1. catchError2. onError…在 Flutter 中Future 和 Stream 都是用于处理异步操作的类它们都基于 Dart 的异步编程模型但是它们的使用场景和工作方式有所不同。以下是它们的区别以及各自适用的场景。 目录 一、Future1、基本使用2、异常处理1. catchError2. onError3、catchError 和 onError 的区别4. 捕获多个错误5. 错误的传播 总结 二、Stream1、基本使用2、异常处理1. onError 处理器2. try-catch 语句3. 异常后流的状态4. 流的中断5. 恢复流6. 流异常的示例 总结 三、 结合使用 Future 和 Stream四、总结1、区别2、选择 Future 还是 Stream 四、疑问1、Stream 流是按照顺序执行的吗?1. 顺序性2. 顺序的保证 2、Stream async* 和 yield 的解释这三个必须要配套使用吗?不适用 yield 行吗?1. Stream 与 async*2. async* 和 yield 必须配合使用吗3. 不使用 yield 行不行4. 如何使用 async* 生成异步数据流总结 一、Future
1、基本使用
Future 是一个表示一个可能还未完成的异步操作的对象。它表示一个将来某个时间点会返回一个结果或错误的计算。
特点 Future 代表的是一个 单次 异步操作。只能返回一次结果或错误不会再有后续的值。在调用时它会返回一个 Future 对象可以通过 then 或 await 等方法获取结果。如果操作失败可以通过 catchError 或 onError 进行错误处理。 使用场景 当你需要等待一个单一的异步结果时使用 Future。比如从网络获取数据执行一个数据库查询或读取一个文件等一次性的操作。 示例代码
// 使用 Future 的示例
FutureString fetchData() async {// 模拟异步操作await Future.delayed(Duration(seconds: 2));return Data fetched successfully!;
}void main() async {try {String data await fetchData();print(data); // 输出 Data fetched successfully!} catch (e) {print(Error: $e);}
}在上面的示例中fetchData 返回一个 FutureString它表示一个将来完成的异步操作。使用 await 来等待这个操作完成并获得结果。
2、异常处理
1. catchError
catchError 是用于捕获和处理 Future 发生错误的一种方式。当 Future 执行失败时它会触发传给 catchError 的回调函数。这个回调函数可以接受错误和栈跟踪信息。
示例
Futureint divide(int a, int b) {return Future.delayed(Duration(seconds: 1), () {if (b 0) {throw Exception(Cannot divide by zero!);}return a ~/ b;});
}void main() {divide(10, 0).catchError((e) {print(Error: $e);});// Output: Error: Exception: Cannot divide by zero!
}在这个例子中当 b 为 0 时Future 会抛出一个 Exception并且 catchError 捕获并打印这个错误。
catchError 的用法细节
返回值的传递catchError 会继续执行 Future 链中的后续操作因此如果你想在错误发生时返回一个默认值可以在 catchError 中指定。
Futureint divide(int a, int b) {return Future.delayed(Duration(seconds: 1), () {if (b 0) {throw Exception(Cannot divide by zero!);}return a ~/ b;}).catchError((e) {print(Handled error: $e);return -1; // 返回默认值});
}void main() async {var result await divide(10, 0);print(Result: $result); // 输出: Handled error: Exception: Cannot divide by zero!// Result: -1
}2. onError
onError 是 Future 的另一种错误处理方式。它与 catchError 类似但它是 Future 构造函数的一部分通常用于直接在 Future 构造时附加错误处理。
示例
Futureint divide(int a, int b) {return Future.delayed(Duration(seconds: 1), () {if (b 0) {throw Exception(Cannot divide by zero!);}return a ~/ b;}).onError((error, stackTrace) {print(Caught an error: $error);return -1; // 返回默认值});
}void main() async {var result await divide(10, 0);print(Result: $result); // 输出: Caught an error: Exception: Cannot divide by zero!// Result: -1
}3、catchError 和 onError 的区别
catchError 是用于捕获在 Future 执行时抛出的异常它通常用于链式调用中捕获错误。onError 是 Future 的一种附加错误处理机制它将错误处理直接嵌入到 Future 构造中。
尽管 catchError 和 onError 都可以捕获错误并返回一个默认值或执行某些操作但 catchError 更灵活通常在复杂的异步链式操作中使用。
4. 捕获多个错误
如果你需要捕获多个错误可以将 catchError 或 onError 绑定到多个 Future 链条上。这样可以对不同类型的错误进行不同的处理。
示例
Futurevoid asyncFunction() {return Future.delayed(Duration(seconds: 1), () {throw Exception(Something went wrong!);});
}void main() {asyncFunction().catchError((e) {print(Caught error: $e);}).catchError((e) {print(Another handler for errors: $e);});// Output: Caught error: Exception: Something went wrong!
}5. 错误的传播
如果在 Future 中没有处理错误错误将会被传播直到被外部捕获或程序崩溃。因此适当的错误处理不仅可以帮助捕获问题还可以避免未捕获的异常导致程序崩溃。
总结
在 Dart 中catchError 和 onError 都可以用于处理异步操作中的错误。它们的主要区别在于用法和灵活性选择哪一个取决于你的代码结构和需求。
二、Stream
1、基本使用
Stream 是一个表示一系列异步事件的对象它允许你在未来的时间点接收多个值。
特点 Stream 代表的是 多个异步事件。它会按顺序提供一系列的结果可以是零个或多个通常用于处理实时数据流。可以是单向的也可以是广播流多个监听者可以订阅。你可以通过 listen 方法来监听事件流。Stream 还支持 await for 语法可以等待并处理每个事件。 使用场景 当你需要处理一个 数据流 或 多个值 时使用 Stream。比如处理实时数据如 WebSocket 数据流、用户输入事件流、文件变化等。 示例代码
// 使用 Stream 的示例
Streamint generateNumbers() async* {for (int i 0; i 5; i) {await Future.delayed(Duration(seconds: 1));yield i; // 每秒产生一个数字}
}void main() async {await for (var number in generateNumbers()) {print(number); // 输出0, 1, 2, 3, 4}
}在这个示例中generateNumbers 返回一个 Streamint它每秒返回一个整数。通过 await for 循环我们可以逐个接收流中的数据。
2、异常处理
当流中发生异常时有几种方式来处理这些异常使得流能够继续工作或适当地终止。
1. onError 处理器
如果你使用 Stream.listen 方法来监听流可以通过传入 onError 回调来处理流中的异常。当流抛出异常时onError 处理器会被触发。
Streamint generateNumbersWithError() async* {yield 1;yield 2;throw Exception(Something went wrong);yield 3; // 这一行永远不会执行
}void main() {generateNumbersWithError().listen((data) {print(Received: $data);},onError: (error) {print(Caught error: $error);},onDone: () {print(Stream is done);},);
}输出
Received: 1
Received: 2
Caught error: Exception: Something went wrong
Stream is done在这个例子中当 Stream 中的 Exception 被抛出时onError 回调会捕获并打印出错误信息。yield 3 后的代码不会执行因为流在抛出异常后被中断。
2. try-catch 语句
在异步生成器如 async*中你可以使用 try-catch 来捕获异常这可以防止异常导致流中断。
Streamint generateNumbersWithErrorHandled() async* {try {yield 1;yield 2;throw Exception(Something went wrong);yield 3; // 这一行不会执行} catch (e) {print(Caught error: $e);}
}void main() async {await for (var data in generateNumbersWithErrorHandled()) {print(Received: $data);}
}输出
Received: 1
Received: 2
Caught error: Exception: Something went wrong在这个例子中即使抛出异常Stream 仍然能够继续执行只是异常会被捕获并处理。
3. 异常后流的状态
当 Stream 抛出异常后流会进入错误状态并且不再发出任何数据除非你有合适的机制来恢复流。
如果流中的 onError 回调没有捕获异常流会直接终止。如果你在 Stream 中使用 try-catch 捕获了异常流可以继续正常工作继续发送后续的数据。
4. 流的中断
流的中断意味着流不再继续发出事件。中断的原因通常有以下几种
异常抛出如果流中的某个操作抛出了异常流会被中断后续的事件不会再触发。用户主动取消订阅如果你使用 StreamSubscription 来订阅流并主动调用 cancel()流也会中断。流结束如果流完成即没有更多的事件要发出流会进入完成状态。
5. 恢复流
如果你希望在流发生异常后恢复流的工作可以通过重新订阅流或使用一些复合的错误处理机制。
例如在监听流时使用 onError 捕获错误并在错误发生时重新启动流
Streamint generateNumbersWithError() async* {yield 1;yield 2;throw Exception(Something went wrong);yield 3;
}void main() {Streamint stream generateNumbersWithError();stream.listen((data) {print(Received: $data);},onError: (error) {print(Caught error: $error);// 重新启动流stream.listen((data) print(Retry received: $data),onError: (e) print(Retry error: $e),onDone: () print(Retry stream is done),);},onDone: () print(Stream is done),);
}在这种情况下流会在错误发生时重新启动。这允许你捕获错误并尝试恢复流的执行。
6. 流异常的示例
假设有一个 Stream 生成器它在生成某个事件时发生异常
Streamint generateNumbersWithError() async* {yield 1;yield 2;throw Exception(Unexpected error);yield 3; // 这行永远不会执行
}void main() async {try {await for (var number in generateNumbersWithError()) {print(Received: $number);}} catch (e) {print(Caught error: $e);}
}输出
Received: 1
Received: 2
Caught error: Exception: Unexpected error在这个例子中异常会导致流中断后续的事件不会被处理且异常被捕获。
总结
流中断当流遇到异常时流会进入错误状态并停止发出事件。异常处理你可以通过 onError 回调或 try-catch 语句捕获和处理异常。恢复流在流发生异常时可以选择恢复流的工作例如通过重新订阅流。
三、 结合使用 Future 和 Stream
在某些情况下Future 和 Stream 可以结合使用。例如如果你有一个 Future 返回一个数据集而这个数据集可以被逐步处理那么你可以将 Future 的结果转换成一个 Stream 来进行逐项处理。
FutureListint fetchData() async {return [1, 2, 3, 4, 5];
}Streamint fetchDataAsStream() async* {Listint data await fetchData();for (var item in data) {yield item;}
}void main() async {await for (var number in fetchDataAsStream()) {print(number); // 输出 1, 2, 3, 4, 5}
}在这个例子中fetchData 是一个 Future而 fetchDataAsStream 将其转换成了一个 Stream使得我们能够以流的形式逐项处理数据。
总结
Future 适合用于处理 单次 的异步操作返回一个值或错误。Stream 适合用于处理 多次 的异步事件或数据流允许你持续接收多个值。
了解它们的区别和使用场景可以帮助你更好地选择异步操作的方式从而提高代码的可读性和性能。
四、总结
1、区别
特性FutureStream返回值只返回一个值或一个错误持续返回多个值或错误生命周期一次性操作完成后不再有新的值持续发出多个值或者是事件异步操作适用于单次异步操作适用于多次异步事件或数据流操作方式then、catchError、awaitlisten、await for、add、addError
2、选择 Future 还是 Stream
使用 Future 你在处理一个单次的异步操作时。比如从网络获取数据、计算结果、执行数据库操作等。示例登录请求、获取单个 API 响应、读取文件内容。 使用 Stream 当你需要处理 多个异步事件例如实时数据流或变化时。比如 WebSocket 消息、实时位置更新、文件读取大文件分片等。示例实时聊天消息、连续的数据更新、传感器数据流等。
四、疑问
1、Stream 流是按照顺序执行的吗?
是的Stream 在 Dart 中是 按顺序 执行的。具体来说Stream 中的事件数据或错误会按照它们被 产生 或 发出 的顺序进行传递和处理。
1. 顺序性
在 Dart 中Stream 会按照 事件发出的顺序 将这些事件传递给订阅者。也就是说先发出的事件会先被监听器处理。
例如如果你有一个 Stream 生成数据流并且在每个数据项之间有延时那么监听器将会按照数据产生的顺序处理每个事件。
示例代码顺序执行的 Stream
Streamint generateNumbers() async* {for (int i 0; i 3; i) {await Future.delayed(Duration(seconds: 1)); // 模拟延时yield i; // 按顺序发出数据}
}void main() async {await for (var number in generateNumbers()) {print(Received: $number); // 按顺序输出}
}输出
Received: 0
Received: 1
Received: 2在这个例子中数据流是按顺序传递给监听器的即 0、1、2 按照生成的顺序被逐个输出。
2. 顺序的保证
Stream 本身的设计保证了事件的顺序。无论你是通过 await for 或者 listen 来处理事件事件会按照发出顺序依次传递给你。
通过 listen 监听的顺序
Streamint generateNumbers() async* {for (int i 0; i 3; i) {await Future.delayed(Duration(seconds: 1));yield i;}
}void main() {generateNumbers().listen((data) {print(Received: $data); // 按顺序输出});
}这段代码会输出
Received: 0
Received: 1
Received: 2如上所示数据按照顺序传递。
2、Stream async* 和 yield 的解释这三个必须要配套使用吗?不适用 yield 行吗?
在 Dart 中Stream、async* 和 yield 是紧密关联的但它们不一定是必须同时使用的。它们之间的关系可以分开解释看看如何组合使用或者是否可以在没有 yield 的情况下使用 async*。
1. Stream 与 async*
Stream 是 Dart 中用于处理异步数据流的核心概念。Stream 对象用于处理一系列异步事件而 async* 是定义异步生成器异步迭代器的一种语法。async* 允许你生成一个 Stream并通过 yield 来发出数据。
async* 标识一个异步生成器函数它返回一个 Stream。yield 用于在异步生成器中逐个发出数据。
2. async* 和 yield 必须配合使用吗
async* 和 yield 一般是配套使用的但你也可以只使用 async*并没有强制要求一定要用 yield。如果你不需要发出数据即你不想使用 yield你也可以用 async* 作为一个简单的异步函数来返回一个空的 Stream或者使用 await 来发出异步的结果。
3. 不使用 yield 行不行
是的可以在没有 yield 的情况下使用 async*但通常这样做的结果是流不会发出任何数据。在这种情况下Stream 会是一个空的流或者说它在没有任何数据的情况下完成。
示例 1不使用 yield生成一个空的流
Streamint generateEmptyStream() async* {// 什么都不发出
}void main() async {await for (var value in generateEmptyStream()) {print(value); // 这里不会有任何输出}
}在这个例子中async* 只是声明了一个异步生成器但是没有 yield因此返回的 Stream 是空的不会有任何数据输出。
4. 如何使用 async* 生成异步数据流
async* 用于返回 Stream可以通过 yield 来逐个发出数据。你也可以结合 await 来进行异步操作后再发出数据。这是一个典型的用法
Streamint generateNumbers() async* {for (int i 1; i 5; i) {await Future.delayed(Duration(seconds: 1)); // 模拟异步操作yield i; // 发出数据}
}void main() async {await for (var number in generateNumbers()) {print(number);}
}输出
1
2
3
4
5在这个例子中async* 通过 yield 发出了多个数字每次发出时都延迟 1 秒。
总结
async* 和 yield 通常一起使用来生成和发出异步数据流。不一定非要有 yield (但不使用 yield 意义不大)但如果你希望生成一个有数据的流就需要使用 yield 或其他发出数据的方式例如 yield*。如果你在 async* 中没有 yield返回的 Stream 将不会发出任何数据通常这种情况用于创建空流或只执行异步操作的函数。
因此虽然 async* 和 yield 是紧密相关的但它们不必总是同时使用。如果不使用 yield可以生成一个空流或执行异步操作但不会有数据发出。