Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Flutter: Patapataが利用するDart Zoneの魔法!その非同期制御のしくみ

Flutter: Patapataが利用するDart Zoneの魔法!その非同期制御のしくみ

GREE Tech Conference 2024で発表された資料です。
https://techcon.gree.jp/2024/session/TrackA-6

gree_tech

October 25, 2024
Tweet

Video

More Decks by gree_tech

Other Decks in Technology

Transcript

  1. アプリにおけるトランザクション: 問題 Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理

    - バックグランドで動いている処理 このコードはどうなる。。。? 6 Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); } // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); } Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2);
  2. アプリにおけるトランザクション: 問題 Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理

    - バックグランドで動いている処理 このコードはどうなる。。。? 7 Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); } // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); } Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2);
  3. アプリにおけるトランザクション: 問題 8 0 before set 1 before set 2

    before set 1 after set 1 after notify 2 after set 2 after notify 0 after set 0 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); }
  4. アプリにおけるトランザクション: 問題 9 0 before set 1 before set 2

    before set 1 after set 1 after notify 2 after set 2 after notify 0 after set 0 after notify 2 before set 0 after set Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); }
  5. アプリにおけるトランザクション: 問題 10 Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 -

    非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); } await db.setInt('version', version); 0 before set 1 before set 2 before set 1 after set 1 after notify 2 after set 2 after notify 0 after set 0 after notify 1 after set 2 after set 0 after set 処理にかかる時間が一定ではない
  6. アプリにおけるトランザクション: 問題 11 Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 -

    非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); } await db.setInt('version', version); 0 before set 1 before set 2 before set 1 after set 1 after notify 2 after set 2 after notify 0 after set 0 after notify 1 after set 2 after set 0 after set トランザクションの問題を無視していませんよね!?
  7. アプリにおけるトランザクション: 順次実行 12 0 before set 0 after set 0

    after notify 1 before set 1 after set 1 after notify 2 before set 2 after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); }
  8. アプリにおけるトランザクション: 順次実行 13 • 最後に実行されたのはversion = 2 • DBにセットされたのはversion =

    2 0 before set 0 after set 0 after notify 1 before set 1 after set 1 after notify 2 before set 2 after set 2 after notify 2 before set 2 after set
  9. アプリにおけるトランザクション: キャンセル 14 0 before set 2 before set 2

    after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future<void> save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); }
  10. アプリにおけるトランザクション: save(0)の動作 15 0 before set 2 before set 2

    after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); print('$version before set'); // ここでsave(1)実行のためキャンセル await db.setInt('version', version); print('$version after set');
  11. アプリにおけるトランザクション: save(0)の動作 16 0 before set 2 before set 2

    after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); print('$version before set'); // ここでsave(1)実行のためキャンセル await db.setInt('version', version); print('$version after set'); print('$version before set'); // ここでsave(1)開始のためキャンセル 実行されない Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); print('$version before set'); // ここでsave(1)実行のためキャンセル await db.setInt('version', version); print('$version after set'); print('$version before set'); // ここでsave(1)実行のためキャンセル 実行されない before setのみ実行
  12. アプリにおけるトランザクション: save(1)の動作 17 0 before set 2 before set 2

    after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); // ここでsave(2)実行のためキャンセル print('$version before set'); await db.setInt('version', version); print('$version after set');
  13. アプリにおけるトランザクション: save(1)の動作 18 0 before set 2 before set 2

    after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); // ここでsave(2)実行のためキャンセル print('$version before set'); await db.setInt('version', version); print('$version after set'); // ここでsave(2)実行のためキャンセル 実行されない Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); // ここでsave(2)実行のためキャンセル print('$version before set'); await db.setInt('version', version); print('$version after set'); // ここでsave(2)実行のためキャンセル 実行されない 何も実行しない
  14. アプリにおけるトランザクション: save(2)の動作 19 0 before set 2 before set 2

    after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); print('$version before set'); await db.setInt('version', version); print('$version after set'); // 最後まで実行される
  15. アプリにおけるトランザクション: save(2)の動作 20 0 before set 2 before set 2

    after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); print('$version before set'); await db.setInt('version', version); print('$version after set'); // 最後まで実行される 全て実行する
  16. アプリにおけるトランザクション: キャンセル 21 0 before set 2 before set 2

    after set 2 after notify • 後続処理が実行された時、先行処理 をキャンセルする ◦ 最後に実行されたのはversion = 2 ◦ DBにセットされたのはversion = 2
  17. save(0) save(1) save(2) アプリにおけるトランザクション 22 save(0) save(1) save(2) キャンセル 順次実行

    開始 キャンセル 終了 Dartの「Zone」をカスタマイズして実行している!
  18. Zoneとは: 概要 • Dartの実行コンテキスト • カスタムされたZoneを作成できる ◦ 新しいZoneを作成 ◦ 既存のZoneを上書き

    • できること ◦ Zone内で使用できる変数を定義 ◦ 未処理エラーのカスタマイズ ◦ 非同期処理のカスタマイズ ◦ etc … 24
  19. runZoned( zoneValues: { #key: 'customZone', }, () => print( "#key

    = ${Zone.current[#key]}", ), ); Zoneとは: Zone内で使用できる変数を定義 • Zone内をスコープとしたグローバル 変数のようなもの 28 #key = customZone runZoned( zoneValues: { #key: 'customZone', }, () => print( "#key = ${Zone.current[#key]}", ), );
  20. Zoneとは: zoneValue 30 #key = ‘customZone’ Zone runZoned( zoneValues: {

    #key: 'customZone', }, () => print( "#key = ${Zone.current[#key]}", ), ); zoneValues: { #key: 'customZone', },
  21. Zoneとは: Zone内で使用できる変数を定義 31 取得 Zone #key = ‘customZone’ Zone内で行う処理 runZoned(

    zoneValues: { #key: 'customZone', }, () => print( "#key = ${Zone.current[#key]}", ), ); Zone.current[#key]
  22. Zone内で行う処理 Zoneとは: Zone内で使用できる変数を定義 32 Zone #key = ‘customZone’ runZoned( zoneValues:

    { #key: 'customZone', }, () => print( "#key = ${Zone.current[#key]}", ), ); () => print( "#key = ${Zone.current[#key]}", ), #key = customZone
  23. Zoneとは: Zone内で使用できる変数を定義 33 取得 Zone #key = ‘customZone’ Zone内で行う処理 #key

    = customZone runZoned( zoneValues: { #key: 'customZone', }, () => print( "#key = ${Zone.current[#key]}", ), ); runZoned( zoneValues: { #key: 'customZone', }, () => print( "#key = ${Zone.current[#key]}", ), );
  24. Zoneとは: 未処理エラーのカスタマイズ • 以前のFirebase Crashlyticsの設定 34 runZonedGuarded<Future<void>>( () async {

    // 省略 }, (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ), ); runZonedGuarded<Future<void>>( () async { // 省略 }, (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ), );
  25. Zoneとは: 未処理エラーのカスタマイズ 35 Zone runZonedGuarded<Future<void>>( () async { // 省略

    }, (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ), ); runZonedGuarded<Future<void>>( );
  26. Zone内で行う処理 Zoneとは: 未処理エラーのカスタマイズ 36 Zone runZonedGuarded<Future<void>>( () async { //

    省略 }, (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ), ); () async { // 省略 },
  27. エラー処理 Zoneとは: 未処理エラーのカスタマイズ 37 Zone Zone内で行う処理 runZonedGuarded<Future<void>>( () async {

    // 省略 }, (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ), ); (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ),
  28. Zoneとは: 未処理エラーのカスタマイズ 38 Zone Zone内で行う処理 エラー処理 runZonedGuarded<Future<void>>( () async {

    // 省略 }, (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ), ); runZonedGuarded<Future<void>>( () async { // 省略 }, (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ), );
  29. Zoneとは: 非同期処理のカスタマイズ • ZoneSpecification 39 zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p,

    z, f) { // 省略 }, createTimer: (s, p, z, d, f) { // 省略 }, createPeriodicTimer: (s, p, z, d, f) { // 省略 }, ), zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { // 省略 }, createTimer: (s, p, z, d, f) { // 省略 }, createPeriodicTimer: (s, p, z, d, f) { // 省略 }, ),
  30. Timer()で行う処理 Timer.periodic()処理 Zoneとは: 非同期処理のカスタマイズ 40 scheduleMicrotask()で行う処理 Zone zoneSpecification: ZoneSpecification( scheduleMicrotask:

    (s, p, z, f) { // 省略 }, createTimer: (s, p, z, d, f) { // 省略 }, createPeriodicTimer: (s, p, z, d, f) { // 省略 }, ), scheduleMicrotask: (s, p, z, f) { // 省略 }, createTimer: (s, p, z, d, f) { // 省略 }, createPeriodicTimer: (s, p, z, d, f) { // 省略 },
  31. zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { // 省略

    }, createTimer: (s, p, z, d, f) { // 省略 }, createPeriodicTimer: (s, p, z, d, f) { // 省略 }, ), zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { // 省略 }, createTimer: (s, p, z, d, f) { // 省略 }, createPeriodicTimer: (s, p, z, d, f) { // 省略 }, ), Zoneとは: 非同期処理のカスタマイズ 41 Zone scheduleMicrotask()で行う処理 Timer.periodic()処理 Timer()で行う処理 Q. カスタマイズできる非同期処理が 3種類のみ であるのはなぜか?
  32. zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { // 省略

    }, createTimer: (s, p, z, d, f) { // 省略 }, createPeriodicTimer: (s, p, z, d, f) { // 省略 }, ), zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { // 省略 }, createTimer: (s, p, z, d, f) { // 省略 }, createPeriodicTimer: (s, p, z, d, f) { // 省略 }, ), Zoneとは: 非同期処理のカスタマイズ 42 Zone scheduleMicrotask()で行う処理 Timer.periodic()処理 Timer()で行う処理 *Nativeからのイベントはコードで制御できませんが、結果的にscheduleMicrotaskとなります A. Dartの非同期処理は 全て* 3種類のどれかになるため Q. カスタマイズできる非同期処理が 3種類のみ であるのはなぜか?
  33. Dartの非同期処理: 分類例 48 コード Zoneの該当処理 追加されるキュー scheduleMicrotask() scheduleMicrotask Microtask Queue

    Future.microtask() scheduleMicrotask Future.then() scheduleMicrotask async / await scheduleMicrotask Timer() createTimer Event Queue Timer.run() createTimer Future() createTimer Timer.periodic() createPeriodicTimer
  34. コード Zoneの該当処理 追加されるキュー scheduleMicrotask() scheduleMicrotask Microtask Queue Future.microtask() scheduleMicrotask Future.then()

    scheduleMicrotask async / await scheduleMicrotask Timer() createTimer Event Queue Timer.run() createTimer Future() createTimer Timer.periodic() createPeriodicTimer Dartの非同期処理: 分類例 49
  35. Dartの非同期処理: Future.microtask() 50 factory Future.microtask( FutureOr<T> computation() ) { _Future<T>

    result = new _Future<T>(); scheduleMicrotask(() { // 省略 }); return result; }
  36. Dartの非同期処理: Future.microtask() scheduleMicrotaskを呼び出している → scheduleMicrotask 51 factory Future.microtask( FutureOr<T> computation()

    ) { _Future<T> result = new _Future<T>(); scheduleMicrotask(() { // 省略 }); return result; } scheduleMicrotask(() { });
  37. Dartの非同期処理: Future.then() 52 void _addListener(_FutureListener listener) { assert(listener._nextListener == null);

    if (_mayAddListener) { // 省略 } else { _zone.scheduleMicrotask(() { _propagateToListeners(this, listener); }); } } ※future_impl.dart 452行目 ~
  38. Dartの非同期処理: Future.then() 53 void _addListener(_FutureListener listener) { assert(listener._nextListener == null);

    if (_mayAddListener) { // 省略 } else { _zone.scheduleMicrotask(() { _propagateToListeners(this, listener); }); } } ※future_impl.dart 452行目 ~ _zone.scheduleMicrotask(() { _propagateToListeners(this, listener); }); scheduleMicrotaskを呼び出している → scheduleMicrotask
  39. Dartの非同期処理: Future() 55 factory Future( FutureOr<T> computation() ) { _Future<T>

    result = new _Future<T>(); Timer.run(() { // 省略 }); return result; }
  40. Dartの非同期処理: Future() Timer.run()を呼び出している → createTimer 56 factory Future( FutureOr<T> computation()

    ) { _Future<T> result = new _Future<T>(); Timer.run(() { // 省略 }); return result; } Timer.run(() { // 省略 });
  41. Dartの非同期処理: 分類例 57 コード Zoneの該当処理 追加されるキュー scheduleMicrotask() scheduleMicrotask Microtask Queue

    Future.microtask() scheduleMicrotask Future.then() scheduleMicrotask async / await scheduleMicrotask Timer() createTimer Event Queue Timer.run() createTimer Future() createTimer Timer.periodic() createPeriodicTimer
  42. Dartの非同期処理: Zoneによるカスタマイズ例 58 runZoned( () { Future.microtask(() { print('scheduleMicrotask called');

    }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), );
  43. Dartの非同期処理: Zoneによるカスタマイズ例 59 Zone Zone内で行う処理 runZoned( () { Future.microtask(() {

    print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, );
  44. Dartの非同期処理: Zoneによるカスタマイズ例 60 scheduleMicrotask called Zone Zone内で行う処理 runZoned( () {

    Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, );
  45. Dartの非同期処理: Zoneによるカスタマイズ例 61 カスタマイズされた scheduleMicrotask Zone Zone内で行う処理 runZoned( () {

    Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ),
  46. Dartの非同期処理: Zoneによるカスタマイズ例 62 scheduleMicrotask cancelled カスタマイズされた scheduleMicrotask Zone Zone内で行う処理 呼び出し

    runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), );
  47. runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification:

    ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); runZoned( () { () { print('scheduleMicrotask called'); } }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f { print('scheduleMicrotask cancelled'); }, ), ); Dartの非同期処理: Zoneによるカスタマイズ例 63 scheduleMicrotask cancelled カスタマイズされた scheduleMicrotask Zone Zone内で行う処理 呼び出し 引数 f として渡される
  48. Dartの非同期処理: Zoneによるカスタマイズ例 64 scheduleMicrotask cancelled カスタマイズされた scheduleMicrotask Zone Zone内で行う処理 呼び出し

    runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); f を呼び出していない
  49. Dartの非同期処理: Zoneによるカスタマイズ例 65 scheduleMicrotask cancelled カスタマイズされた scheduleMicrotask Zone Zone内で行う処理 呼び出し

    runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); () { print('scheduleMicrotask called'); } scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, 上書きされるイメージ scheduleMicrotask cancelled scheduleMicrotask called は出力されない
  50. Dartの非同期処理: Zoneによるカスタマイズ例 66 scheduleMicrotask cancelled カスタマイズされた scheduleMicrotask Zone Zone内で行う処理 呼び出し

    runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), ); runZoned( () { Future.microtask(() { print('scheduleMicrotask called'); }); }, zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { print('scheduleMicrotask cancelled'); }, ), );
  51. 非同期処理のカスタマイズ: SequentialWorkQueue • 主な機能 ◦ キューに処理を追加し順次実行できる ◦ 実行中の処理をキャンセルできる ◦ (キューをクリアできる

    ) 72 final _queue = SequentialWorkQueue(); // キューに処理を追加 _queue.add(() => save(0)); _queue.add( () async => await save(1), onCancel: () => true, // キャンセルできる ); // キューをクリア _queue.clear();
  52. 非同期処理のカスタマイズ: 順次実行 75 • add()を呼び出す ◦ キューに処理が追加される ◦ 追加した順番に実行される ◦

    ひとつの処理でエラーが発生しても 他の処理には影響しない _queue.add(() async { await save(0); }); _queue.add(() async { await save(1); }); _queue.add(() async { await save(2); });
  53. 非同期処理のカスタマイズ: キャンセル 78 • clear()を呼び出す ◦ 実行中の処理がキャンセルされる _queue.add(() async {

    await save(0); }); _queue.clear(); _queue.add(() async { await save(1); }); _queue.add(() async => await save(0)); _queue.add( () async => await save(1), // キャンセルできるようになる onCancel: () => true, ); _queue.clear(); _queue.add(() async => await save(2));
  54. 非同期処理のカスタマイズ: キャンセル 79 • clear()を呼び出す ◦ 実行中の処理がキャンセルされる _queue.add(() async {

    await save(0); }); _queue.clear(); _queue.add(() async { await save(1); }); _queue.add(() async => await save(0)); _queue.add( () async => await save(1), // キャンセルできるようになる onCancel: () => true, ); _queue.clear(); _queue.add(() async => await save(2)); runZoned( // 省略 zoneSpecification: ZoneSpecification( scheduleMicrotask: (s, p, z, f) { // 特定条件で f() を実行させないコード }, ), ); _queue.add(() async => await save(0)); _queue.add( () async => await save(1), // キャンセルできるようになる onCancel: () => true, ); _queue.clear(); _queue.add(() async => await save(2));
  55. 非同期処理のカスタマイズ: ProviderModel 81 class ExampleModel extends ProviderModel { Future<void> save(int

    version) async { await lock( (_) async { // 処理 }, overridable: true, override: true, ); } } class ExampleModel extends ProviderModel { Future<void> save(int version) async { await lock( (_) async { // 省略 }, overridable: true, override: true, ); } } // 省略の中身 print('$version before set'); await _db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify');
  56. 非同期処理のカスタマイズ: ProviderModel 82 class ExampleModel extends ProviderModel { Future<void> save(int

    version) async { await lock( (_) async { // 省略 }, overridable: true, override: true, ); } } class ExampleModel extends ProviderModel { }
  57. 非同期処理のカスタマイズ: ProviderModel 83 class ExampleModel extends ProviderModel { Future<void> save(int

    version) async { await lock( (_) async { // 省略 }, overridable: true, override: true, ); } } await lock( );
  58. 非同期処理のカスタマイズ: ProviderModel 84 class ExampleModel extends ProviderModel { Future<void> save(int

    version) async { await lock( (_) async { // 省略 }, overridable: true, override: true, ); } } (_) async { // 省略 }, // 省略の中身 print('$version before set'); await _db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify');
  59. 非同期処理のカスタマイズ: ProviderModel 85 class ExampleModel extends ProviderModel { Future<void> save(int

    version) async { await lock( (_) async { // 処理 }, overridable: true, override: true, ); } } overridable: true, override: true, // 省略の中身 print('$version before set'); await _db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify');
  60. 非同期処理のカスタマイズ: 実行例 86 0 before set 2 before set 2

    after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 _model.save(0); // 全く同じタイミングで実行 _model.save(1); // さらに全く同じタイミングで実行 _model.save(2);
  61. 最後に • ZoneはDartにおける実行コンテキスト • Dartのコードベースでの非同期処理は以下の3つに大別される ◦ scheduledMicrotask ◦ createTimer ◦

    createPeriodicTimer • Zoneによって非同期処理をカスタマイズしてできることの一例 ◦ データを安全に扱うことができるSequentialWorkQueueと ProviderModel 88