平時咱們在寫業務邏輯的時候,確定都會與網絡打交道,那確定也就避免不了異步請求,代碼相似以下:微信
int getData() async {
Response r = await Dio().get('https://www.baidu.com');
return r.data;
}
複製代碼
這段代碼相信不少人都很是很是熟悉了,咱們也都知道 async 是什麼意思,那加上一個星號,你還知道嗎?網絡
加上星號其實就是「函數生成器」的意思。異步
那咱們先從「sync/sync*」提及。async
「sync」咱們都知道是默認程序運行的狀態,舉個例子:函數
foo1 (){
print('foo1 start');
for(int i = 0; i < 3; i++){
print(i);
}
print('foo1 stop');
}
複製代碼
當咱們在 main
函數裏運行,結果你們應該都很清楚:ui
foo1 startspa
0 1 2code
foo1 stopcdn
那所謂的函數生成器呢?接口
被「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」來入羣。
推薦閱讀: