Flutter 假异步的实现示例
就像android有handle一样,消息队列这东西好像还真是系统必备,Flutter也有自己的消息队列,只不过队列直接封装在了Dart的线程类型Isolate里面了,不过Flutter还是提供了Futrue这个API来专门来操作各种消息,以及实现基于消息队列的假异步
Flutter的“异步”机制
这里的异步是加了引号的,可见此异步非真异步,而是假异步。Flutter的异步不是开新线程,而是往所属线程的消息队列中添加任务,当然大家也可以按上文那样自己展开真异步操作
Flutter对代码分2类:同步代码和异步代码
- 同步代码:传统一行行写下来,一行行执行的代码
- 异步代码:通过FutureAPI把任务添加到Isolate所属消息队列执行的伪异步
- 执行顺序:先运行同步代码,再运行异步代码
为啥,很明显啊,异步代码是往消息队列里添加任务,那肯定得等现在的代码运行完了,线程有空闲了才能开始执行消息队列里的任务呀~
举个例子:
voidtest(){
print("AA");
Future(()=>print("Futrue"));
print("BB");
}
~~~~~~~~~~~log~~~~~~~~~~~~~
I/flutter(10064):AA
I/flutter(10064):BB
I/flutter(10064):Futrue
print("Futrue"))任务等到最后才执行的...
Flutter提供了往消息队列添加数据的API:Future
往MicroTask队列添加任务
scheduleMicrotask((){
//...codegoeshere...
});
newFuture.microtask((){
//...codegoeshere...
});
往Event队列添加任务
newFuture((){
//...codegoeshere...
});
Future的基本使用
Future对象是Flutter专门提供的,基于消息队列实现异步的类,Future对象会把自身当做一个任务添加到消息队列中去排队执行
Future对象接受的是一个函数,就是要执行的任务,用()=>...简写也是可以的
voidtask(){
print("AA");
}
varfutrue=Future(task);
创建Future任务方式:
- Future()
- Future.microtask()
- Future.sync()-同步任务
- Future.value()
- Future.delayed()-延迟xx时间添加任务
- Future.error()-错误处理
我们来看几个代表性的:
Future.sync()-阻塞任务,会阻塞当前代码,sync的任务执行完了,代码才能走到下一行
voidtest(){
print("AA");
Future.sync(()=>print("Futrue"));
print("BB");
}
~~~~~~~~~~~~log~~~~~~~~~~~~~~
I/flutter(10573):AA
I/flutter(10573):Futrue
I/flutter(10573):BB
Future.delayed()-延迟任务,指定xx时间后把任务添加到消息队列,要是消息队列前面有人执行的时间太长了,那么执行时间点就不能把握了,这点大家要知道
voidtest(){
print("AA");
Future.delayed(Duration(milliseconds:500),()=>print("Futrue"));
print("BB");
}
~~~~~~~~~~~~log~~~~~~~~~~~~~~
I/flutter(10573):AA
I/flutter(10573):BB
I/flutter(10573):Futrue
Future的链式调用
Future也支持链式调用的,在API使用上也是很灵活的,提供了下面的选择给大家
.then-在Future执行完后执行,相当于一个callback,而不是重新创建了一个Future
Future.delayed(Duration(seconds:1),(){
print(("AAA"));
return"AA";
}).then((value){
print(value);
});
.catchError-future不管在任何位置发生了错误,都会立即执行catchError
Future.delayed(Duration(seconds:1),(){
throwException("AAA");
}).then((value){
print(value);
}).catchError((error){
print(error);
});
.whenComplete-不管是否发生异常,在执行完成后,都会执行该方法
Future.delayed(Duration(seconds:1),(){
throwException("AAA");
}).then((value){
print(value);
}).catchError((error){
print(error);
}).whenComplete((){
print("complete...");
});
.wait-可以等待所有的future都执行完毕再走then的方法
Future.wait([
//2秒后返回结果
Future.delayed(newDuration(seconds:2),(){
return"hello";
}),
//4秒后返回结果
Future.delayed(newDuration(seconds:4),(){
return"world";
})
]).then((results){
print(results[0]+results[1]);
}).catchError((e){
print(e);
});
大家想想啊
Futrue() .then() .then() ...
这样的链式写法不就是标准的去callback回调地狱的方式嘛
async/await关键字
async/await这组关键字是系统提供的另一种实现异步任务的API,async/await底层还是用Futrue实现的,从使用上看是对Futrue的简化,本质上还是基于消息队列实现的异步,是假异步,和Isoalte是不一样的
async/await的特点就是:成对出现
- async-修饰方法,用async声明的方法都是耗时的
- await-调用async方法时使用,也可以在async方法内部是适用,await表示阻塞,下面的任务必须等await调用的方法执行完之后才能执行
比如这样:
anysncTest()async{
print("async休眠start...");
sleep(Duration(seconds:1));
print("async休眠end...");
}
awaitanysncTest();
本质上await调用的方法其实是把这个方法包装到Futrue中去消息队列里执行,只不过是:Future.sync()阻塞式的Future任务
这async在布局中也是可以直接用的
classTestWidgetStateextendsState{ int_count=0; @override Widgetbuild(BuildContextcontext){ returnMaterial( FlatButton( onPressed:()async{ _count=countEven(1000000000); setState((){}); }, child:Text( _count.toString(), )), ); }
async/await是阻塞式的函数
实验1:
//这是异步任务代码
aaa()async{
print("main1...");
awaitanysncTest();
print("main2...");
print("main3...");
}
anysncTest()async{
print("async休眠start...");
sleep(Duration(seconds:1));
print("async休眠end...");
}
//点击按钮去执行
Widgetbuild(BuildContextcontext){
returnRaisedButton(
child:(Text("click!")),
onPressed:()async{
awaitaaa();
},
);
}
执行的方法的确是阻塞时的,至少在这个async方法里绝对是阻塞式的
实验2:
那么范围扩展一下,在async外面再来看看async/await是不是阻塞式的?有人说async/await和协程一样,协程的关键点在于非竞争式资源,协程的概念中,当多个协程中有一个协程挂起之后,并不会阻塞CPU,CPU回去执行其他协程方法,直到有空闲了再来执行之前挂起后恢复的协程,虽然在协程看来我挂起了线程,但其实CPU不会被协程挂起阻塞,这点就是协程的核心优势,大大提升多线程下的执行效率。
从这点出发我们就能知道async/await是不是又一个协程了,看看他阻塞CPU,我们在await之后看看async后面的代码会不会执行就OK了
//还是这组方法
aaa()async{
print("main1...");
awaitanysncTest();
print("main2...");
print("main3...");
}
anysncTest()async{
print("async休眠start...");
sleep(Duration(seconds:1));
print("async休眠end...");
}
//执行,注意此时按钮的点击方法不是async的
Widgetbuild(BuildContextcontext){
returnRaisedButton(
child:(Text("click!")),
onPressed:(){
print("click1...");
aaa();
print("click2...");
print("click3...");
},
);
}
I/flutter(5733):click1... I/flutter(5733):main1... I/flutter(5733):async休眠start... I/flutter(5733):async休眠end... I/flutter(5733):click2... I/flutter(5733):click3... I/flutter(5733):main2... I/flutter(5733):main3...
await阻塞是真的阻塞CPU了,所以async/await不是协程,但是大家注意啊,在await结速阻塞之后执行的是click2也就是async外部的方法,说明await标记的方法返回的都是Futrue对象的说法是正确的,队列只有在线程空闲时才会执行,显然此时线程不是空闲的,点击方法还没执行完呢
实验3:
这次做对比实验,把点击事件也变成async的看看执行顺序
//还是这组方法
aaa()async{
print("main1...");
awaitanysncTest();
print("main2...");
print("main3...");
}
anysncTest()async{
print("async休眠start...");
sleep(Duration(seconds:1));
print("async休眠end...");
}
//执行
Widgetbuild(BuildContextcontext){
returnRaisedButton(
child:(Text("click!")),
onPressed:()async{
print("click1...");
awaitaaa();
print("click2...");
print("click3...");
},
);
}
I/flutter(5733):click1... I/flutter(5733):main1... I/flutter(5733):async休眠start... I/flutter(5733):async休眠end... I/flutter(5733):main2... I/flutter(5733):main3... I/flutter(5733):click2... I/flutter(5733):click3...
这样看的话在async方法内部,是严格按照顺序执行的
async方法的格式
1.async标记的方法返回值都是Futrue类型的
上文书哦说await调用的方法返回的都是Futrue对象,那么就是说在声明async函数时,返回值都是Futrue类型的,Futrue内部包裹实际的返回值类型
FutruegetData()async{ data=awaithttp.get(Uri.encodeFull(url),headers:{"Accept":"application/json"}); }
Futrue
我们在用的时候都是配合await使用的,这时候可以直接用具体类型值接返回值了
Stringdata=awaitgetData();
记住:
Future就是event,很多Flutter内置的组件比如前几篇用到的Http(http请求控件)的get函数、RefreshIndicator(下拉手势刷新控件)的onRefresh函数都是event。每一个被await标记的句柄也是一个event,每创建一个Future就会把这个Future扔进eventqueue中排队等候安检~
Stream
Stream和Future一样都是假异步操作,区别是Stream可以接受多次数据,我不详细展开了,有待以后详细研究
Stream.fromFutures([
//1秒后返回结果
Future.delayed(newDuration(seconds:1),(){
return"hello1";
}),
//抛出一个异常
Future.delayed(newDuration(seconds:2),(){
throwAssertionError("Error");
}),
//3秒后返回结果
Future.delayed(newDuration(seconds:3),(){
return"hello3";
})
]).listen((data){
print(data);
},onError:(e){
print(e.message);
},onDone:(){
});
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。