平时我们在写业务逻辑的时候,肯定都会与网络打交道,那肯定也就避免不了异步请求,代码类似如下:
int getData() async {
Response r = await Dio().get('https://www.baidu.com');
return r.data;
}
这段代码相信很多人都非常非常熟悉了,我们也都知道 async 是什么意思,那加上一个星号,你还知道吗?
加上星号其实就是「函数生成器」的意思。
那我们先从「sync/sync*」说起。
「sync」我们都知道是默认程序运行的状态,举个例子:
foo1 (){
print('foo1 start');
for(int i = 0; i < 3; i++){
print(i);
}
print('foo1 stop');
}
当我们在 main
函数里运行,结果大家应该都很清楚:
foo1 start
0 1 2
foo1 stop
那所谓的函数生成器呢?
被「sync*」标记的函数,一定要返回一个 「Iterable」,这样的函数生成器叫做同步生成器:
Iterable<int> foo2() sync*{
print('foo2 start');
for(int i = 0; i < 3; i++){
print('运行了foo2,当前index:${i}');
yield i;
}
print('foo2 stop');
}
这回我们在 main
函数里运行 foo2()
,会出现什么效果?
答案是什么也不会发生,print也没有打印。
这是为什么?
当我们调用 foo2()
的时候,这里会马上返回一个 Iterable
,就像网络请求会马上返回一个 Feature
一样。
但是在我们没有调用 Iterable
的 moveNext
的时候,当前函数体是不会执行的。
而当我们调用了 moveNext
方法后,代码会执行到 yield
关键字的位置,并且在这里停住。
当我们再一次调用 moveNext
后,会再恢复执行,然后再次停到 yield
关键字的位置,依次循环,当没有下一个值得时候,函数会隐式的调用 return方法来终止函数。
来看一下调用方式和结果:
var b = foo2().iterator;
print('还没开始调用 moveNext');
b.moveNext();
print('第${b.current}次moveNext');
b.moveNext();
print('第${b.current}次moveNext');
b.moveNext();
print('第${b.current}次moveNext');
结果为:
还没开始调用 moveNext
foo2 start
运行了foo2,当前index:0
第0次moveNext
运行了foo2,当前index:1
第1次moveNext
运行了foo2,当前index:2
第2次moveNext
从运行结果上来看,我们的说法是正确的,下面就来说一下异步生成器。
说异步生成器之前,先来说一下普通的异步调用。
现在有一个这样的需求,我想每隔一秒钟请求一下数据,一共请求10次,看看有没有人关注我等等,
如果使用原始的 async,该怎么做?
getData() async {
for (int i = 0; i < 10; i++){
await Future.delayed(Duration(seconds: 1), ()async {
Data data = await getXXX();
setState(){
//业务逻辑
};
});
}
}
这里使用循环,然后每一秒钟请求依次接口,返回数据后 setState();
这样肯定不行,因为你不可能一两秒钟就 setState()一次,
这个时候 async* 就派上用场了:
Stream<Data> getData() async* {
for (int i = 0; i < 10; i++){
await Future.delayed(Duration(seconds: 1));
yield await getXXX();
}
}
在页面上,我们可以用 StreamBuilder
来包住,这样每次返回数据就不用 setState() 了。
其实函数生成器可能一年都用不上一两次,但是当你用到之后,就会觉得真的很舒服。
其实我个人认为这种函数生成器还有一种作用就是可以让一个函数返回多个值。
另我个人创建了一个「Flutter 交流群」,可以添加我个人微信 「17610912320」来入群。
推荐阅读:
Flutter | 思路解析 WPopupMenu 仿微信聊天长按弹出菜单
Flutter | 一个关于背景颜色引发的打脸惨案
Flutter | 自定义一个 Stepper 步骤组件