在 App 开发中,经常会遇到处理异步任务的场景,如网络请求、读写文件等。Android、iOS 使用的是多线程,而在 Flutter 中为单线程事件循环;
学习Future之前先了解下dart的消息循环
Dart的消息循环机制
1. Dart的单线程执行
当一个Dart的方法开始执行时,他会一直执行直至达到这个方法的退出点。换句话说Dart的方法是不会被其他Dart代码打断的。
2. isolate
Dart是基于单线程模型的语言。在Dart中有一个很重要的概念叫isolate,它其实就是一个线程或者进程的实现,具体取决于Dart的实现。默认情况下,我们用Dart写的应用都是运行在****main isolate****中的(可以对应理解为Android中的main thread)。当然我们在必要的时候也可以通过isolate API创建新的isolate,多个isolate可以更好的利用多核CPU的特性来提高效率。但是要注意的是在Dart中isolate之间是无法直接共享内存的,不同的isolate之间只能通过isolate API进行通信。因为 isolate 是单线程实体,所以 isolate中的代码是按顺序执行的。
3. Dart的消息循环和消息队列
一个Dart应用有一个消息循环(event loop) 和两个消息队列(event queue 和 microtask queue)。
- 消息循环(event loop):每个isolate都有单独的消息队列
- event队列:包含所有外来的事件:I/O,mouse events,drawing events,timers,isolate之间的message等。
- microtask队列:在Dart中是必要的,因为有时候事件处理想要在稍后完成一些任务但又希望是在执行下一个事件消息之前。
event队列包含Dart和来自系统其它位置的事件。但microtask队列只包含来自当前isolate的内部代码。
Dart 中的代码执行优先级可以分为三个级别:
- 在 Main 中写代码将最先执行;
- 执行完 Main 中的代码,然后会检查并执行 Microtask Queue 中的任务, 通常使用 scheduleMicrotask 将事件添加到 MicroTask Queue 中;
- 最后执行 EventQueue 队列中的代码,通常使用 Future 向 EventQueue加入事件,也可以使用 async 和 await 向 EventQueue 加入事件。
总结:Dart 中事件的执行顺序:Main > MicroTask > EventQueue。
- 举例一:
void test1(){
Future f2 = Future(() => null);
Future f1 = Future(() => null);
Future f3 = Future(() => null);
f1.then((_) => print("1"));
f2.then((_) => print("2"));
f3.then((_) => print("3"));
}
结果:2 1 3
- 举例二
void test2() {
Future f1 = Future(() => null);
Future f2 = Future(() => null);
Future f3 = Future(() => null);
f1.then((_) => print("f1 -> f1"));
f3.then((_) {
print("f3 -> f3");
f1.then((_) => print("f3.then -> f1"));
});
f2.then((_) => print("f2 -> f2"));
}
结果:
flutter: f1 -> f1
flutter: f2 -> f2
flutter: f3 -> f3
flutter: f3.then -> f1
- 举例3
main() {
print('main #1 of 2');
scheduleMicrotask(() => print('microtask #1 of 2'));
Future.delayed( Duration(seconds:1),
() => print('future #1 (delayed)'));
Future(() => print('future #2 of 3'));
Future(() => print('future #3 of 3'));
scheduleMicrotask(() => print('microtask #2 of 2'));
print('main #2 of 2');
}
结果:
flutter: main #1 of 2
flutter: main #2 of 2
flutter: microtask #1 of 2
flutter: microtask #2 of 2
flutter: future #2 of 3
flutter: future #3 of 3
flutter: future #1 (delayed)
- 举例4
import 'dart:async';
/// Copyright © 2023 Shanghai Yejia Digital Technology Co., Ltd. All rights reserved.
/// @since 2023/7/27 16:34
/// @author gdd
void main(){
print('main 1');
scheduleMicrotask(() => print('microtask 1'));
Future.delayed(const Duration(seconds:1),
() => print('future 1'));
Future(() => print('future 2'))
.then((_) => print('future #2.1'))
.then((_) {
print('future #2.2');
scheduleMicrotask(() => print('microtask 4'));
})
.then((_) => print('future #2.3'));
scheduleMicrotask(() => print('microtask 2'));
Future(() => print('future 3'))
.then((_) => Future(
() => print('future #3.1 new')))
.then((_) => print('future #3.2'));
Future(() => print('future 4'));
scheduleMicrotask(() => print('microtask 3'));
print('main 2');
}
执行结果:
flutter: main 1
flutter: main 2
flutter: microtask 1
flutter: microtask 2
flutter: microtask 3
flutter: future 2
flutter: future #2.1
flutter: future #2.2
flutter: future #2.3
flutter: microtask 4
flutter: future 3
flutter: future 4
flutter: future #3.1 new
flutter: future #3.2
flutter: future 1
Future
同步编程中,立即返回执行的结果或者抛出异常;而异步编程中,返回的是将要完成的结果未来。Future代表异步执行的未来。
Future返回值:
Future 是一个泛型,其中T代表的是我们耗时操作返回的具体值
Flutter提供了下面三个方法,让我们来注册回调,来监听处理Future的结果。
- 1.处理完成时候的回调,一般都是成功回调
Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError});
- 2.处理失败的回调,比如throw一个error就会走到这里
Future<T> catchError(Function onError, {bool test(Object error)});
- 3.Future.whenComplete总是在Future完成后调用,不管Future的结果是正确的还是错误的。
Future<T> whenComplete(FutureOr action());
Future处理异常
异常处理是我们在开发中特别需要注意的,正确的处理程序运行中的异常,能给用户带来更好的体验。
在future中可以使用catchError()或在then()方法中传入可选参数onError,如下所示:
var future = getNetworkData();
future.then((value) {
print(value);
}, onError: (e) {
print(e);
});
//或者在catchError中处理异常
future.then((value) {
print(value);
}).catchError((e) {
print(e);
});
Future<String> getNetworkData() {
return Future.delayed(const Duration(seconds: 3), () {
int result = 0;
for (int i = 0; i < 100000; i++) {
result += i;
}
//抛出异常
return throw Exception('this is ');
});
}
使用async和await关键字来处理future
- async:在方法体前面是使用,定义该方法为一个异步方法。
- await:等待并获得异步表达式的执行结果,并且该关键字只能在async修饰的方法中。
//找回密码
static void findPwd(BuildContext context, Map<String, dynamic> params,
Function(LoginResult) callback) {
ApiManager().request(() async {
final result = await HttpManager().post(
"${BaseApi.getBaseUrl(ApiTypeEnum.core)}${ServiceType.getServiceType(1000)}$findPwdPath",
data: params,
options: Options(),
cancelToken: CancelToken());
LoginResult loginResult = LoginResult.fromJson(result);
callback(loginResult);
}, context);
}
处理async方法中的异常
对于async方法中的异常,需要通过try/catch进行处理。
Future request(Function() block, BuildContext context,
{bool Function(ApiException)? onError, bool isShowLoading = true}) async {
if (isShowLoading) {
showLoading();
}
try {
await block();
} catch (e) {
handleException(context, ApiException.from(e), onError: onError);
} finally {
dismissLoading();
}
return;
}
参考文档://www.greatytc.com/p/2fdfb4d70deb/
参考文档:https://blog.csdn.net/mrRuby/article/details/122563629