Slide 1

Slide 1 text

Flutter: Patapataが利用する Dart Zoneの魔法! その非同期制御のしくみ グリー株式会社 クライアントエンジニア 薩間 俊彰

Slide 2

Slide 2 text

薩間 俊彰 (さつま としあき) 建設業向けのDX事業を手掛ける企業を経て、 2022年11月グリー株式会社に中途入社。 現在はFlutterエンジニアとして、マンガアプリ 「DADAN」のアプリ開発などを担当。 グリー株式会社 クライアントエンジニア 2

Slide 3

Slide 3 text

目的 ● DartにおけるZoneや非同期処理に対する理解を深めること 3 対象の方 ● Flutterでのアプリ開発経験がある方 ● Dartの非同期処理の基本的な使い方を理解している方 ● アプリの安定性向上やデバッグの効率化に興味がある方

Slide 4

Slide 4 text

内容 ● アプリにおけるトランザクション ● DartのZone ● Dartの非同期処理 ● Zoneを使った非同期処理のカスタマイズ 4

Slide 5

Slide 5 text

アプリにおけるトランザクション: 導入 5

Slide 6

Slide 6 text

アプリにおけるトランザクション: 問題 Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? 6 Future 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 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);

Slide 7

Slide 7 text

アプリにおけるトランザクション: 問題 Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? 7 Future 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 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);

Slide 8

Slide 8 text

アプリにおけるトランザクション: 問題 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 save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); }

Slide 9

Slide 9 text

アプリにおけるトランザクション: 問題 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 save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); }

Slide 10

Slide 10 text

アプリにおけるトランザクション: 問題 10 Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future 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 処理にかかる時間が一定ではない

Slide 11

Slide 11 text

アプリにおけるトランザクション: 問題 11 Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 save(0); // 全く同じタイミングで実行 save(1); // さらに全く同じタイミングで実行 save(2); Future 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 トランザクションの問題を無視していませんよね!?

Slide 12

Slide 12 text

アプリにおけるトランザクション: 順次実行 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 save(int version) async { print('$version before set'); await db.setInt('version', version); print('$version after set'); _notifyEveryoneAboutNewVersion(); print('$version after notify'); }

Slide 13

Slide 13 text

アプリにおけるトランザクション: 順次実行 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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

アプリにおけるトランザクション: 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');

Slide 16

Slide 16 text

アプリにおけるトランザクション: 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のみ実行

Slide 17

Slide 17 text

アプリにおけるトランザクション: 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');

Slide 18

Slide 18 text

アプリにおけるトランザクション: 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)実行のためキャンセル 実行されない 何も実行しない

Slide 19

Slide 19 text

アプリにおけるトランザクション: 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'); // 最後まで実行される

Slide 20

Slide 20 text

アプリにおけるトランザクション: 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'); // 最後まで実行される 全て実行する

Slide 21

Slide 21 text

アプリにおけるトランザクション: キャンセル 21 0 before set 2 before set 2 after set 2 after notify ● 後続処理が実行された時、先行処理 をキャンセルする ○ 最後に実行されたのはversion = 2 ○ DBにセットされたのはversion = 2

Slide 22

Slide 22 text

save(0) save(1) save(2) アプリにおけるトランザクション 22 save(0) save(1) save(2) キャンセル 順次実行 開始 キャンセル 終了 Dartの「Zone」をカスタマイズして実行している!

Slide 23

Slide 23 text

Zoneとは 23

Slide 24

Slide 24 text

Zoneとは: 概要 ● Dartの実行コンテキスト ● カスタムされたZoneを作成できる ○ 新しいZoneを作成 ○ 既存のZoneを上書き ● できること ○ Zone内で使用できる変数を定義 ○ 未処理エラーのカスタマイズ ○ 非同期処理のカスタマイズ ○ etc … 24

Slide 25

Slide 25 text

Zoneとは: できることの一例 ● カスタムされたZoneを作成 ● Zone内で使用できる変数を定義 ● 未処理エラーのカスタマイズ ● 非同期処理のカスタマイズ 25

Slide 26

Slide 26 text

Zoneとは: カスタムされたZoneを作成する ● 方法は以下の2つ ○ runZoned ○ runZonedGuarded 26 runZoned( () {}, ); runZonedGuarded( () {}, (error, stackTrace) {}, );

