Flutter —— 异步编程
1. 异步编程
1.1 Future
Future 类,其表示一个 T 类型的异步操作结果。如果异步操作不需要结果,则类型为 Future。也就是说首先Future是个泛型类,可以指定类型。如果没有指定相应类型的话,则Future会在执行动态的推导类型。
在网络请求中经常使用异步编程,那么来探索一下Flutter 中的异步编程。
dart是单线程的,所以底层没有锁之类的东西,但是这不代表着dart不能异步,异步不代表多线程。
下面代码运行后发现做其他事情被堵塞住了,这里async不起作用是因为需要搭配Future使用。
将耗时操作使用Future包装起来,这里可以看到做其他事情就不会被堵塞住了,那么现在即使方法不加async也是异步的,因为Future里面已经是异步的了。或者说async不会异步执行,Future才会异步执行。
如果把 print(‘结束data = $_data’); 放在Future外面那么其就会先于Future里面的代码执行。
1.2 async ,await
那么如何让等待Future里面的执行完在执行后面代码呢?这时候需要用到await。加了await之后,后面的代码就会等待Future里面的执行完之后在执行。这里的await需要搭配async使用,而没有await的情况下,async是没有意义的。await后面的操作也需要是异步的。那么也就是说,Future是用来异步执行的,而async和await搭配是用来让Future里面的某块代码同步执行的。
1.3 Future.then()
Future的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用。
这里可以使用then来将特定的需要等待的任务放进去,然后不堵塞后面的任务的执行。 如果then没有返回值的话,那么value就是null。
这里看到Future里面return的话那么value就是return的那个值。这个时候,Future里面返回的数据会被Future包装,然后给到了then里面 的value。
1.4 Future.catchError
异步中的错误是用catchError来进行处理的。当异步任务中出现异常之后,
如果调用下列方法就会报错,这是因为任务里面抛出了异常没有处理。
getData() async {
print('开始data = $_data');
//耗时操作
Future future = Future(() {
for (int i = 0; i < 10000000; i++) {}
throw Exception('网络异常');
return "假的网络数据";
});
future.then((value) {
print('value = $value');
});
print('做一点其他事情');
}
在.then 下面添加代码
future.catchError((e){print("捕获到了错误:$e");});
运行后发现还是报错,但是捕获到了异常。正常来说如果处理了异常,就不应该报错了,那么这里是执行顺序的问题吗?
将catch和then交换位置后运行,发现还是报错
把then注释掉后发现不报错了。
那么这里要怎么处理呢?这里一般用链式调用。
getData() async {
print('开始data = $_data');
//耗时操作
Future future = Future(() {
for (int i = 0; i < 10000000; i++) {}
throw Exception('网络异常');
return "假的网络数据";
}).then((value) {
print('value = $value');
}).catchError((e) {
print("捕获到了错误:$e");
});
print('做一点其他事情');
}
或者在.then中添加onError对错误进行处理
future.then((value) {
print('value = $value');
},onError: (error){print("捕获到了错误:$error");});
onError是在.then这一次对错误进行处理,而catchError则是多次链式调用中的错误处理
如果在.then之前catchError了,那么.then中的value就是catchError中传过来的值了。在catchError之前的.then不会执行。
在CatchError之后添加了whenComplete运行后发现还是报出了异常。
getData() async {
print('开始data = $_data');
//耗时操作
Future future = Future(() {
for (int i = 0; i < 10000000; i++) {}
throw Exception('网络异常');
return "假的网络数据";
});
future.catchError((e) {
print('error = $e');
return "错误";
}).then((value) => print('$value'));
future.whenComplete(() => print("完成了"));
print('做一点其他事情');
}
这个时候可以在whenComplete之后添加一个catchError,或者使用链式调用。
future.catchError((e) {
print('error = $e');
return "错误";
}).then((value) => print('$value')).whenComplete(() => print("完成了"));
上面的例子可以看到链式调用能避免大部分的错误,所以在一般都是使用链式调用来进行处理的,并且把catchError放到最后调用,这样出现异常的时候,前面的.then就不会执行了。如果链式太长的话,可以创建方法来让链式更加简洁。
getData() async {
print('开始data = $_data');
//耗时操作
Future future = Future(() {
for (int i = 0; i < 10000000; i++) {}
throw Exception('网络异常');
return "假的网络