デバウンスを利用して、同じ非同期処理が過剰に走ることを防ぐ
株式会社Sally新人エンジニアの @wellPicker です。
日々の業務の中で学んだことを書き残すことで読者の皆様(と将来の自分)の役に立てればと思います。
はじめに
アプリケーション開発において、ユーザーからの入力やイベントを適切に処理することは重要です。
その中でも、頻繁に発生するイベントを効率的に扱うためのテクニックとしてデバウンスという考え方があります。
そこで、本記事ではデバウンスの基本的な説明と、Flutterでの具体的な実装方法について解説します。
そもそもデバウンスとは何か
デバウンスとは、特定のイベントが短期間に連続して発生した際に、タイマーによる入力遅延を利用して一度だけ処理が実行されるようにする手法です。
これにより過剰な処理を避け、パフォーマンスを向上させることができます。
デバウンスを活用する具体例
デバウンスを活用することが多い具体例の一つは検索欄です。
検索欄に1文字入力するたびにサーバーにリクエストを送るのではなく、文字の入力が一定時間途切れてからリクエストを送るようにすることで、サーバーとのやり取りを最小限にすることが可能です。
その他にも、特にデータベースとのやり取りなど、時間がかかる非同期処理を短期間に複数回実行させたくない時などに、デバウンスという考え方を活用することが可能です。
デバウンスの基本的な考え方
デバウンスの基本的な考え方は以下の通りです。
1.トリガーとなるイベントが発生した際に、一定時間待機するようなタイマーを設定する。
2.タイマーが終了する前に再度イベントが発生した場合、タイマーをリセットする。
3.タイマーが終了したら、初めてイベントに対応する処理を実行する。
これにより、タイマーの時間内に同じイベントが何度も発生した場合は処理を実行せずに待機することが可能になります。
Flutterでデバウンスさせる方法
Flutterでも、上記の考え方を活用してデバウンスの実装をすることが可能です。
複数の箇所でデバウンスを利用する場合は、以下のようにDebouncerクラスを作成すると便利です。
class Debouncer {
final int milliseconds;
VoidCallback? action;
Timer? _timer;
Debouncer({required this.milliseconds});
run(VoidCallback action) {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(Duration(milliseconds: milliseconds), action);
}
}
このクラスを使って、入力遅延させたい処理をラップする形で使用します。
class DebounceExample extends StatefulWidget {
_DebounceExampleState createState() => _DebounceExampleState();
}
class _DebounceExampleState extends State<DebounceExample> {
final _debouncer = Debouncer(milliseconds: 500);
String _searchQuery = '';
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Debounce Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
onChanged: (value) {
// 非同期処理などを_debouncer.run()でラップすることで処理を遅延させ、過剰に走らないようにする
_debouncer.run(() {
_performSearch(_searchQuery);
});
},
decoration: InputDecoration(
hintText: 'Enter search query',
),
),
SizedBox(height: 20),
Text('Search Query: $_searchQuery'),
],
),
),
);
}
Future<void> _performSearch(String query) async {
// 実際の処理内容を記述する
}
}
まとめ
今回はデバウンスというテクニックの基本的な説明と、Flutterにおける具体的な実装方法について解説しました。
検索欄だけでなく、非同期処理を含む処理が頻繁に呼び出されるケースであれば思いのほか有効です。頭の片隅にでもこの考え方を残してもらえれば幸いです。
Discussion
packageだとEasyDebonceやstream_transformでstream拡張して使うなどがあるかなと思います!