対象者
- Flutter/Dart 実務経験者
- Microtaskを知りたい人
- 非同期処理やイベントループの詳しい挙動を学びたいエンジニア
はじめに
Flutterアプリ開発では、Future
を使った非同期処理が一般的ですが、同じdart:async
に属する「Microtask」は扱い方が少し異なります。実務では「軽量な後処理を即座に行いたい」といった場面でMicrotaskを活用します。本記事では、Microtaskと通常のFutureの違いを深掘りし、実務で役立つユースケース、動作確認のテストコードまでお届けします。
Microtaskとは何か
Dartのイベントループには「Microtaskキュー」と「Eventキュー」があり、MicrotaskはEventよりも高い優先度で処理されます。scheduleMicrotask
やFuture.microtask
で登録された処理は、現在のイベントループ中に他のMicrotaskが残っていないかぎりすぐに実行されます。
Futureとの違い
イベントループ内での実行順序
- Microtaskキュー:
scheduleMicrotask
/Future.microtask
で登録 - Eventキュー:
Future(() => …)
(Timer.run
相当)で登録
- 「同期処理」がすべて終わる
- Microtaskキュー内のタスクをFIFOで実行
- Eventキュー内のタスクを一つだけ実行し、次のイベントループへ
スケジューリングの仕組み
Future.microtask(computation)
→ 内部でscheduleMicrotask
を呼び出し、MicrotaskキューへFuture(computation)
→ 内部でTimer.run
を呼び出し、EventキューへscheduleMicrotask(callback)
→ 直接Microtaskキューへ追加
Microtaskのパフォーマンス特性
- 即時性:現在のイベントループ内で最優先実行
- 軽量処理向き:短い計算やState更新などに適合
- 注意点:Microtaskキューが空にならないとEventキューの処理(UI描画など)が遅延し、アプリの応答性低下を招くリスクあり
実務での典型的ユースケース
initState内での初期化遅延
@override
void initState() {
super.initState();
Future.microtask(() {
context.read<MyNotifier>().fetchData();
});
}
initState
内で直接context.read<MyNotifier>().fetchData()
を呼び出すと、まだウィジェットツリーのビルド前に状態更新が発生し、context
が不安定なタイミングで参照されるためエラーや不整合を起こす可能性があります。
そこでFuture.microtask
を使うことで、同一イベントループ内の次のマイクロタスクサイクルに処理を遅延させ、ビルド完了後(=安定したcontext
が利用可能なタイミング)に安全にデータフェッチを実行できます。
このパターンにより、
- ビルド中の
context
参照エラーを回避 - 同一フレーム内で即時かつ軽量に非同期処理を行う
- UI描画前後のタイミングを厳密に制御
といったメリットを得られます。
テストにおけるタイミング制御
以下のテストコード例を使い、MicrotaskとFutureの順序を厳密に検証できます。
Flutter Testで挙動を確認するコード例
以下のテストコード例を使い、MicrotaskとFutureの順序を厳密に検証できます。
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('micro taskの順番確認', () async {
final result = [];
result.add('start');
scheduleMicrotask(() => result.add('micro task1'));
final future = Future(() => result.add('future'));
scheduleMicrotask(() => result.add('micro task2'));
result.add('end');
// microtask キューを空にする
await Future.microtask(() {});
expect(result.length, 4);
expect(result, ['start', 'end', 'micro task1', 'micro task2']);
// 次のイベントループを待って Future を実行
await Future<void>.delayed(Duration.zero);
expect(result.length, 5);
expect(result, ['start', 'end', 'micro task1', 'micro task2', 'future']);
});
}
注意点とベストプラクティス
- Microtaskの使いすぎ注意:無限に登録するとUI更新を阻害
- 粒度の調整:重い処理は
compute
やIsolate
、あるいはFuture.delayed
に分散 - 明示的なキュー制御:テストでは
await Future.microtask(...)
とawait Future.delayed(Duration.zero)
を併用
参考
- Why use a Microtask in Flutter?
- 10 Lesser-Known Dart and Flutter Functionalities You Should Start Using
- Dart API: Future.microtask コンストラクタ
- Dart API: scheduleMicrotask 関数
- Microtasks and Event loop in Dart | by Purva Dalvi – Medium
- What is the difference in calling Future and Future.microtask in Flutter?
Q&A
Q1. MicrotaskとFutureの違いは何ですか?
A: Microtaskは現在のイベントループ内で即実行され、Future(Timer.run
相当)は次のイベントループで実行されます。
Q2. Microtaskを多用すると何が問題になりますか?
A: Microtaskキューが空にならないと他のイベント(UI描画やタイマーなど)が実行されず、アプリが固まるリスクがあります。
Q3. 実務でMicrotaskをどんな場面で使うべきですか?
A: 次のフレーム描画前に完了させたい軽量処理に実施します
まとめ
本記事では、Dart/FlutterにおけるMicrotaskの仕組みとFutureとの違いを解説しました。実務では「今すぐ軽量に非同期処理したい」場合にMicrotaskが力を発揮しますが、使いすぎには注意が必要です。適切な粒度でMicrotaskを活用し、UIの応答性とテストの正確性を両立させましょう。
-
Next
記事がありません