如果想要產生一個一組一連串的數值,Dart 提供了產生器函數 (Generator Function)。
產生器函數有兩種,一種是同步產生器 (Synchronous generator),另一種是異步產生器 (Asynchornous generator)。
Iterable
物件。Stream
物件。例如:
單一數值 | 零或更多數值 | |
---|---|---|
同步產生器 | int | Iterable<int> |
異步產生器 | Future <int> |
Stream <int> |
Future
數值轉成 Stream
數值。介紹同步產生器之前,先介紹 Iterator<E>
以及 Iterable<E>
迭代器 (Iterator) 是一個簡單的介面,可以迭代一系列的值。
abstract class Iterator<E>{
bool moveNext();
E get current;
}
false
。讓某類別能夠提供特定類型的迭代器。如前面介紹過的 List
。
如何使用 ?
例如: MyStrings
類別擴展了 Iterable<String>
,所以它需要一個迭代器屬性(Iterator get iterator => string.iterator;)
class MyStrings extends Iterable<String>{
MyStrings(this.strings);
final List<String> strings;
Iterator<String> get iterator =>
string.iterator;
}
接者,我們就可以使用 for-in
來迭代走訪迭代器裡的每一個元素。
void main(){
final myStrings = MyStrings([
'How',
'Are',
'You'
]);
for (final str in myStrings){
print(str);
}
}
//How
//Are
//You
for-in
迴圈,Dart 會自動使用迭代器裡的 moveNext 以及 current。除了可以使用 for-in
迴圈之外,也可以使用 forEach
來迭代走訪每一個元素。
myStrings.forEach((value) => print(str);)
有了 Iterable
、 Iterator
的概念之後,我們來看看怎麼使用同步產生器。
利用關鍵字 sync*
將函式定義為同步產生器,再利用關鍵字 yield
返回一個值。
範例:
Iterable<int> getRange(int start, int finish) sync*{
for(int i = start; i <= finish; i++ ){
yield i;
}
}
for
迴圈才會開始啟動,然後迭代出數值。void main(){
final numbers = getRange(1, 5);
for(final val in numbers){
print(val);
}
}
//1
//2
//3
//4
//5
Iterable<int> getRange(int start, int finish) sync*{
if(start <= finish){
yield start;
for(final val in getRange(start+1, finish)){
yield val;
}
}
}
可以將遞迴改為
Iterable<int> getRange(int start, int finish) sync*{
if(start <= finish){
yield start;
yield* getRange(start + 1, finish);
}
}
yield*
可以在每一次的遞迴取得一個值,而不需要 for 迴圈。在 Stream 章節有提到,可以使用 async*
來建立 Stream<T>
。這就是異步產生器。
我們知道,Stream 就是一連串的 Future。
例如:
有一個函數 fetchString() 會回傳:
Future<String> fetchString(String str) async{
return Future.value(str);
}
Future<String>
的值,並且可以使用 then
當 Future 結束之後取到真實的值。void main(){
fetchString("test").then((value) => print(value));
}
//test
如果需要得到一連串的 Future 呢?
可以使用 async*
來回傳一個 Stream 。
Stream<String> fetchStrings(List<String> strings) async* {
for(var string in strings){
yield await getString(string);
}
}
同樣的,我們可以使用遞迴的方式取得每一個 Stream 的值
Stream<String> fetchStrings(List<String> strings) async* {
yield* fetchStrings(strings);
}
yield*
來取得遞迴時每一個 Future 的值利用產生器我們可以產生一連串的同步資料或是異步資料,而不需要使用基本型態或是其他類別。
關鍵字 sync*
用來產生一連串的同步資料;關鍵字 async*
則是用來取得一連串的異步資料。
同步資料產生後,其型別為 Iterable
可以使用 for-in 或是 forEach 取得所有的元素。
異步資料產生之後,其型別為 Stream
可以使用 listener 來取得每一個 Future 元素。
另外,使用 yield*
加上遞迴的函數,那麼就可以直接用一行程式碼取得同步或異步的資料。