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 将返回firstFuturesecondFuture中第一个返回结果的值

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"

img

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等事件,所以要谨慎使用,如下为流程图:

event queue和microtask queue

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

《379页Android开发面试宝典》

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

op一直在循环之中,当主线程发现事件队列不为空时发现,就会取出事件,并且执行。

microtask queue中事件优先于event queue执行,当有任务发送到microtask队列时,会在_当前event执行完成后_,阻塞当前event queue转而去执行microtask queue中的事件,这样为Dart提供了任务插队的解决方案。

event queue的阻塞意味着app无法进行UI绘制,响应鼠标和I/O等事件,所以要谨慎使用,如下为流程图:

event queue和microtask queue

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

[外链图片转存中…(img-GJxCbh7u-1646141862287)]

《379页Android开发面试宝典》

[外链图片转存中…(img-AJsl9pgc-1646141862288)]

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

[外链图片转存中…(img-APWWXDbF-1646141862288)]

因为文件太多,全部展示会影响篇幅,暂时就先列举这些部分截图,大家可以**点击这里**自行领取。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