1.简介:
Dart库充满了返回Future或Stream对象的函数;这些函数都是异步的。这些函数在做一些可能耗时的操作(如I/O)时,会立即返回一个Future或Stream对象,进行函数后面代码的执行,而不需要等待该操作完成后再执行函数后面的代码。
Dart中使用async``await
关键字来实现异步编程,但是和Java、OC等异步不同,代码类似于同步代码,实际上是异步执行的。
关键术语:
同步操作:同步操作阻止其他操作执行,直到它完成。
同步函数:同步函数只执行同步操作。
异步操作:异步操作一旦启动,就允许其他操作在它完成之前执行。
异步函数:一个异步函数至少执行一个异步操作,也可以执行同步操作。
异步场景:
- 获取网络数据(请求数据、图片下载、文件下载等);
- 读取数据(从文件、数据库、磁盘中);
- 写入数据(文件、数据库、磁盘等)。
2.处理Future
:
Future
表示一个不会立即完成的计算过程。与普通函数直接返回结果不同的是异步函数返回一个将会包含结果的Future
。该 Future
会在结果准备好时通知调用者。
future与Future:
future(小写“f”)是Future类的一个实例。future表示异步操作的结果,可以有两种状态:未完成uncompleted
或已完成completed
。
未完成uncompleted
:
当调用一个异步函数时,它将返回一个未完成的future。该future表示函数正在等待函数的异步操作完成或抛出错误。
成功的完成Completing with a value
:
Future <T>
类型的future以T类型的值结束。例如,Future <String>
类型的future产生一个字符串值。如果future没有产生一个可用的值,那么future的类型是Future <void>
。
错误的完成Completing with an error
:
如果函数执行的异步操作由于任何原因失败,则future函数将以错误的方式完成。
// 引入Future,并返回一个Future<void>的例子
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info from another service or database.
return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}
// 返回一个future错误的例子
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info but encounters a bug
return Future.delayed(const Duration(seconds: 2),
() => throw Exception('Logout failed: user ID is invalid'));
}
Futuer和future总结:
-
Future<T>
实例产生类型T的值; - 如果
future
没有产生一个可用的值,那么future的类型是future <void>
; -
future
有两种状态:未完成或已完成; - 当调用一个返回
future
的函数时,该函数将会进入一个异步任务队列中queues
,并立即返回一个未完成状态的future
; - 当
future
的操作完成时,它会返回一个T类型的值或者一个错误。
3.异步async
和等待await
:
async
和await
关键字提供了一种声明性的方式来定义异步函数并使用它们的结果;使用的基本原则:
- 要定义一个异步函数,请在函数体前添加
async
; - await关键字只在异步函数中有效;
- 同步函数中调用异步函数,处理异步函数结果需要
.then
语法。
Future<String> createOrderMessage() async {
print("message star");
var order = await fetchUserOrder3();
print("message return");
return 'Your order is: $order';
}
Future<String> fetchUserOrder3() async {
// Imagine that this function is more complex and slow.
print("order star");
var result = await Future.delayed(
const Duration(seconds: 2), () {
return 'Large Latte';
});
print("order return");
return result;
}
// 同步函数中调用
print('message top');
createOrderMessage().then((value) {
print(value);
});
print('message bottom');
// 打印顺序
flutter: message top
flutter: message star
flutter: order star
flutter: message bottom
flutter: order return
flutter: message return
flutter: Your order is: Large Latte
如何解读上述代码的打印顺序呢?
- 同步函数中代码是从上往下顺序执行的;
- 异步函数中,可能是全部同步执行,也可能有同步和也有异步执行;
- 异步函数中,以
await
关键字为分界线,await
以上包含await
调用函数的语句是同步的,await
的返回结果和后面的代码是异步执行。
示例如图,红色的代码为同步执行,绿色代码为异步执行的:
异步函数的执行.png
开始解读:
- 同步函数首先打印:message top;
- 同步调用异步函数createOrderMessage,进入该函数;
- 在函数createOrderMessage中,首先同步打印:message star;
- 同步调用
await fetchUserOrder3();
,进入函数fetchUserOrder3; - 在函数fetchUserOrder3中,首先同步打印:order star;
- 同步调用
await Future.delayed
,进行函数Future.delayed调用; - 函数Future.delayed延迟执行,为异步调用,直接返回一个Future<String>的实例future,并加入异步队列中等待执行;
- 函数fetchUserOrder3检测到异步执行,直接返回一个future,退出同步执行,将异步部分加入异步队列中等待执行;
- 函数createOrderMessage与6相同;
- 同步函数中异步函数createOrderMessage的同步部分执行完毕,继续执行下面同步代码,打印:message bottom;
- 队列要求先进先出,当最先加入异步队列的任务已经完成后,会以加入的次序执行后续加入的异步代码,后面的打印书序为:
order return
message return
Your order is: Large Latte
3.异步错误error
处理:
在异步函数中,如同同步函数一样使用try-catch
处理出现的错误或者异常。
Future<void> printOrderMessage() async {
try {
print('Awaiting user order...');
var order = await fetchUserOrder();
print(order);
} catch (err) {
print('Caught error: $err');
}
}
Future<String> fetchUserOrder() {
// Imagine that this function is more complex.
var str = Future.delayed(
const Duration(seconds: 4),
() => throw 'Cannot locate user order');
return str;
}
Future<void> main() async {
// 会抛出Cannot locate user order异常
await printOrderMessage();
}
4.处理流Streams
:
官方文档
Stream 是一系列异步事件的序列。其类似于一个异步的 Iterable,不同的是当你向 Iterable 获取下一个事件时它会立即给你,但是 Stream 则不会立即给你而是在它准备好时告诉你。
Stream
概要:
-
Stream
提供一个异步的数据序列。 - 数据序列包括用户生成的事件和从文件读取的数据。
- 你可以使用
Stream API
中的listen()
方法和await for
关键字来处理一个Stream
。 - 当出现错误时,
Stream
提供一种处理错误的方式。 - Stream 有两种类型:
Single-Subscription
和Broadcast
。
4.1接收Stream
事件:
Stream
可以通过许多方式创建,而这些所有的创建方式都可以相同的方式在代码中使用:像使用for
循环 迭代一个Iterable
一样,我们可以使用 异步for
循环 (通常我们直接称之为await for
)来迭代Stream
中的事件。例如:
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
sum += value;
}
return sum;
}
上面代码只是简单地接收整型事件流中的每一个事件并将它们相加,然后返回(被Future
包裹)相加后的整型值。当循环体结束时,函数会暂停直到下一个事件到达或Stream
完成。内部使用await for
循环的函数需要使用async
关键字标记。
下面的示例中使用了async*
函数生成一个简单的整型Stream
来测试上一个代码片段:
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
sum += value;
}
return sum;
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
// 调用
var stream = countStream(10);
var sum = sumStream(stream);
sum.then((value) {
print(value);
});
4.2Stream
错误处理:
当Stream
再也没有需要处理的事件时会变为完成状态。与此同时,调用者可以像接收到新事件回调那样接收Stream
完成的事件回调。当使用await for
循环读取事件时,循环会在Stream
完成时停止。
有时在Stream
完成前会出现错误;比如从远程服务器获取文件时出现网络请求失败,或者创建事件时出现 bug;在出现错误时,需要将错误告知使用者。
Stream
可以像提供数据事件那样提供错误事件。大多数Stream
会在第一次错误出现后停止,但其也可以提供多次错误并可以在在出现错误后继续提供数据事件。在本篇文档中我们只讨论Stream
最多出现并提供一次错误事件的情况。
当使用await for
读取Stream
时,如果出现错误,则由循环语句抛出,同时循环结束。你可以使用try-catch
语句捕获错误。下面的示例会在循环迭代到参数值等于 4 时抛出一个错误:
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
if (i == 4) {
throw Exception('Intentional exception');
} else {
yield i;
}
}
}
5.隔离区Isolates
:
大多数计算机中,甚至在移动平台上,都在使用多核 CPU。为了有效利用多核性能,开发者一般使用共享内存的方式让线程并发地运行。然而,多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错。
为了解决多线程带来的并发问题,Dart 使用isolate
替代线程,所有的 Dart 代码均运行在一个 isolate 中。每一个isolate
有它自己的堆内存以确保其状态不被其它 isolate 访问。
所有的 Dart 代码都是在一个isolate
中运行,而非线程。每个 isolate 都有一个单独的执行线程,并且不与其他的isolate
共享任何可变对象。
你可以查阅下面的文档获取更多相关信息:
- Dart 中的并发特性
- dart:isolate API 参考 介绍了 Isolate.spawn() 和 TransferableTypedData 的用法
- Flutter 文档上关于 后台解析 的实用教程
- Isolate sample app
网友评论