服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Flutter 假异步的实现示例

Flutter 假异步的实现示例

2022-11-04 14:04前行的乌龟 Android

这篇文章主要介绍了Flutter 假异步的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

就像 android 有 handle 一样,消息队列这东西好像还真是系统必备,Flutter 也有自己的消息队列,只不过队列直接封装在了 Dart 的线程类型 Isolate 里面了,不过 Flutter 还是提供了 Futrue 这个 API 来专门来操作各种消息,以及实现基于消息队列的假异步

Flutter 的“异步”机制

这里的异步是加了引号的,可见此异步非真异步,而是假异步。Flutter 的 异步 不是开新线程,而是往所属线程的 消息队列 中添加任务,当然大家也可以按上文那样自己展开真异步操作

Flutter 对代码分2类: 同步代码和异步代码

  • 同步代码:传统一行行写下来,一行行执行的代码
  • 异步代码:通过 Future API 把任务添加到 Isolate 所属消息队列执行的伪异步
  • 执行顺序:先运行同步代码,再运行异步代码

为啥,很明显啊,异步代码是往消息队列里添加任务,那肯定得等现在的代码运行完了,线程有空闲了才能开始执行消息队列里的任务呀~

举个例子:

?
1
2
3
4
5
6
7
8
9
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 队列添加任务

?
1
2
3
4
5
6
7
scheduleMicrotask((){
 // ...code goes here...
});
 
new Future.microtask((){
  // ...code goes here...
});

往 Event 队列添加任务

?
1
2
3
new Future(() {
 // ...code goes here...
});

Future 的基本使用

Future 对象是 Flutter 专门提供的,基于消息队列实现异步的类,Future 对象会把自身当做一个任务添加到消息队列中去排队执行

Future 对象接受的是一个函数,就是要执行的任务,用 () => ... 简写也是可以的

?
1
2
3
4
5
void task() {
  print("AA");
}
 
var futrue = Future(task);

创建 Future 任务方式:

  • Future()
  • Future.microtask()
  • Future.sync() - 同步任务
  • Future.value()
  • Future.delayed() - 延迟xx时间添加任务
  • Future.error() - 错误处理

我们来看几个代表性的:

Future.sync() - 阻塞任务,会阻塞当前代码,sync 的任务执行完了,代码才能走到下一行

?
1
2
3
4
5
6
7
8
9
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时间后把任务添加到消息队列,要是消息队列前面有人执行的时间太长了,那么执行时间点就不能把握了,这点大家要知道

?
1
2
3
4
5
6
7
8
9
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

?
1
2
3
4
5
6
Future.delayed(Duration(seconds: 1),(){
  print(("AAA"));
  return "AA";
 }).then((value){
  print(value);
 });

.catchError - future 不管在任何位置发生了错误,都会立即执行 catchError

?
1
2
3
4
5
6
7
Future.delayed(Duration(seconds: 1),(){
 throw Exception("AAA");
}).then((value){
 print(value);
}).catchError((error){
 print(error);
});

.whenComplete - 不管是否发生异常,在执行完成后,都会执行该方法

?
1
2
3
4
5
6
7
8
9
Future.delayed(Duration(seconds: 1), () {
  throw Exception("AAA");
 }).then((value) {
  print(value);
 }).catchError((error) {
  print(error);
 }).whenComplete(() {
  print("complete...");
 });

.wait - 可以等待所有的 future 都执行完毕再走 then 的方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
  });

大家想想啊

?
1
2
3
4
Futrue()
  .then()
  .then()
  ...

这样的链式写法不就是标准的去 callback 回调地狱的方式嘛

async/await 关键字

async/await 这组关键字是系统提供的另一种实现 异步 任务的 API, async/await 底层还是用 Futrue 实现的,从使用上看是对 Futrue 的简化,本质上还是基于 消息队列 实现的异步,是 假异步 ,和 Isoalte 是不一样的

async/await 的特点就是: 成对出现

  • async - 修饰方法,用 async 声明的方法都是耗时的
  • await - 调用 async 方法时使用,也可以在 async 方法内部是适用,await 表示阻塞,下面的任务必须等 await 调用的方法执行完之后才能执行

比如这样:

?
1
2
3
4
5
6
7
anysncTest() async {
  print("async 休眠 start...");
  sleep(Duration(seconds: 1));
  print("async 休眠 end...");
 }
 
await anysncTest();

本质上 await 调用的方法其实是把这个方法包装到 Futrue 中去消息队列里执行,只不过是: Future.sync() 阻塞式的 Future 任务

async 在布局中也是可以直接用的

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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:

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 这是异步任务代码
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();
  },
 );
}

Flutter 假异步的实现示例

可以看到 async/await

执行的方法的确是阻塞时的,至少在这个 async 方法里绝对是阻塞式的

实验2:

那么范围扩展一下,在 async 外面再来看看 async/await 是不是阻塞式的? 有人说 async/await 和协程一样 ,协程的关键点在于非竞争式资源,协程的概念中,当多个协程中有一个协程挂起之后,并不会阻塞 CPU,CPU 回去执行其他协程方法,直到有空闲了再来执行之前挂起后恢复的协程,虽然在协程看来我挂起了线程,但其实 CPU 不会被协程挂起阻塞,这点就是协程的核心优势,大大提升多线程下的执行效率。

从这点出发我们就能知道 async/await 是不是又一个协程了,看看他阻塞 CPU,我们在 await 之后看看 async 后面的代码会不会执行就 OK了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 还是这组方法
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...");
  },
 );
}
?
1
2
3
4
5
6
7
8
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 的看看执行顺序

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 还是这组方法
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...");
  },
 );
}
?
1
2
3
4
5
6
7
8
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 内部包裹实际的返回值类型

?
1
2
3
Futrue<String> getData() async {
 data = await http.get(Uri.encodeFull(url), headers: {"Accept": "application/json"}); 
}

Futrue<String> 我们可以不写,dart 也会自动推断出来,但是我们一定要知道是 Futrue 类型的,要不有时会报类型错误

我们在用的时候都是配合 await 使用的,这时候可以直接用具体类型值接返回值了

?
1
String data = await getData();

记住:

Future就是event,很多Flutter内置的组件比如前几篇用到的Http(http请求控件)的get函数、RefreshIndicator(下拉手势刷新控件)的onRefresh函数都是event。每一个被await标记的句柄也是一个event,每创建一个Future就会把这个Future扔进event queue中排队等候安检~

Stream

StreamFuture 一样都是假异步操作,区别是 Stream 可以接受多次数据,我不详细展开了,有待以后详细研究

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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: (){
 
});

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://juejin.im/post/5db6dab16fb9a02047525e84

延伸 · 阅读

精彩推荐