Flutter实战-异步与多线程
Future异步
在上面网络请求的时候用到了Future,那么这个到底是什么?Fluture是主线程当中的异步代码
- 下面打印的顺序是什么:
void main() {
testFuture();
print('A');
}
void testFuture() async {
Future(() {
print('C');
});
print('B');
}

- 加入
await之后打印的顺序是什么?
void main() {
testFuture();
print('A');
}
void testFuture() async {
await Future(() {
print('C');
}).then((value) => print('D'));
print('B');
}

经过测试发现使用
Future修饰的代码块会异步执行,不会卡住当前的线程。如果希望在这个异步任务执行完成之后再操作,需要在Future前面加上一个await。
- 多个
Future并行的时候的打印顺序:
void main() {
testFuture();
print('A');
}
void testFuture() async {
Future(() {
return '任务1';
}).then((value) => print('$value 执行结束'));
Future(() {
return '任务2';
}).then((value) => print('$value 执行结束'));
Future(() {
return '任务3';
}).then((value) => print('$value 执行结束'));
Future(() {
return '任务4';
}).then((value) => print('$value 执行结束'));
print('任务添加完毕');
}

调整顺序或者是让其中的一个任务加入睡眠等多项测试之后,发现多个
Future并行发现这个执行的顺序就是添加的顺序。
- 有依赖关系的
Future打印顺序:
void testFuture() async {
Future(() {
return '任务1';
}).then((value) {
print('$value结束');
return '$value任务2';
}).then((value) {
print('$value结束');
return '$value任务3';
}).then((value) {
print('$value结束');
return '$value任务4';
});
}

- 多个
Future执行完成之后再操作:
void testFuture() async {
Future.wait([
Future(() {
return '任务1';
}),
Future(() {
return '任务2';
}),
Future(() {
return '任务3';
}),
Future(() {
return '任务4';
}),
]).then((value) => print('$value')); // wait里面的执行顺序也是添加的顺序
}

scheduleMicrotask可以插队到Future任务前面
void testFuture() async {
Future.wait([
Future(() {
return '任务1';
}),
Future(() {
return '任务2';
}),
Future(() {
return '任务3';
}),
Future(() {
return '任务4';
}),
]).then((value) => print('$value'));
scheduleMicrotask(() {
print('scheduleMicrotask');
});
}

Dart中就只有两种队列:一种是事件队列,一种是微任务队列,微任务队列的优先级始终高于事件队列。 下图为官方图:

- 下列打印的顺序是什么?
void testFuture() async {
Future x = Future(() => print('A'));
Future(() => print('B'));
scheduleMicrotask(() {
print('C');
});
x.then((value) => print('D'));
print('E');
}

首先肯定是主线程的E,然后是微任务的C,剩下的两个Future按照添加的顺序执行,首先执行A和D最后是B
Fluture中嵌套微任务的执行顺序:
void testFuture() async {
Future(() => print('A')).then((value) {
scheduleMicrotask(() {
print('D');
});
}).then((value) => print('F'));
Future(() => print('B'));
scheduleMicrotask(() {
print('C');
});
print('E');
}

这里可以这么理解:then后面的代码可以理解为丢到了微任务队列去执行。
补充:使用Flutter的时候尽量使用链式调用,保证在最后调用.catchError来捕获异常
getData() async {
print('开始了');
await Future(() {
for (int i = 0; i < 100; i++) {}
return '循环结束';
})
.then((value) => print('$value'))
.whenComplete(() => print('完成了'))
.catchError((e) => print(e.toString()));
print('await之后的代码');
}
Dart中的多线程
Dart当中的Isolate更像一个进程,有独立的内存空间。意味着每个进程之间的数据是独立的,不存在抢夺空间的情况,所以不需要锁的概念。
void main() {
IsolateDemo();
}
void IsolateDemo() {
print('1');
Isolate.spawn(func, 3);
Isolate.spawn(func, 4);
Isolate.spawn(func, 5);
Isolate.spawn(func, 6);
Isolate.spawn(func, 7);
Isolate.spawn(func, 8);
Isolate.spawn(func, 9);
sleep(Duration(seconds: 3));
print('2');
}
上面说了不存在同一块内存的问题,我们也来验证一下,在下面的代码中,我在Isolate中修改了a的值,那么最后打印的a=?
int a = 100;
void IsolateDemo() {
print('1');
Isolate.spawn(func, 3);
Isolate.spawn(func, 4);
Isolate.spawn(func, 5);
Isolate.spawn(func, 6);
Isolate.spawn(func, 7);
Isolate.spawn(func, 8);
Isolate.spawn(func, 9);
sleep(Duration(seconds: 3));
print('2');
print('a = $a');
}
func(int count) {
print('$count');
a = count;
}
经验证,最后a还是等于100.也就是说在Isolate中的修改并没有效果。
ReceivePort & Isolate
void IsolateDemo() {
print('1');
ReceivePort port = ReceivePort();
Isolate.spawn(func, port.sendPort);
port.listen((message) {
print('message=$message');
});
sleep(Duration(seconds: 3));
print('2');
}
func(SendPort port) {
port.send(10);
}

上面的代码还是有一点需要完善的地方:此时注意需要手动的关闭端口和销毁Isolate
void IsolateDemo() async {
print('1');
ReceivePort port = ReceivePort();
Isolate iso = await Isolate.spawn(func, port.sendPort);
port.listen((message) {
print('message=$message');
port.close();
iso.kill();
});
sleep(Duration(seconds: 3));
print('2');
}
compute
用法跟Isolate差不多,是在Isolate上的进一步包装。compute不需要手动kill
void main() {
Comouterdemo();
}
void Comouterdemo() {
print('1');
compute(func1, 10);
sleep(Duration(seconds: 3));
print('2');
}
func1(int num) {
print('$num');
}

搭配await异步使用
void main() {
Comouterdemo();
}
void Comouterdemo() async {
print('1');
int result = await compute(func1, 10);
sleep(Duration(seconds: 3));
print('result=$result');
print('2');
}
int func1(int num) {
print('$num');
return num;
}

一些小知识
- 关于import, as关键字就是给库起别名,目的是防止类名、方法名冲突
import 'package:http/http.dart' as http; - 导入库,默认是整个文件都会导入,如果需要指定的话有两个关键字。
show:执行需要导入的内容;hide:需要隐藏的内容 pubspec.yaml中publish_to:指定发布到哪里去,默认的都是到pub.dev里面去pubspec.yaml中version: 当前项目的版本号pubspec.yaml中 Dart的版本environment:sdk: ">=2.12.0 <3.0.0"pubspec.yaml中dev_dependencies: 开发环境依赖的版本打包的时候不会有这些pubspec.yaml中dependencies: 第三方库导入位置。dio:^4.0.1大版本不变的区间写法,相当于>=4.0.1 <5.0.0;dio:4.0.1指定4.0.1版本;dio:any任意版本
转载自:https://juejin.cn/post/7030706164534870052