Dart 代码运行在单个执行“线程”中。如果 Dart 代码在执行时阻塞,例如:处理一个需要长时间运行的计算操作或等待 I/O 完成。此时整个程序会被“冻结”。
异步操作可以让你的程序在等待一个操作完成时继续处理其它的工作。Dart 使用 Future 对象来表示异步操作的结果。你可以用 async 和 await 关键字或 Future 类的相关 API 来配合使用 future。
1.Future
下面是一段会产生线程堵塞的代码
import 'dart:io';
main(List<String> args) {
print('main start ${getDateStringNow()}');
print('${gatherNewsReports()}');
print('main start ${getDateStringNow()}');
}
//一个线程堵塞事件
String gatherNewsReports() {
//睡眠3秒钟
sleep(Duration(seconds: 3));
//获得时间
String dateStr = getDateStringNow();
//返回时间;
return 'this is news reports $dateStr';
}
//获取当前的时间
String getDateStringNow() {
int timeStamp = DateTime.now().millisecondsSinceEpoch;
String dateStr = getDateStringFromTimeStamp(timeStamp: timeStamp);
return dateStr;
}
//根据时间戳/年月日格式获取时间
String getDateStringFromTimeStamp({
int timeStamp = 0, //fromMillisecondsSinceEpoch
String yyyy = '年',
String MM = '月',
String dd = '日',
String hh = ':',
String mm = ':',
}) {
if (timeStamp == null || timeStamp == "") {
return "";
}
//把时间戳转化成DateTime实例对象;
// DateTime theDate = DateTime.fromMicrosecondsSinceEpoch(timeStamp * 1000);
DateTime theDate = DateTime.fromMillisecondsSinceEpoch(timeStamp);
var year = theDate.year.toString(); //取的年份String类型
var month = theDate.month.toString(); // 取的月份,String类型
if (theDate.month <= 9) {
month = "0" + month;
}
var day = theDate.day.toString(); // 取的日期,String类型;
if (theDate.day < 10) {
day = "0" + day;
}
var hour = theDate.hour.toString(); // 取的小时,String类型;
if (theDate.hour < 10) {
hour = '0' + hour;
}
var minutes = theDate.minute.toString(); // 取的分钟数,String类型;
if (theDate.minute < 10) {
minutes = '0' + minutes;
}
var seconds = theDate.second.toString(); // 取的秒数,String类型;
if (theDate.second < 10) {
seconds = '0' + seconds;
}
//声明一个空时间字符串用来存储最后的时间;
String finalDateString = '';
finalDateString = '$year$yyyy$month$MM$day$dd$hour$hh$minutes$mm$seconds';
return finalDateString;
}
打印结果:
main start 2020年09月25日14:21:26
this is news reports 2020年09月25日14:21:29
main start 2020年09月25日14:21:29
gatherNewsReports();
事件堵塞了线程3秒钟;
使用Future
对阻塞线程的事件进行异步调用的方案:
import 'dart:io';
main(List<String> args) {
print('main start ${getDateStringNow()}');
// print('${gatherNewsReports()}');
gatherNewsReports().then((value) {
//成功获得回调
print(value);
}).catchError((e) {
print(e.runtimeType);
});
print('main start ${getDateStringNow()}');
}
//一个线程堵塞事件
Future<String> gatherNewsReports() {
Future f = Future<String>(() {
//睡眠3秒钟
sleep(Duration(seconds: 3));
//获得时间
String dateStr = getDateStringNow();
//返回时间;
if (true) {
return 'this is news reports $dateStr';
} else {
var resulet = ['this is news reports $dateStr'];
throw Exception(resulet);
}
});
return f;
}
打印结果:
main start 2020年09月25日14:26:58
main start 2020年09月25日14:26:58
this is news reports 2020年09月25日14:27:01
代码分析:
在上面的代码中,创建了一个Future
是里f
,把耗时的操作放在f的的回调方法中了,所以线程不会被耗时操作堵塞,并且把Future
的实例作为了函数的返回结果,当耗时操作结束后,得到的结果会通过f
的实例方法then(value);
得到函数耗时操作的结果;
所以在main()
函数中,调用gatherNewsReports()
方法后,可以通过Future
的实例的then(onvalue);
方法拿到函数的回调结果;
当函数执行有异常需要抛出的时候,可以通过throw
语法抛出异常,通过Future
的catchError();
方法处理异常回调;更多细节可以查看Flutter官方文档;
1.1 Future的嵌套使用
void FutureTest2() {
print('strat ${getDateStringNow()}');
Future<List>(() {
sleep(Duration(seconds: 3));
return ['AAA ${getDateStringNow()}'];
}).then((List value) {
//第一次操作回调结果 value1
print('value1 = $value');
//拿到第一次结果后,进行第二次操作:
Future(() {
sleep(Duration(seconds: 3));
String obj = 'BBB ${getDateStringNow()}';
value.add(obj);
return value;
}).then((value) {
//拿到第二次操作的回调结果;
print('value2 = $value');
});
});
print('end ${getDateStringNow()}');
}
1.2 Future嵌套的链式调用
//Future的嵌套使用2
void FutureTest3() {
print('strat ${getDateStringNow()}');
Future<List>(() {
//第一次异步操作
sleep(Duration(seconds: 3));
return ['AAA ${getDateStringNow()}'];
}).then((List value) {
//得到第一次异步操作的回调,并且执行第二次异步操作
print(value);
sleep(Duration(seconds: 3));
value.add('BBB ${getDateStringNow()}');
return value;
}).then((value) {
//得到第二次异步操作的回调
print(value);
});
print('end ${getDateStringNow()}');
}
1.3 Future 的其他使用方式:
void FutureTest4() {
//成功回调的链式语法
Future.value('what the fuck!').then((value) => print(value));
Map<String, int> error = {'value': 1};
//异常抛出
Future<Map<String, int>>.error(error).catchError((er) {
print(er.runtimeType);
var result = er['value'];
print(result);
});
//延迟执行
Future.delayed(Duration(seconds: 3), () {
return 'delayed Future';
}).then((value) => print(value));
}
main(List<String> args) {
//5.Future的带参写法
getNewSting('aaa').then((value) {
print(value);
return getNewSting(value);
}).then((value) {
print(value);
return getNewSting(value);
}).then((value) {
print(value);
});
}
int i = 0;
Future getNewSting(String msg) {
i++;
msg = '${msg}-$i';
return Future.value(msg);
}
//写法2:
main(List<String> args) async {
var f1 = await getNewSting('hello flutter');
print(f1);
var f2 = await getNewSting(f1);
print(f2);
var f3 = await getNewSting(f2);
print(f3);
var f4 = getNewSting(f3);
print(f4.runtimeType);
}
Future getNewSting(String msg) {
return Future(() {
sleep(Duration(seconds: 3));
return '${msg} + hello flutter';
});
}
//优化写法
2.await & async
await
, async
是什么东西?
- 它们是Dart语法中 关键字,所以在写代码的时候,是不能使用这种变量名的
- 使用
await
,async
可以实现使用同步代码的格式
去实现异步调用的效果
- 并且通过一个
async
函数可以返回一个Future
的instance
2.1 案例代码演练
import 'dart:io';
import 'lib/rf_timetool.dart';
main(List<String> args) {
print('main start ${getDateStringNow()}');
getNetworkData().then((value) => print(value));
print('main end ${getDateStringNow()}');
}
/*
await 和 async 的使用需要解决的两个问题
1. await 必须在 async 函数中调用
2. async 函数返回的结果必须是Future;
*/
Future<String> getNetworkData() async {
await sumMethod();
return 'you have get datas from net ${getDateStringNow()}';
}
//这个地方还有一个点需要注意
// async 函数只能返回 Future,但是我们可以看到上面这个函数字面上返回一个String
//其实dart 语法运行时会自动帮我们把String包装成 Future的retuan value
void sumMethod() {
int a = 0;
for (var i = 0; i < 100; i++) {
a += i;
}
print('$a ${getDateStringNow()}');
}
3.多核CPU的利用
3.1.Isolate的概念
Dart语法中有一个Isolate的概念,Isolate是什么意思?
- 我们已经知道Dart是单线程的,这个线程有自己可以访问的内存空间,以及需要运行的事件循环;
- 我们可以将这个空间系统称作为Isolate;
- 比如Flutter中就有一个Root Isolate,负责运行Flutter代码,比如UI渲染和用户交互等;
- 在Isolate中资源隔离做的非常好.每个Isolate都有自己的Even roop 和 Queue
- Isolate 之间不共享任何的资源,只依靠消息机制通讯,因此就没有资源抢占的问题,
- 但是如果只有一个Isolate的话,意味着永远只能利用一个线程,对于多个的设备来说就是一种资源浪费.
如果在开发中,有很多的的耗时计算,开发者完全可以根据自己的需求创建Isolate,在独立的Isolate中完成,计算操作.
3.1.1如何创建Isolate
创建Isolate比较简单:
import 'dart:io';
import 'dart:isolate';
import 'lib/rf_timetool.dart';
main(List<String> args) {
print('main start ${DateTime.now()}');
//开辟另一个Isolate执行耗时操作
Isolate.spawn(caculate, 100).then((value) {
print('value = $value');
print('${getDateStringNow()}');
});
print('main end ${DateTime.now()}');
}
caculate(int count) {
sleep(Duration(seconds: 3));
// print('${getDateStringNow()}');
var total = 0;
for (var i = 0; i <= count; i++) {
total += i;
}
print(total);
return total;
}
Isolate之间的通讯:
import 'dart:io';
import 'dart:isolate';
main(List<String> args) async {
print('main start ${DateTime.now()}');
//1. 创建管道
ReceivePort port = ReceivePort();
//2.创建Isolata
Isolate isolate = await Isolate.spawn(foo, port.sendPort);
//3.监听管道
port.listen((message) {
print('$message , ${DateTime.now()}');
//关闭监听;
port.close();
// 销毁被监听的Isolate
isolate.kill();
});
print('main end ${DateTime.now()}');
}
foo(SendPort senport) {
sleep(Duration(seconds: 3));
int total = 0;
for (var i = 0; i < 100000000; i++) {
total += i;
}
return senport.send('total = ${total}');
}
网友评论