Flutter 中的同步与异步,我的Android美团求职之路
Future.error创建一个执行结果为error的futurefactory Future.error(Object error, [StackTrace? stackTrace]) {/// …return new _Future.immediateError(error, stackTrace);}_Future.immediateError(var error, StackTrace s
Future.error
创建一个执行结果为error的future
factory Future.error(Object error, [StackTrace? stackTrace]) {
/// …
return new _Future.immediateError(error, stackTrace);
}
-
_Future.immediateError(var error, StackTrace stackTrace)
- _zone = Zone._current {
_asyncCompleteError(error, stackTrace);
}
Future.error(new Exception(“err msg”))
.then((value) => print(“err value: $value”))
.catchError((e) => print(e));
/// 执行结果为:Exception: err msg
Future.delayed
创建一个延迟执行回调的future,内部实现为Timer加延时执行一个Future
factory Future.delayed(Duration duration, [FutureOr computation()?]) {
/// …
new Timer(duration, () {
if (computation == null) {
result._complete(null as T);
} else {
try {
result._complete(computation());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
}
});
return result;
}
Future.wait
等待多个Future并收集返回结果
static Future<List> wait(Iterable<Future> futures,
{bool eagerError = false, void cleanUp(T successValue)?}) {
/// …
}
FutureBuilder结合使用:
child: FutureBuilder(
future: Future.wait([
firstFuture(),
secondFuture()
]),
builder: (context,snapshot){
if(!snapshot.hasData){
return CircularProgressIndicator();
}
final first = snapshot.data[0];
final second = snapshot.data[1];
return Text(“data $first $second”);
},
),
Future.any
返回futures集合中第一个返回结果的值
static Future any(Iterable<Future> futures) {
var completer = new Completer.sync();
void onValue(T value) {
if (!completer.isCompleted) completer.complete(value);
}
void onError(Object error, StackTrace stack) {
if (!completer.isCompleted) completer.completeError(error, stack);
}
for (var future in futures) {
future.then(onValue, onError: onError);
}
return completer.future;
}
对上述例子来说,Future.any snapshot.data 将返回firstFuture 、secondFuture中第一个返回结果的值
Future.forEach
为传入的每一个元素,顺序执行一个action
static Future forEach(Iterable elements, FutureOr action(T element)) {
var iterator = elements.iterator;
return doWhile(() {
if (!iterator.moveNext()) return false;
var result = action(iterator.current);
if (result is Future) return result.then(_kTrue);
return true;
});
}
这里边action是方法作为参数,头一次见这种形式语法还是在js中,当时就迷惑了很大一会儿,使用示例:
Future.forEach([“one”,“two”,“three”], (element) {
print(element);
});
Future.doWhile
执行一个操作直到返回false
Future.doWhile((){
for(var i=0;i<5;i++){
print(“i => $i”);
if(i >= 3){
return false;
}
}
return true;
});
/// 结果打印到 3
以上为Future中常用构造函数和方法
在Widget中使用Future
Flutter提供了配合Future显示的组件FutureBuilder,使用也很简单,伪代码如下:
child: FutureBuilder(
future: getFuture(),
builder: (context, snapshot){
if(!snapshot.hasData){
return CircularProgressIndicator();
} else if(snapshot.hasError){
return _ErrorWidget(“Error: ${snapshot.error}”);
} else {
return _ContentWidget(“Result: ${snapshot.data}”)
}
}
)
Async-await
使用
这两个关键字提供了异步方法的同步书写方式,Future提供了方便的链式调用使用方式,但是不太直观,而且大量的回调嵌套造成可阅读性差。因此,现在很多语言都引入了await-async语法,学习他们的使用方式是很有必要的。
两条基本原则:
-
定义一个异步方法,必须在方法体前声明 async
-
await关键字必须在async方法中使用
首先,在要执行耗时操作的方法体前增加async:
void main() async { ··· }
然后,根据方法的返回类型添加Future修饰
Future main() async { ··· }
现在就可以使用await关键字来等待这个future执行完毕
print(await createOrderMessage());
例如实现一个由一级分类获取二级分类,二级分类获取详情的需求,使用链式调用的代码如下:
var list = getCategoryList();
list.then((value) => value[0].getCategorySubList(value[0].id))
.then((subCategoryList){
var courseList = subCategoryList[0].getCourseListByCategoryId(subCategoryList[0].id);
print(courseList);
}).catchError((e) => (){
print(e);
});
现在来看下使用async/await,事情变得简单了多少
Future main() async {
await getCourses().catchError((e){
print(e);
});
}
Future getCourses() async {
var list = await getCategoryList();
var subCategoryList = await list[0].getCategorySubList(list[0].id);
var courseList = subCategoryList[0].getCourseListByCategoryId(subCategoryList[0].id);
print(courseList);
}
可以看到这样更加直观
缺陷
async/await 非常方便,但是还是有一些缺点需要注意
因为它的代码看起来是同步的,所以是会阻塞后面的代码执行,直到await返回结果,就像执行同步操作一样。它确实可以允许其他任务在此期间继续运行,但后边自己的代码被阻塞。
这意味着代码可能会由于_有大量await代码相继执行而阻塞_,本来用Future编写表示并行的操作,现在使用await变成了_串行_,例如,首页有一个同时获取轮播接口,tab列表接口,msg列表接口的需求
Future getBannerList() async {
return await Future.delayed(Duration(seconds: 3),(){
return “banner list”;
});
}
Future getHomeTabList() async {
return await Future.delayed(Duration(seconds: 3),(){
return “tab list”;
});
}
Future getHomeMsgList() async {
return await Future.delayed(Duration(seconds: 3),(){
return “msg list”;
});
}
使用await编写很可能会写成这样,打印执行操作的时间
Future main2() async {
var startTime = DateTime.now().second;
await getBannerList();
await getHomeTabList();
await getHomeMsgList();
var endTime = DateTime.now().second;
print(endTime - startTime); // 9
}
在这里,我们直接等待所有三个模拟接口的调用,使每个调用3s。后续的每一个都被迫等到上一个完成, 最后会看到_总运行时间为9s_,而实际我们想三个请求同时执行,代码可以改成如下这种:
Future main() async {
var startTime = DateTime.now().second;
var bannerList = getBannerList();
var homeTabList = getHomeTabList();
var homeMsgList = getHomeMsgList();
await bannerList;
await homeTabList;
await homeMsgList;
var endTime = DateTime.now().second;
print(endTime - startTime); // 3
}
将三个Future存储在变量中,这样可以同时启动,最后打印时间_仅为3s_,所以在编写代码时,我们必须牢记这点,避免性能损耗。
原理
–
线程模型
当一个Flutter应用或者Flutter Engine启动时,它会启动(或者从池中选择)另外三个线程,这些线程有些时候会有重合的工作点,但是通常,它们被称为UI线程,GPU线程,IO线程。需要注意一点这个UI线程并不是程序运行的主线程,或者说和其他平台上的主线程理解不同,通常的,Flutter将平台的主线程叫做"Platform thread"

UI线程是所有的Dard代码运行的地方,例如framework和你的应用,除非你启动自己的isolates,否则Dart将永远不会运行在其他线程。平台线程是所有依赖插件的代码运行的地方。该线程也是native frameworks为其他任务提供服务的地方,一般来说,一个Flutter应用启动的时候会创建一个Engine实例,Engine创建的时候会创建一个Platform thread为其提供服务。跟Flutter Engine的所有交互(接口调用)必须发生在Platform Thread,试图在其它线程中调用Flutter Engine会导致无法预期的异常。这跟Android/iOS UI相关的操作都必须在主线程进行相类似。
Isolates是Dart中概念,本意是隔离,它的实现功能和thread类似,但是他们之间的实现又有着本质的区别,Isolote是独立的工作者,它们之间不共享内存,而是通过channel传递消息。Dart是单线程执行代码,Isolate提供了Dart应用可以更好的利用多核硬件的解决方案。
事件循环
单线程模型中主要就是在维护着一个事件循环(Event Loop) 与 两个队列(event queue和microtask queue)当Flutter项目程序触发如点击事件、IO事件、网络事件时,它们就会被加入到eventLoop中,eventLoop一直在循环之中,当主线程发现事件队列不为空时发现,就会取出事件,并且执行。
microtask queue中事件优先于event queue执行,当有任务发送到microtask队列时,会在_当前event执行完成后_,阻塞当前event queue转而去执行microtask queue中的事件,这样为Dart提供了任务插队的解决方案。
event queue的阻塞意味着app无法进行UI绘制,响应鼠标和I/O等事件,所以要谨慎使用,如下为流程图:

《960全网最全Android开发笔记》

《379页Android开发面试宝典》

《507页Android开发相关源码解析》

op一直在循环之中,当主线程发现事件队列不为空时发现,就会取出事件,并且执行。
microtask queue中事件优先于event queue执行,当有任务发送到microtask队列时,会在_当前event执行完成后_,阻塞当前event queue转而去执行microtask queue中的事件,这样为Dart提供了任务插队的解决方案。
event queue的阻塞意味着app无法进行UI绘制,响应鼠标和I/O等事件,所以要谨慎使用,如下为流程图:

《960全网最全Android开发笔记》
[外链图片转存中…(img-GJxCbh7u-1646141862287)]
《379页Android开发面试宝典》
[外链图片转存中…(img-AJsl9pgc-1646141862288)]
《507页Android开发相关源码解析》
[外链图片转存中…(img-APWWXDbF-1646141862288)]
因为文件太多,全部展示会影响篇幅,暂时就先列举这些部分截图,大家可以**点击这里**自行领取。
更多推荐

所有评论(0)