十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
Flutter中怎么实现假异步,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
十载的九台网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。成都全网营销的优势是能够根据用户设备显示端的尺寸不同,自动调整九台建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联从事“九台网站设计”,“九台网站推广”以来,每个客户项目都认真落实执行。
Flutter 的“异步”机制
这里的异步是加了引号的,可见此异步非真异步,而是假异步。Flutter 的 异步
不是开新线程,而是往所属线程的 消息队列
中添加任务,当然大家也可以按上文那样自己展开真异步操作
Flutter 对代码分2类: 同步代码和异步代码
同步代码:传统一行行写下来,一行行执行的代码异步代码:通过 Future API 把任务添加到 Isolate 所属消息队列执行的伪异步执行顺序:先运行同步代码,再运行异步代码
为啥,很明显啊,异步代码是往消息队列里添加任务,那肯定得等现在的代码运行完了,线程有空闲了才能开始执行消息队列里的任务呀~
举个例子:
void test() { print("AA"); Future(() => print("Futrue")); print("BB");}~~~~~~~~~~~log~~~~~~~~~~~~~I/flutter (10064): AAI/flutter (10064): BBI/flutter (10064): Futrue
print("Futrue"))
任务等到最后才执行的...
Flutter 提供了往 消息队列
添加数据的 API: Future
往 MicroTask 队列添加任务
scheduleMicrotask((){ // ...code goes here...}); new Future.microtask((){ // ...code goes here...});
往 Event 队列添加任务
new Future(() { // ...code goes here...});
Future 的基本使用
Future
对象是 Flutter 专门提供的,基于消息队列实现异步的类,Future 对象会把自身当做一个任务添加到消息队列中去排队执行
Future
对象接受的是一个函数,就是要执行的任务,用 () => ...
简写也是可以的
void task() { print("AA");}var futrue = Future(task);
创建 Future 任务方式:
Future()Future.microtask()Future.sync() - 同步任务Future.value()Future.delayed() - 延迟xx时间添加任务Future.error() - 错误处理
我们来看几个代表性的:
Future.sync()
- 阻塞任务,会阻塞当前代码,sync 的任务执行完了,代码才能走到下一行
void test() { print("AA"); Future.sync(() => print("Futrue")); print("BB");}~~~~~~~~~~~~log~~~~~~~~~~~~~~I/flutter (10573): AAI/flutter (10573): FutrueI/flutter (10573): BB
Future.delayed()
- 延迟任务,指定xx时间后把任务添加到消息队列,要是消息队列前面有人执行的时间太长了,那么执行时间点就不能把握了,这点大家要知道
void test() { print("AA"); Future.delayed(Duration(milliseconds: 500),() => print("Futrue")); print("BB");}~~~~~~~~~~~~log~~~~~~~~~~~~~~I/flutter (10573): AAI/flutter (10573): BBI/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),(){ throw Exception("AAA"); }).then((value){ print(value); }).catchError((error){ print(error); });
.whenComplete
- 不管是否发生异常,在执行完成后,都会执行该方法
Future.delayed(Duration(seconds: 1), () { throw Exception("AAA"); }).then((value) { print(value); }).catchError((error) { print(error); }).whenComplete(() { print("complete..."); });
.wait
- 可以等待所有的 future 都执行完毕再走 then 的方法
Future.wait([ // 2秒后返回结果 Future.delayed(new Duration(seconds: 2), () { return "hello"; }), // 4秒后返回结果 Future.delayed(new Duration(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..."); }await anysncTest();
本质上 await 调用的方法其实是把这个方法包装到 Futrue 中去消息队列里执行,只不过是: Future.sync()
阻塞式的 Future 任务
这 async
在布局中也是可以直接用的
class TestWidgetState extends State
async/await 是阻塞式的函数
实验1:
// 这是异步任务代码 aaa() async{ print("main1..."); await anysncTest(); print("main2..."); print("main3..."); } anysncTest() async { print("async 休眠 start..."); sleep(Duration(seconds: 1)); print("async 休眠 end..."); } // 点击按钮去执行 Widget build(BuildContext context) { return RaisedButton( child: (Text("click!")), onPressed: () async { await aaa(); }, ); }
可以看到 async/await
执行的方法的确是阻塞时的,至少在这个 async 方法里绝对是阻塞式的
实验2:
那么范围扩展一下,在 async 外面再来看看 async/await
是不是阻塞式的? 有人说 async/await 和协程一样
,协程的关键点在于非竞争式资源,协程的概念中,当多个协程中有一个协程挂起之后,并不会阻塞 CPU,CPU 回去执行其他协程方法,直到有空闲了再来执行之前挂起后恢复的协程,虽然在协程看来我挂起了线程,但其实 CPU 不会被协程挂起阻塞,这点就是协程的核心优势,大大提升多线程下的执行效率。
从这点出发我们就能知道 async/await
是不是又一个协程了,看看他阻塞 CPU,我们在 await 之后看看 async 后面的代码会不会执行就 OK了
// 还是这组方法 aaa() async{ print("main1..."); await anysncTest(); print("main2..."); print("main3..."); } anysncTest() async { print("async 休眠 start..."); sleep(Duration(seconds: 1)); print("async 休眠 end..."); } // 执行,注意此时按钮的点击方法不是 async 的 Widget build(BuildContext context) { return RaisedButton( 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..."); await anysncTest(); print("main2..."); print("main3..."); } anysncTest() async { print("async 休眠 start..."); sleep(Duration(seconds: 1)); print("async 休眠 end..."); } // 执行 Widget build(BuildContext context) { return RaisedButton( child: (Text("click!")), onPressed: () async { print("click1..."); await 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): main2...I/flutter ( 5733): main3...I/flutter ( 5733): click2...I/flutter ( 5733): click3...
这样看的话在 async 方法内部,是严格按照顺序执行的
async 方法的格式
1. async 标记的方法返回值都是 Futrue 类型的
上文书哦说 await 调用的方法返回的都是 Futrue 对象,那么就是说在声明 async 函数时,返回值都是 Futrue 类型的,Futrue 内部包裹实际的返回值类型
Futrue
Futrue
我们可以不写,dart 也会自动推断出来,但是我们一定要知道是 Futrue 类型的,要不有时会报类型错误
我们在用的时候都是配合 await 使用的,这时候可以直接用具体类型值接返回值了
String data = await getData();
记住:
Future就是event,很多Flutter内置的组件比如前几篇用到的Http(http请求控件)的get函数、RefreshIndicator(下拉手势刷新控件)的onRefresh函数都是event。每一个被await标记的句柄也是一个event,每创建一个Future就会把这个Future扔进event queue中排队等候安检~
Stream
Stream
和 Future
一样都是假异步操作,区别是 Stream
可以接受多次数据,我不详细展开了,有待以后详细研究
Stream.fromFutures([ // 1秒后返回结果 Future.delayed(new Duration(seconds: 1), () { return "hello 1"; }), // 抛出一个异常 Future.delayed(new Duration(seconds: 2),(){ throw AssertionError("Error"); }), // 3秒后返回结果 Future.delayed(new Duration(seconds: 3), () { return "hello 3"; })]).listen((data){ print(data);}, onError: (e){ print(e.message);},onDone: (){});
看完上述内容,你们掌握Flutter中怎么实现假异步的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!