Slide 27

Slide 27 text

Zoneとは: カスタムされたZoneを作成する ● 方法は以下の2つ ○ runZoned ○ runZonedGuarded ● 違いはエラー処理の有無 27 runZoned( () {}, ); runZonedGuarded( () {}, (error, stackTrace) {}, );

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Zoneとは: zoneValue 30 #key = ‘customZone’ Zone runZoned( zoneValues: { #key: 'customZone', }, () => print( "#key = ${Zone.current[#key]}", ), ); zoneValues: { #key: 'customZone', },

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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]}", ), );

Slide 34

Slide 34 text

Zoneとは: 未処理エラーのカスタマイズ ● 以前のFirebase Crashlyticsの設定 34 runZonedGuarded>( () async { // 省略 }, (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ), ); runZonedGuarded>( () async { // 省略 }, (error, stack) => FirebaseCrashlytics.instance.recordError( error, stack, fatal: true, ), );

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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) { // 省略 }, ),

Slide 40

Slide 40 text

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) { // 省略 },

Slide 41

Slide 41 text

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種類のみ であるのはなぜか?

Slide 42

Slide 42 text

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種類のみ であるのはなぜか?

Slide 43

Slide 43 text

Dartの非同期処理 43

Slide 44

Slide 44 text

Dartの非同期処理: 実現方法 ● シングルスレッド・イベントループ ● 2つのキューを持つ ○ Microtask Queue ○ Event Queue 44 Microtask Queue Event Queue

Slide 45

Slide 45 text

Dartの非同期処理: Microtask queue ● 内部からのタスクが追加される ○ Dartのコードで生じたタスク 45 Task4 Task3 Task2 Task1 Microtask queue

Slide 46

Slide 46 text

Dartの非同期処理: Event queue ● 外部からのイベントが追加される ○ タップイベント ○ 描画イベント ○ I/Oイベント ○ タイマーイベント 46 Timer Tap Repaint Tap Event queue

Slide 47

Slide 47 text

Event Loop Dartの非同期処理: キューの優先度 47 Microtaskを 実行 Eventを 実行 Event Queue .isEmpty Microtask Queue .isEmpty Yes Yes No No

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

コード 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

Slide 50

Slide 50 text

Dartの非同期処理: Future.microtask() 50 factory Future.microtask( FutureOr computation() ) { _Future result = new _Future(); scheduleMicrotask(() { // 省略 }); return result; }

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Dartの非同期処理: Future() 55 factory Future( FutureOr computation() ) { _Future result = new _Future(); Timer.run(() { // 省略 }); return result; }

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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'); }, ), );

Slide 58

Slide 58 text

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'); }); }, );

Slide 59

Slide 59 text

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'); }); }, );

Slide 60

Slide 60 text

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'); }, ),

Slide 61

Slide 61 text

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'); }, ), );

Slide 62

Slide 62 text

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 として渡される

Slide 63

Slide 63 text

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 を呼び出していない

Slide 64

Slide 64 text

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 は出力されない

Slide 65

Slide 65 text

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'); }, ), );

Slide 66

Slide 66 text

非同期処理のカスタマイズ 67

Slide 67

Slide 67 text

非同期処理のカスタマイズ: Patapataとは ● 商用アプリ開発を楽にするためのフレームワーク ● 様々な機能をアプリに簡単に実装できる (一部抜粋) ○ アナリティクス、ログ、エラー検知などのコアにあたる機能 ○ 画面サイズによってUIサイズを自動的に調整する機能 ○ データを安全に操作できる機能 ○ etc … ● OSSとして公開 68

Slide 68

Slide 68 text

非同期処理のカスタマイズ: Patapataとは ● 商用アプリ開発を楽にするためのフレームワーク ● 様々な機能をアプリに簡単に実装できる (一部抜粋) ○ アナリティクス、ログ、エラー検知などのコアにあたる機能 ○ 画面サイズによってUIサイズを自動的に調整する機能 ○ データを安全に操作できる機能 ○ etc … ● OSSとして公開 69

Slide 69

Slide 69 text

非同期処理のカスタマイズ: データを安全に操作できる ● SequentialWorkQueue ○ 非同期処理をキューイングしてサーバサイドの トランザクションのように実行できる機能 ● ProviderModel ○ SequentialWorkQueueの機能をデータモデル に簡単に組み込むことができる機能 70

Slide 70

Slide 70 text

非同期処理のカスタマイズ: SequentialWorkQueue ● SequentialWorkQueue ○ 非同期処理をキューイングしてサーバサイドの トランザクションのように実行できる機能 ● ProviderModel ○ SequentialWorkQueueの機能をデータモデル に簡単に組み込むことができる機能 71

Slide 71

Slide 71 text

非同期処理のカスタマイズ: SequentialWorkQueue ● 主な機能 ○ キューに処理を追加し順次実行できる ○ 実行中の処理をキャンセルできる ○ (キューをクリアできる ) 72 final _queue = SequentialWorkQueue(); // キューに処理を追加 _queue.add(() => save(0)); _queue.add( () async => await save(1), onCancel: () => true, // キャンセルできる ); // キューをクリア _queue.clear();

Slide 72

Slide 72 text

非同期処理のカスタマイズ: 順次実行 73 ● 主な機能 ○ キューに処理を追加し順次実行できる ○ 実行中の処理をキャンセルできる ○ (キューをクリアできる )

Slide 73

Slide 73 text

save(0) save(1) save(2) 非同期処理のカスタマイズ: 順次実行 74 save(0) save(1) save(2)

Slide 74

Slide 74 text

非同期処理のカスタマイズ: 順次実行 75 ● add()を呼び出す ○ キューに処理が追加される ○ 追加した順番に実行される ○ ひとつの処理でエラーが発生しても 他の処理には影響しない _queue.add(() async { await save(0); }); _queue.add(() async { await save(1); }); _queue.add(() async { await save(2); });

Slide 75

Slide 75 text

非同期処理のカスタマイズ: キャンセル 76 ● 主な機能 ○ キューに処理を追加し順次実行できる ○ 実行中の処理をキャンセルできる ○ (キューをクリアできる )

Slide 76

Slide 76 text

save(0) save(1) save(2) 非同期処理のカスタマイズ: キャンセル 77 save(0) save(1) save(2)

Slide 77

Slide 77 text

非同期処理のカスタマイズ: キャンセル 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));

Slide 78

Slide 78 text

非同期処理のカスタマイズ: キャンセル 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));

Slide 79

Slide 79 text

非同期処理のカスタマイズ: ProviderModel ● SequentialWorkQueue ○ 非同期処理をキューイングしてサーバサイドの トランザクションのように実行できる機能 ● ProviderModel ○ SequentialWorkQueueの機能をデータモデル に簡単に組み込むことができる機能 80

Slide 80

Slide 80 text

非同期処理のカスタマイズ: ProviderModel 81 class ExampleModel extends ProviderModel { Future save(int version) async { await lock( (_) async { // 処理 }, overridable: true, override: true, ); } } class ExampleModel extends ProviderModel { Future 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');

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

非同期処理のカスタマイズ: ProviderModel 84 class ExampleModel extends ProviderModel { Future 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');

Slide 84

Slide 84 text

非同期処理のカスタマイズ: ProviderModel 85 class ExampleModel extends ProviderModel { Future 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');

Slide 85

Slide 85 text

非同期処理のカスタマイズ: 実行例 86 0 before set 2 before set 2 after set 2 after notify Flutterで本格的なアプリを作ると、非同期処理 がいろいろなところで並行して呼ばれるケース がたくさん 例えば... - ユーザーからの操作 - 非同期で動いている処理 - バックグランドで動いている処理 このコードはどうなる。。。? // ユーザー操作で処理を実行 _model.save(0); // 全く同じタイミングで実行 _model.save(1); // さらに全く同じタイミングで実行 _model.save(2);

Slide 86

Slide 86 text

最後に 87

Slide 87

Slide 87 text

最後に ● ZoneはDartにおける実行コンテキスト ● Dartのコードベースでの非同期処理は以下の3つに大別される ○ scheduledMicrotask ○ createTimer ○ createPeriodicTimer ● Zoneによって非同期処理をカスタマイズしてできることの一例 ○ データを安全に扱うことができるSequentialWorkQueueと ProviderModel 88

Slide 88

Slide 88 text

ご清聴ありがとうございました 89 Patapataに興味をお持ちいただいた方はこちらから! Github pub.dev

Slide 89

Slide 89 text

No content