Flutter 假异步的实现示例
就像 android 有 handle 一样,消息队列这东西好像还真是系统必备,flutter 也有自己的消息队列,只不过队列直接封装在了 dart 的线程类型 isolate 里面了,不过 flutter 还是提供了 futrue
这个 api 来专门来操作各种消息,以及实现基于消息队列的假异步
flutter 的“异步”机制
这里的异步是加了引号的,可见此异步非真异步,而是假异步。flutter 的 异步
不是开新线程,而是往所属线程的 消息队列
中添加任务,当然大家也可以按上文那样自己展开真异步操作
flutter 对代码分2类: 同步代码和异步代码
- 同步代码:传统一行行写下来,一行行执行的代码
- 异步代码:通过 future api 把任务添加到 isolate 所属消息队列执行的伪异步
- 执行顺序:先运行同步代码,再运行异步代码
为啥,很明显啊,异步代码是往消息队列里添加任务,那肯定得等现在的代码运行完了,线程有空闲了才能开始执行消息队列里的任务呀~
举个例子:
void test() { 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((){ // ...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): aa i/flutter (10573): futrue i/flutter (10573): bb
future.delayed()
- 延迟任务,指定xx时间后把任务添加到消息队列,要是消息队列前面有人执行的时间太长了,那么执行时间点就不能把握了,这点大家要知道
void test() { 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),(){ 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<testwidget> { int _count = 0; @override widget build(buildcontext context) { return material( flatbutton( onpressed: () async { _count = counteven(1000000000); setstate(() {}); }, child: text( _count.tostring(), )), ); }
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<string> getdata() async { data = await http.get(uri.encodefull(url), headers: {"accept": "application/json"}); }
futrue<string>
我们可以不写,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: (){ });
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 追女攻略:从搭讪开始,总有一款你需要
下一篇: 爆笑的老公与老婆