From 8ea1285e2932494e36485b3a4316ed236580faa1 Mon Sep 17 00:00:00 2001 From: Adil Hanney Date: Wed, 8 May 2024 05:17:54 +0100 Subject: [PATCH] perf(prefs): use completers instead of polling --- lib/data/prefs.dart | 61 ++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/lib/data/prefs.dart b/lib/data/prefs.dart index 5f599c4ab..33b373855 100644 --- a/lib/data/prefs.dart +++ b/lib/data/prefs.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:collection'; import 'dart:convert'; import 'dart:io'; @@ -320,12 +321,6 @@ abstract class IPref extends ValueNotifier { final T defaultValue; - bool _loaded = false; - - /// Whether this pref has changes that have yet to be saved to disk. - @protected - bool _saved = true; - IPref( this.key, this.defaultValue, { @@ -335,11 +330,12 @@ abstract class IPref extends ValueNotifier { deprecatedKeys = deprecatedKeys ?? [], super(defaultValue) { if (Prefs.testingMode) { - _loaded = true; + loaded = true; + return; } else { _load().then((T? loadedValue) { - _loaded = true; + loaded = true; if (loadedValue != null) { value = loadedValue; } @@ -367,23 +363,52 @@ abstract class IPref extends ValueNotifier { return super.value; } + bool _loaded = false; + Completer? _loadedCompleter = Completer(); bool get loaded => _loaded; - bool get saved => _saved; + @protected + set loaded(bool loaded) { + _loaded = loaded; - Future waitUntilLoaded() async { - while (!loaded) { - await Future.delayed(const Duration(milliseconds: 10)); + if (loaded) { + if (!_loadedCompleter!.isCompleted) _loadedCompleter!.complete(); + _loadedCompleter = null; } } + final _saved = ValueNotifier(true); + + /// Whether this pref has changes that have yet to be saved to disk. + bool get saved => _saved.value; + @protected + set saved(bool saved) { + _saved.value = saved; + } + + Future waitUntilLoaded() async { + if (loaded) return; + assert(_loadedCompleter != null, + '_loadedCompleter should be non-null until loaded'); + return _loadedCompleter!.future; + } + /// Waits until the value has been saved to disk. /// Note that there is no guarantee with shared preferences that /// the value will actually be saved to disk. @visibleForTesting Future waitUntilSaved() async { - while (!saved) { - await Future.delayed(const Duration(milliseconds: 10)); + if (saved) return; + + final completer = Completer(); + + void listener() { + if (!saved) return; + _saved.removeListener(listener); + completer.complete(); } + + _saved.addListener(listener); + return completer.future; } /// Lets us use notifyListeners outside of the class @@ -451,7 +476,7 @@ class PlainPref extends IPref { @override Future _save() async { - _saved = false; + saved = false; try { _prefs ??= await SharedPreferences.getInstance(); @@ -512,7 +537,7 @@ class PlainPref extends IPref { return await _prefs!.setString(key, value as String); } } finally { - _saved = true; + saved = true; } } @@ -637,7 +662,7 @@ class EncPref extends IPref { @override Future _save() async { - _saved = false; + saved = false; try { _storage ??= const FlutterSecureStorage(); if (T == String) @@ -646,7 +671,7 @@ class EncPref extends IPref { return await _storage!.write(key: key, value: jsonEncode(value)); return await _storage!.write(key: key, value: jsonEncode(value)); } finally { - _saved = true; + saved = true; } }