文章目录
- 1.Dart异步
- 1.1 Dart单线程
- 阻塞式调用和非阻塞式调用
- 1.2 Dart事件循环
- 什么是事件循环
- 1.3 Dart异步操作
- 1.3.1 认识Future
- 1.3.2 Future的链式调用
- 1.4 await、async
- 1.5 多核CPU的利用-isolate
- 1.5.1 什么是isolate
- 1.5.2 Isolate通信机制
- 2. Flutter网络请求
- 2.1 HttpClient
- 2.2 http库
- 2.3 dio第三方库
1.Dart异步
1.1 Dart单线程
Dart是单线程的,不支持多线程。
Dart实现异步是使用的单线程+循环调用的方式
类似于网络请求、文件读写的IO操作,都可以基于非阻塞调用
阻塞式调用和非阻塞式调用
阻塞和非阻塞关注的是程序在等待调用结果(消息、返回值)时的状态
阻塞式调用: 调用结果返回之前,当前线程会被挂起,调用线程只有在得到调用结果之后才会继续执行
非阻塞式调用: 调用执行后,当前线程不会停止执行,只需要过一段时间来检查一下有没有结果返回即可
1.2 Dart事件循环
什么是事件循环
单线程模型中主要就是在维护着一个事件循环(Event Loop)
事件循环是什么:
1.将需要处理的一系列事件(包括点击事件、IO事件、网络事件)放在一个事件队列(Event Queue)中。
2.不断的从时间队列(Event Queue)中取出事件,并执行其对应需要执行的代码块,直到时间队列清空位置
1.3 Dart异步操作
Dart中的异步操作主要使用Future以及async、await
1.3.1 认识Future
import 'dart:io';
main(List args) {
print("main start");
//发送一个网络请求
var result = getNetworkData();
print(result);
print("main end");
}
//模拟一个网络请求
String getNetworkData(){
sleep(Duration(seconds:2));
return "Hello World";
}
输出结果
main start
//控制台输出在此处有等待
Hello World
main end
此时网路请求阻塞了线程
加入Future后
import 'dart:io';
main(List args) {
print("main start");
//发送一个网络请求
var future = getNetworkData();
print(future);
//2.拿到结果
//then后面的回调函数什么时候被执行?
//需要在Future(函数)有结果,才执行下面的回调函数
future.then((value){
print(value);
}).catchError((err){
print("打出错误:$err");
}).whenComplete(() => {
print("代码执行完成")
});
print("main end");
}
//模拟一个网络请求
Future getNetworkData(){
return Future((){
//1.将耗时操作包裹到Future的回调函数中
//1.1结果情况1:只要有返回结果,就执行Future对应的then的回调
//1.2结果情况2:如果没有结果返回(有错误信息),需要在Future回调中抛出一个异常
sleep(Duration(seconds:2));
//模拟结果情况1
return "Hello World";
//模拟结果情况2
throw Exception("我是错误信息");
});
}
main start
Instance of 'Future'
main end
Hello World
代码执行完成
main start
Instance of 'Future'
main end
打出错误:Exception: 我是错误信息
代码执行完成
1.3.2 Future的链式调用
import 'dart:io';
main(List args) {
print("main start");
Future((){
//1.发送第一次请求
sleep(Duration(seconds:8));
return "第一次的结果";
}).then((value){
print(value);
//2.发送的第二次请求
sleep(Duration(seconds:3));
//return "第二次的结果";
throw Exception("第二次请求异常");
}).then((value){
print(value);
}).catchError((err){
print(err);
}).whenComplete(() => {
print("代码完成")
});
print("main end");
}
1.4 await、async
await、async是Dart中的关键字,他们可以让我们用同步的代码格式,去实现异步的调用过程。并且,通常一个async的函数会返回一个Future。
不使用异步时
main(List args) {
print("main start");
var result = getNetworkData();
print(result);
print("main end");
}
String getNetworkData(){
sleep(Duration(seconds:3));
return "Hello World";
}
main start
//程序在这里阻塞,3秒后打印出后面内容
Hello World
main end
使用Future时
main(List args) {
print("main start");
var result = getNetworkData().then((value) {
print(value);
});
print(result);
print("main end");
}
Future getNetworkData(){
return Future((){
sleep(Duration(seconds:5));
return "使用了Future";
});
}
main start
Instance of 'Future'
main end
//程序在这里等待5秒后输出结果,不阻塞main end
使用了Future
使用await、async
await、async相当于Future的语法糖
1.await必须在async函数中才能使用
2.async函数返回的结果必须是一个Future
await需要写在耗时操作之前,表示等待该操作结果
main(List args) {
print("main start");
var result = getNetworkData().then((value) {
print(value);
});
print(result);
print("main end");
}
Future getNetworkData() async {
var response = await (这里写耗时操作);
return "Hello World";
}
虽然return写的返回String,内部会自动包裹为Future
main start
Instance of 'Future'
main end
Hello World
例
链式调用普通写法
main(List args) {
print("main start");
getData();
print("main end");
}
void getData(){
//1.调用第一次网络请求
getNetworkData("argument1").then((value){
print(value);
return getNetworkData(value);
}).then((value) {
print(value);
return getNetworkData(value);
}).then((value) {
print(value);
});
}
Future getNetworkData(String arg){
return Future((){
sleep(Duration(seconds:3));
return "Hello World "+arg;
});
}
main start
main end
Hello World argument1
Hello World Hello World argument1
Hello World Hello World Hello World argument1
使用await、async进行链式调用
main(List args) {
print("main start");
getData().then((value){
print("最终结果$value");
}).catchError((err){
print(err);
});
print("main end");
}
Future getData() async{
//1.调用第一次网络请求
var res1 = await getNetworkData("argument1");
print(res1);
var res2 = await getNetworkData(res1);
print(res2);
var res3 = await getNetworkData(res2);
print(res3);
return res3;
}
Future getNetworkData(String arg){
return Future((){
sleep(Duration(seconds:3));
return "Hello World "+arg;
});
}
1.5 多核CPU的利用-isolate
1.5.1 什么是isolate
- 我们都知道Dart是单线程的,这个线程有自己可以访问的内存空间以及运行的事件循环
- 我们可以将这个空间系统称之为是一个isolate
- 在 Isolate 中,资源隔离做得非常好,每个 Isolate 都有自己的 Event Loop 与 Queue,Isolate 之间不共享任何资源,只能依靠消息机制通信,因此也就没有资源抢占问题。
- 但是,如果只有一个Isolate,那么意味着我们只能永远利用一个线程,这对于多核CPU来说,是一种资源的浪费。
- 如果在开发中,我们有非常多耗时的计算,完全可以自己创建Isolate,在独立的Isolate中完成想要的计算操作。(一般不建议使用Isolate,大数据尽量在服务器端处理)
//第一个参数是方法的名称
//第二个参数是给方法传的参数
import "dart:isolate";
main(List args) {
Isolate.spawn(foo, "Hello Isolate");
}
void foo(info) {
print("新的isolate:$info");
}
新的isolate:Hello Isolate
1.5.2 Isolate通信机制
但是在真实开发中,我们不会只是简单的开启一个新的Isolate,而不关心它的运行结果:
- 我们需要新的Isolate进行计算,并且将计算结果告知Main Isolate(也就是默认开启的Isolate);
- Isolate 通过发送管道(SendPort)实现消息通信机制;
- 我们可以在启动并发Isolate时将Main Isolate的发送管道作为参数传递给它;
- 并发在执行完毕时,可以利用这个管道给Main Isolate发送消息;
import "dart:isolate";
main(List args) async {
// 1.创建管道
ReceivePort receivePort= ReceivePort();
// 2.创建新的Isolate
Isolate isolate = await Isolate.spawn(foo, receivePort.sendPort);
// 3.监听管道消息
receivePort.listen((data) {
print('Data:$data');
// 不再使用时,我们会关闭管道
receivePort.close();
// 需要将isolate杀死
isolate?.kill(priority: Isolate.immediate);
});
}
void foo(SendPort sendPort) {
sendPort.send("Hello World");
}
但是我们上面的通信变成了单向通信,如果需要双向通信呢?
- 事实上双向通信的代码会比较麻烦
- Flutter提供了支持并发计算的compute函数,它内部封装了Isolate的创建和双向通信;
- 利用它我们可以充分利用多核心CPU,并且使用起来也非常简单;
- 注意:下面的代码不是dart的API,而是Flutter的API,所以只有在Flutter项目中才能运行
main(List args) async {
int result = await compute(powerNum, 5);
print(result);
}
int powerNum(int num) {
return num * num;
}
2. Flutter网络请求
在Flutter中常见的网络请求方式有三种:HttpClient、http库、dio库
2.1 HttpClient
HttpClient是dart自带的请求类,在io包中,实现了基本的网络请求相关的操作。
网络调用通常遵循如下课步骤:
- 创建client
- 构造Uri
- 发起请求,等待请求,同时可配置请求headers、body
- 关闭请求,等待响应
- 解码响应的内容
void requestNetwork() async{
//1.创建HttpClient对象
final httpClient = HttpClient();
//2.构建请求的uri
final uri = Uri.parse("https://wanandroid.com/wxarticle/chapters/json");
//3.构建请求
final request = await httpClient.getUrl(uri);
//4.发送请求,必须
final response = await request.close();
if(response.statusCode == HttpStatus.ok){
print(await response.transform(utf8.decoder).join());
}else{
print(response.statusCode);
}
}
- HttpClient也可以发送post相关的请求
- HttpClient虽然可以发送正常的网络请求,但是会暴露过多的细节:比如需要主动关闭request请求,拿到数据后也需要手动的进行字符串解码
- 实际开发中,通常使用一些库来完成网络请求
2.2 http库
http是Dart官方提供的另一个网络请求类,相比于HttpClient,易用性提升了。但是,没有默认集成到Dart的SDK中,所以我们需要先在pubspec.yaml中依赖它
http:^0.12.0+2
import 'package:http/http.dart' as http;
void requestNetwork() async{
//1.创建Client
final client = http.Client();
//2.构建uri
final url = Uri.parse("https://wanandroid.com/wxarticle/chapters/json");
//3.发送请求
final response = await client.get(url);
//4.获取结果
if(response.statusCode == HttpStatus.ok) {
print(response.body);
}else{
print(response.statusCode);
}
}
2.3 dio第三方库
dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义适配等
dio: ^3.0.10