在 dart 世界裡, 透過 sync* 關鍵字標註一個函式讓它可以用 yield 暫停後回傳一筆資料(實際上是包裝成一個 iterable 物件直接回傳,因此不會 block 程式),讓後續用 forEach 依序讀取, 若將 yield 放進一個 for 迴圈, 就能產生一個數列:
IntRange(int start, int end) sync* {for(int i=start; i<=end; i++) yield i;}//一個整數數列
void main() => IntRange(1,10).forEach(print); // 實際上是一個 iterable物件
或是搭配 yield * 直接呼叫遞迴函式(recursive funtion), 讓它看似沒有 loop 的序列產生器:
IntRange(int start, int end) sync* { // 打包成 Iterable 類型的物件
if (start <= end) { // 結束遞迴的條件
yield start; // 產生初始值
yield * IntRange(start+1, end); // 事件(event loop)註冊, 下次繼續用遞迴函式產生
}
}
void main() => IntRange(1,10).forEach(print); // 實際上是一個 iterable物件
當來到非同步世界後, 同樣可以用 yield 產生序列, 但要改用 async* (非同步產生器)標註函式, async * 所產生的是序列會包裝成資料流 stream, 而非 iterable:
IntStrean(int start, int end) async* {for(int i=start; i<=end; i++) yield i;}//一串整數流
void main() => IntStrean(1,10).forEach(print);//實際上是一個 Stream<int> 物件
若是 yieild Future 物件的話, 也就是非同步串, 實際上 Future 是一個抽象類型(abstract class), 它並非當下產生資料, 而是當完成後 then( )或是等待他完成 await 才現出物件的原型:
futureStream(int start, int end) async * { for(int i=start; i <=end; i++) yield Future.value(i); } // 實際是回傳 Stream<Future>
void main() => futureStream(1,10).forEach( (f) => f.then(print));// f 實際上是 Future 物件, 用 then( ) 現出物件原型
用 yield await 看似等待完成後才回傳, 實際只是註冊到 event loop , 順便將物件打包成相對應之類型,他並不會 block 程式的運行:
intStream(int start, int end) async* { // 打包成 Stream 類型的物件
for(int i=start; i <=end; i++) yield await Future.value(i); // 註冊至 event loop, 用 await 現出原型
}
void main() => intStream(1,10).forEach(print);//實際上是一個 Stream<int>物件
當然也能搭配 yield * 呼叫遞迴函式(recursive funtion)形成一個看似不用 loop 的資料流產生器,實際上也是註冊至 event loop, 待後續再執行:
intStream(int start, int end) async* {
if (start <= end) { // 結束遞迴的條件
yield await Future.value(start); // 註冊事件, 用 await 等待物件現出原型
yield * intStream(start+1, end); // 只註冊至 event loop 續用遞迴函式, 無需等待
}
}
void main() => intStream(1,10).forEach(print);//實際上是一個 Stream<int>物件
dart 程式庫的 Future 物件可類比成 Javascript 的 promise 物件, 而將 Future.value(值)類比成 promise 的 resolv(非同步傳回值), 他們運作邏輯是相同的, 同樣的,我們也可以用簡單邏輯來理解 async/await 的運作方式, async 其實只是把將來要回傳(return)的值一起打包成 Future/promise 物件, 而 await 負責註冊事件並把後續程式碼(位在相應的 async 函式碼)打包放入 then( ) 的 callback 函式內,後續當物件現出原形時, 呼叫這個 callback 方法完成後續動作, then 是 Future/promise 裏面一個註冊過的從屬函式(簡稱方法), 一旦條件滿足(物件現出原形)才會執行. 對於 sync * 產生器而言, 打包的是 iterable 物件, iterable 物件是一個在迴圈中可以一個一個取出使用的一串數列, 而 async * 所打包的是 Stream 類型的物件, Stream 則是一個可以一個一個取出來的非同步事件(a sequence of events), forEach 只是其中一個取出物件的方法, 透過註冊事件與物件方法間接或直接讀取物件的原型, 而不會 block 程式的運行,如此而已. Dart 的 async/await 語法跟 Javascript 語法長的很像:
asyncFunction( ) async => 3; // async 標註會將函式傳回值打包成 Future 類型的物件
void main( ) async => print(await asyncFunction( )); // await 只能在 async 用, 他讓物件現出原型
或是同步程式的寫法,用 Future 的 then( ) 方法現出物件原型後再處理:
void main( ) => asyncFunction( ).then(print);
同步程式也可以打包成 Future 物件, 後續沿用 then 方法取得物件原型:
syncFunction( ) => Future.value(3); // Future.value 是立刻賦值, promise resolve
void main( ) => syncFunction( ).then(print); // then 其實是一個 Future 物件裏面一個方法
沒有留言:
張貼留言