「这是我参与11月更文应战的第20天,活动概略检查:2021最后一次更文应战」
今天这篇文章我们来介绍一下Dart
中的异步编程;
我们先来看一段代码:
String _string = '默许';
void main() {
getData();
print('其他业务');
}
getData() {
print('初步');
for (int i = 0; i < 10000000000; i++) {
_string = '耗时操作';
}
print('结束:$_string');
}
其实行打印效果如下:
在这段代码中,for
循环的耗时操作卡着了后续代码的实行;
那么怎么想for
循环变成异步操作呢?这个时分我们需求用到Future
;
Future
Future
与JavaScript
中的Promise
非常类似,标明一个异步操作的毕竟结束及其效果的标明。简略来说Future
便是用来处理异步操作的,异步处理成功了就实行成功的操作,异步处理失利了就捕获差错或许间断后续操作。一个Future
只会对应一个效果,要么成功,要么失利;
需求留心的是,Future
的全部API
的回来值依然是一个Future
方针,所以我们可以很方便的进行链式调用;
我们将代码批改如下:
String _string = '默许';
void main() {
getData();
print('其他业务');
}
getData() {
print('初步:$_string');
Future(() {
for (int i = 0; i < 10000000000; i++) {
_string = '耗时操作';
}
print('结束:$_string');
});
}
工作效果:
经过工作效果我们发现,将for
循环放进Future
中之后,耗时操作将不会堵塞代码的实行;
await/async
在Flutter
中还有一种方法可以让我们像写同步代码一样来实行异步任务而不用运用回调的方法,这便是async/await
了,我们来看一段代码:
String _string = '默许';
void main() {
getData();
print('其他业务');
}
getData() async {
print('初步:$_string');
await Future(() {
for (int i = 0; i < 1000000000; i++) {
_string = '网络数据';
}
print('获取到数据:$_string');
});
print('结束:$_string');
}
在此段代码中getData
方法运用了async
标识,而在方法内部for
循环地点的Future
添加了await
标识,那么会有什么效果呢?
可以看到,Future
之外的代码,比及Future
中代码实行结束之后才持续实行了;
那么,sync
和await
都是什么效果呢?
-
async
用来标明函数是异步的,定义的函数会回来一个Future
方针,可以运用then
方法添加回调函数; -
await
后边是一个Future
,标明等候该异步任务结束,异步结束后才会持续往下实行;await
必须出现在async
函数内部;
可以看到,我们经过运用async
和await
将一个异步流调用运用同步的代码逻辑标明了出来;
async
和await
仅仅一个语法糖,编译器或许解说器毕竟都会将其转化为一个Future
的调用链;
then
那么,假如不运用await
,我们能否结束相同的效果呢?
Future
方法的回来值依然是一个Future
方针,我们运用Future
方针接纳此回来值;然后经过future.then
来打印一下回来的数据:
我们将getData
方法批改如下:
getData() async {
print('初步:$_string');
Future future = Future(() {
for (int i = 0; i < 1000000000; i++) {
_string = '网络数据';
}
print('获取到数据:$_string');
});
future.then((value) {
print('then方法: $_string');
});
print('结束:$_string');
}
我们看一下打印效果:
我们可以看到,尽管没有了await
标识,可是Future
方针的then
方法中依然可以获取到异步任务的数据;
可是此刻then
方法中的value
却是空的:
这是因为,Future
中捕获的异步任务没有回来值,那么我们给这个异步任务添加一个回来值,我们将getData
方法批改如下:
getData() async {
print('初步:$_string');
Future future = Future(() {
for (int i = 0; i < 1000000000; i++) {
_string = '网络数据';
}
print('获取到数据:$_string');
return '回来数据';
});
future.then((value) {
print('then方法: $_string, value: $value');
});
print('结束:$_string');
}
我们再来看一下打印效果:
此刻,then
方法中回来的value
已经有值了,值为异步任务中return
的回来值;在Future
中回来的数据会被包装进Future
的方针中,然后回来一个Future
的方针;
catchError
许多情况下,我们在进行网络处理的时分的时分,会抛出失常,那么在Future
中应该怎么处理失常呢?
我们来看下边代码:
getData() async {
print('初步:$_string');
Future future = Future(() {
for (int i = 0; i < 1000000000; i++) {
_string = '网络数据';
}
throw Exception('网络失常');
});
future.then((value) {
print('then方法: $_string, value: $value');
});
print('结束:$_string');
}
在Future
内部,经过throw
抛出了一个Exception
,我们来看一下代码的实行效果:
可以看到,实行过程中代码报错,抛出了网络失常
的差错信息;那么我们就需求进行阻拦,将Exception
捕获到,不能在实行中让工程犯错,这个时分我们就需求运用catchError
捕获失常,我们将代码批改如下:
工作效果:
我们确实捕获到了失常,可是工程依然报错了,这是为什么呢?这个时分,我们需求用到链式调用,在then
方法之后,直接运用链式调用来捕获失常,代码如下:
工作效果:
onError
then
的定义如下:
Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});
我们发现,在then
方法中还有一个onError
方法,此方法也可以用来处理差错以及失常:
工作效果:
可以看到,运用then
中的onError
方法捕获差错之后,catchError
将不会调用;
此处的
onError
方法是在then
内部定义的,所以需求在then
方法内部调用,而非平级;
catchError
是在整个Future
的链式调用过程中捕获失常,而onError
只在当时then
中处理;我们在一个链式调用的过程中可能存在多个then
;
我们将catchError
与then
方法交换次序:
工作效果:
我们发现,假如catchError
在then
方法前面,即使在catchError
中捕获了失常,那么then
方法依然会实行;
whenComplete
有时分,我们的异步任务不论成功与失利,都需求去做一些其他工作的时分,有两种方法处理*
- 在
then
或许catchError
中处理; - 运用
Future
的whenComplete
回调处理;
我们将代码批改如下:
工作效果:
尽管我们的whenComplete
结束情况实行了,可是失常依然被抛出了;这里有两种方法处理:
-
whenComplete
运用链式调用方法,不运用Future
的方针调用; - 在
whenComplete
之后,再次捕获失常,代码如下:
我们一般运用过程中将then
方法中放在前面,这样当失常捕获时,then
方法将不会实行:
在项目中运用时,我们举荐以下写法:
举荐将各个情况的逻辑处理方法抽出,如下: