<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Flutter異步編程-sync*和async*生成器函數(shù)

          共 10351字,需瀏覽 21分鐘

           ·

          2021-04-01 18:19

          生成器函數(shù)可能比較陌生,在平時(shí)開發(fā)中使用的不是特別頻繁,但是因?yàn)樗彩荄art異步編程的不可缺少的一部分,所以這里還是展開講解分析。「生成器函數(shù)是一種用于延遲生成值序列的函數(shù)」,并且在Dart中生成器函數(shù)主要分為兩種: 「同步生成器函數(shù)和異步生成器函數(shù)」。我們知道比如 int 類型變量(同步)或 Future<int> (異步)類型變量都只能產(chǎn)生單一的值,而 Iterable<int> 類型變量(同步)或 Stream<int> 類型變量(異步)具有產(chǎn)生多個(gè)值的能力。「其中同步生成器函數(shù)是立即按需生成值,并不會(huì)像Future,Stream那樣等待,而異步生成器函數(shù)則是異步生成值,也就是它有足夠時(shí)間去生成值」


          Single value(單一的值)Zero or more value(零個(gè)值或者更多的值)
          Sync(同步)intIterable
          Async(異步)FutureStream

          1. 為什么需要生成器函數(shù)

          其實(shí)在平時(shí)Flutter或者Dart開發(fā)中生成器函數(shù)使用并不多,但是遇到使用它的場(chǎng)景,有了生成器函數(shù)就會(huì)非常簡(jiǎn)單,因?yàn)槭謩?dòng)去實(shí)現(xiàn)值的生成函數(shù)還是比較繁雜的。比如說要實(shí)現(xiàn)一個(gè)同步生成器,需要自定義一個(gè)可迭代的類去繼承 IterableBase 并且需要重寫 iterator 方法用于返回一個(gè)新的Iterator對(duì)象。為此還得需要聲明自己的迭代器類。此外還得實(shí)現(xiàn)成員方法 moveNext 和成員屬性 current 以此來判斷是否迭代到末尾。這還是寫一個(gè)同步生成器步驟,整個(gè)過程還是比較繁雜的。所以Dart給你提供一個(gè)方法可以直接生成一個(gè)同步生成器函數(shù),簡(jiǎn)化整個(gè)實(shí)現(xiàn)的步驟。但是如果要去手動(dòng)實(shí)現(xiàn)一個(gè)異步生成器遠(yuǎn)比同步生成器更復(fù)雜,所以直接提供一個(gè)簡(jiǎn)單異步生成器函數(shù)會(huì)使得整個(gè)開發(fā)更加高效,可以把精力更加專注于業(yè)務(wù)。這里就以同步生成器函數(shù)舉例:

          說到同步生成器函數(shù)先來回顧下 Iterable 和 Iterator

          //Iterator可以將一系列的值依次迭代
          abstract class Iterator<E{
            bool moveNext();//表示是否迭代器有下一個(gè)值,迭代器把下一個(gè)值加載為當(dāng)前值,直到下一個(gè)值為空返回false

            E get current; //返回當(dāng)前迭代的值,也就是最近迭代一次的值
          }

          按照上面介紹步驟一起來手動(dòng)實(shí)現(xiàn)一個(gè) Iterable ,實(shí)際上也很簡(jiǎn)單只是簡(jiǎn)單地包了個(gè)殼

          class StringIterable extends Iterable<String{
            final List<String> stringList; //實(shí)際上List就是一個(gè)Iterable

            StringIterable(this.stringList);

            @override
            Iterator<Stringget iterator =>
                stringList.iterator; //通過將List的iterator,賦值給iterator

          }

          //這樣StringIterable就是一個(gè)特定類型String的迭代器,我們就可以使用for-in循環(huán)進(jìn)行迭代
          void main() {
            var stringIterable = StringIterable([
              "Dart",
              "Java",
              "Kotlin",
              "Swift"
            ]);
            for (var value in stringIterable) {
              print('$value');
            }
          }

          //甚至你還可以將StringIterable結(jié)合map、where、reduce之類操作符函數(shù)之類對(duì)迭代器值進(jìn)行變換
          void main() {
            var stringIterable = StringIterable([
              "Dart",
              "Java",
              "Kotlin",
              "Swift"
            ]);
            stringIterable
                .map((language) => language.length)//可以結(jié)合map一起使用,實(shí)際上本質(zhì)就是Iterable對(duì)象轉(zhuǎn)換,將StringIterable轉(zhuǎn)換成MappedIterable
                .forEach(print);
          }

          輸出結(jié)果:可以看到上面其實(shí)還不是一個(gè)真正嚴(yán)格意義手動(dòng)實(shí)現(xiàn)一個(gè) Iterable , 那么問題來了如何手動(dòng)實(shí)現(xiàn)一個(gè)同步生成器函數(shù),注意:「同步生成器函數(shù)必須返回一個(gè) Iterable 類型,然后需要使用 sync* 修飾該函數(shù),以此來標(biāo)記該函數(shù)是一個(gè)同步生成器函數(shù)。」

          void main() {
            final numbers = getRange(110);
            for (var value in numbers) {
              print('$value');
            }
          }

          //用于生成一個(gè)同步序列
          Iterable<int> getRange(int start, int end) sync* { //sync*告訴Dart這個(gè)函數(shù)是一個(gè)按需生產(chǎn)值的同步生成器函數(shù)
            for (int i = start; i <= end; i++) {
              yield i;//yield關(guān)鍵字有點(diǎn)像return,但是它是單次返回值,并不會(huì)像return直接結(jié)束整個(gè)函數(shù)
            }
          }

          輸出結(jié)果:通過對(duì)比發(fā)現(xiàn)了生成器函數(shù)是真的簡(jiǎn)單方便,只需要通過 sync* 和 yield 關(guān)鍵字就能實(shí)現(xiàn)一個(gè)任意類型迭代器,比手動(dòng)實(shí)現(xiàn)一個(gè)同步生成器函數(shù)更加簡(jiǎn)單,所以應(yīng)該知道為什么需要生成器函數(shù)。其實(shí)異步生成器函數(shù)也是類似。

          2. 什么是生成器(Generator)

          生成器函數(shù)是「一種可以很方便延遲生成值的序列的函數(shù)」,生成器主要分為兩種:**同步生成器函數(shù)和異步生成器函數(shù)。其中同步生成函數(shù)需要使用 sync* 關(guān)鍵字修飾,返回一個(gè) Iterable 對(duì)象(表示可以順序訪問值的集合);異步生成器函數(shù)需要使用 async* 關(guān)鍵字修飾,返回的是一個(gè) Stream 對(duì)象(表示異步數(shù)據(jù)事件)。**此外同步生成器函數(shù)是立即按需生成值,并不會(huì)像Future,Stream那樣等待,而異步生成器函數(shù)則是異步生成值,也就是它有足夠時(shí)間去生成值。

          2.1 同步生成器(「Synchronous」 Generator)

          同步生成器函數(shù)需要配合 sync* 關(guān)鍵字和 yield 關(guān)鍵字,最終返回的是一個(gè) Iterable 對(duì)象,其中yield 關(guān)鍵字用于每次返回單次的值,并且需要注意它的返回并不是結(jié)束整個(gè)函數(shù)。

          import 'dart:io';

          void main() {
            final numbers = countValue(10);
            for (var value in numbers) {
              print('$value');
            }
          }

          Iterable<int> countValue(int max) sync* {//sync*告訴Dart這個(gè)函數(shù)是一個(gè)按需生產(chǎn)值的同步生成器函數(shù)
            for (int i = 0; i < max; i++) {
              yield i; //yield關(guān)鍵字每次返回單次的值
              sleep(Duration(seconds: 1));
            }
          }

          輸出結(jié)果:

          2.2 異步生成器(「Asynchronous」 Generator)

          異步生成器函數(shù)需要配合 async* 關(guān)鍵字和 yield 關(guān)鍵字,最終返回的是一個(gè) Stream 對(duì)象,需要注意的是「生成Stream也是一個(gè)單一訂閱模型的Stream,」 也就是說不能同時(shí)存在多個(gè)訂閱者監(jiān)聽否則會(huì)出現(xiàn)異常,如果要實(shí)現(xiàn)支持多個(gè)監(jiān)聽者通過 asBroadcastStream 轉(zhuǎn)換成一個(gè)廣播訂閱模型的Stream。

          import 'dart:io';

          void main() {
            Stream<int> stream = countStream(10);
            stream.listen((event) {
              print('event value: $event');
            }).onDone(() {
              print('is done');
            });
          }

          Stream<int> countStream(int max) async* {
            //async*告訴Dart這個(gè)函數(shù)是生成異步事件流的異步生成器函數(shù)
            for (int i = 0; i < max; i++) {
              yield i;
              sleep(Duration(seconds: 1));
            }
          }

          輸出結(jié)果:

          2.3 yield關(guān)鍵字

          「yield關(guān)鍵字」在生成器函數(shù)中是用于依序生成每一個(gè)值,它有點(diǎn)類似return語句,但是和return語句不一樣的是執(zhí)行一次yield并不會(huì)結(jié)束整個(gè)函數(shù)。相反它每次只提供單個(gè)值,并掛起(注意不是阻塞)等待調(diào)用者請(qǐng)求下一個(gè)值,然后它就會(huì)再執(zhí)行一遍,比如上述例子for循環(huán)中,「每一次循環(huán)執(zhí)行都會(huì)觸發(fā)yield執(zhí)行一次返回每次的單值并且進(jìn)入下一次循環(huán)的等待」

          Iterable<int> countValue(int max) sync* {//sync*告訴Dart這個(gè)函數(shù)是一個(gè)按需生產(chǎn)值的同步生成器函數(shù)
            for (int i = 0; i < max; i++) {
              yield i; //每執(zhí)行一次循環(huán)就會(huì)觸發(fā)當(dāng)前次yield生成值,然后進(jìn)入下一次的等待
              sleep(Duration(seconds: 1));
            }
          }

          2.4 yield* 關(guān)鍵字

          yield關(guān)鍵字是用于循環(huán)中生產(chǎn)值后跟著一個(gè)具體對(duì)象或者值,但是yield后面則是跟著一個(gè)函數(shù),一般會(huì)跟著遞歸函數(shù),通過它就能實(shí)現(xiàn)類似二次遞歸函數(shù)功能。雖然yield關(guān)鍵字也能實(shí)現(xiàn)一個(gè)二次遞歸函數(shù)但是比較復(fù)雜,但是如果使用yield關(guān)鍵字就會(huì)更加簡(jiǎn)單。

          //這是使用yield實(shí)現(xiàn)二次遞歸函數(shù)
          Iterable naturals(n) sync* { 
            if (n > 0) { 
               yield n; 
               for (int i in naturals(n-1)) { 
                 yield i; 
               } 
            }


          //這是使用yield*實(shí)現(xiàn)的二次遞歸函數(shù)
          Iterable naturals(n) sync* { 
            if ( n > 0) { 
              yield n; 
              yield* naturals(n-1); 
           } 

          2.5 await for

          關(guān)于await for在Stream那一篇文章中已經(jīng)有說到,對(duì)于一個(gè)同步的迭代器Iterable而言我們可以使用for-in循環(huán)來遍歷每個(gè)元素,而對(duì)于一個(gè)異步的Stream可以很好地使用await for來遍歷每個(gè)事件元素。

          void main() async {
            Stream<int> stream = Stream<int>.periodic(Duration(seconds: 1), (int value) {
              return value + 1;
            });
            await stream.forEach((element) => print('stream value is: $element'));
          }

          3. 如何使用生成器函數(shù)(Generator)

          3.1 sync* + yield實(shí)現(xiàn)同步生成器

          void main() {
            final Iterable<int> sequence = countDownBySync(10);
            print('start');
            sequence.forEach((element) => print(element));
            print('end');
          }

          Iterable<int> countDownBySync(int numsync* {//sync*
            while (num > 0) {
              yield num--;//yield返回值
            }
          }

          輸出結(jié)果:

          3.2  async* + yield實(shí)現(xiàn)異步生成器

          void main() {
            final Stream<int> sequence = countDownByAsync(10);
            print('start');
            sequence.listen((event) => print(event)).onDone(() => print('is done'));
            print('end');
          }

          Stream<int> countDownByAsync(int numasync* {//async*表示異步返回一個(gè)Stream
            while (num > 0) {
              yield num--;
            }
          }

          輸出結(jié)果:

          3.3 sync* + yield*實(shí)現(xiàn)遞歸同步生成器

          void main() {
            final Iterable<int> sequence = countDownBySyncRecursive(10);
            print('start');
            sequence.forEach((element) => print(element));
            print('end');
          }

          Iterable<int> countDownBySyncRecursive(int numsync* {
            if (num > 0) {
              yield num;
              yield* countDownBySyncRecursive(num - 1);//yield*后跟一個(gè)遞歸函數(shù)
            }
          }

          輸出結(jié)果:

          3.4 async* + yield*實(shí)現(xiàn)遞歸異步生成器

          void main() {
            final Stream<int> sequence = countDownByAsync(10);
            print('start');
            sequence.listen((event) => print(event)).onDone(() => print('is done'));
            print('end');
          }

          Stream<int> countDownByAsyncRecursive(int numasync* {
            if (num > 0) {
              yield num;
              yield* countDownByAsyncRecursive(num - 1);//yield*后跟一個(gè)遞歸函數(shù)
            }
          }

          輸出結(jié)果:

          4. 生成器函數(shù)(Generator)使用場(chǎng)景

          關(guān)于生成器函數(shù)使用在開發(fā)過程其實(shí)并不多,但是也不是說關(guān)于生成器函數(shù)使用場(chǎng)景就沒有了,否則這篇文章就沒啥意義了。實(shí)際上如果有小伙伴接觸Flutter開發(fā)中其中有一個(gè)「Bloc的狀態(tài)管理框架」,可以發(fā)現(xiàn)它里面大量地使用了異步生成器函數(shù),一起來看下:

          class CountBloc extends Bloc<CounterEventint{
            @override
            int get initialState => 0;

            @override
            Stream<int> mapEventToState(CounterEvent event) async* {
              switch (event) {
                case CounterEvent.decrement:
                  yield state - 1;
                  break;
                case CounterEvent.increment:
                  yield state + 1;
                  break;
              }
            }
          }

          其實(shí)除了上面所說Bloc狀態(tài)管理框架中使用到的場(chǎng)景,可能還有一種場(chǎng)景那就是異步二次函數(shù)遞歸場(chǎng)景,比如實(shí)現(xiàn)某個(gè)動(dòng)畫軌跡計(jì)算,實(shí)際上都是通過一些二次函數(shù)計(jì)算模擬出來,所以這時(shí)候生成器遞歸函數(shù)是不是就可以派上用場(chǎng)了。雖然生成器函數(shù)使用場(chǎng)景不是很頻繁,但是需要做到某個(gè)特定場(chǎng)景第一時(shí)間想到用它可以更加簡(jiǎn)單的實(shí)現(xiàn)就可以了。

          5. 總結(jié)

          到這里有關(guān)Flutter異步編程系列就結(jié)束了,下一個(gè)系列將進(jìn)入Dart注解+APT生成代碼技術(shù)專題。盡管生成器函數(shù)使用在開發(fā)過程其實(shí)并不多,但是它也作為Flutter異步中一部分,有一些特定場(chǎng)景如果能想到用它來解決,一定會(huì)事半功倍的。

          感謝關(guān)注,熊喵先生愿和你在技術(shù)路上一起成長(zhǎng)!

          瀏覽 59
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色婷婷综合网 | 天堂网av免费 | 九九精品国产 | 亚洲三级视频网站 | 天堂AV资源 |