Is it a good practice to execute futures together at Initialization of the app OR should it be executed one at a time?
At first glance, I'd suggest not blocking app startup with a call like this:
final initResults = await Future.wait(initFutures);
The user will not know anything is happening and will just see a blank screen for however long it takes for all the futures & initialization to complete.
Using a FutureBuilder
is perhaps the most common way to "wait" for async data to arrive, but also to show the user something while waiting.
I just answered a question with similar requirements (waiting for multiple, dependent sequential async data). Perhaps that will give you a rough idea of how to structure your app start up.
Comments
-
Zenko 10 months
Probably this is a noob question.
Suppose I have a Flutter code that has a few things to initialize at beginning before calling
runApp
. Consider this code:void main() async { WidgetsFlutterBinding.ensureInitialized(); /// Handles localizations final flutterI18nDelegate = FlutterI18nDelegate( translationLoader: FileTranslationLoader( fallbackFile: 'en_US', basePath: 'assets/i18n', forcedLocale: Locale('en_US'), decodeStrategies: [JsonDecodeStrategy()])); /// Handles AppsFlyer Integrations final AppsFlyerOptions appsFlyerOptions = AppsFlyerOptions( afDevKey: 'MY-KEY', appId: 'APP-ID', showDebug: true, ); final appsFlyerSdk = AppsflyerSdk(appsFlyerOptions); /// Starts the Repository final repository = Repository(); /// Initialize Mobile Ads final mobileAds = MobileAds.instance.initialize().then((InitializationStatus status) => print('Mobile Ads Initialized! Status: ${status.adapterStatuses}')); final List<Future<dynamic>> initFutures = [ /// Load Localizations flutterI18nDelegate.load(null), /// Initialize Firebase Firebase.initializeApp(), /// Initialize Repository repository.initialize(), /// Initialize AppsFlyer SDK appsFlyerSdk.initSdk(), /// Initialize Ads mobileAds, /// Initialize adaptiveMode (Dark, Light, System) AdaptiveTheme.getThemeMode() ]; /// Run everything together to save time final initResults = await Future.wait(initFutures); final FlutterI18n flutterI18n = initResults.first; final AdaptiveThemeMode themeSetting = initResults.last ?? AdaptiveThemeMode.system; runApp( RestartWidget( child: MultiRepositoryProvider( providers: [ /// ---- ALL Repository Needs ---- ], child: MultiBlocProvider( providers: [ /// ---- ALL Bloc ---- ], child: MyApp( flutterI18nDelegate: flutterI18nDelegate, themeSetting: themeSetting, ))), ), ); }
Notice that none of the futures being called depending on each other.
But is it a good practice to run futures like this in the start of the app?
Of course, the aim for executing futures together is to save time.
OR Is it better to do it like this:
/// Copying the futures part only /// Load Localizations final FlutterI18n flutterI18n = await flutterI18nDelegate.load(null); /// Initialize Firebase await Firebase.initializeApp(); /// Initialize Repository await repository.initialize(); /// Initialize AppsFlyer SDK await appsFlyerSdk.initSdk(); /// Initialize Ads await MobileAds.instance.initialize().then((InitializationStatus status) => print('Mobile Ads Initialized! Status: ${status.adapterStatuses}')); /// Initialize adaptiveMode (Dark, Light, System) final AdaptiveThemeMode themeSetting = (await AdaptiveTheme.getThemeMode()) ?? AdaptiveThemeMode.system;
Please feel free to point out anything I probably miss or can be a concern for doing this or it is all good? and why? (e.g: memory leak concerns?)
Thanks
-
Zenko over 2 yearsGood suggestion! I'll do that. So do you think it is a good practice to put all together into one list of futures and execute them at once or should we execute them one after another? The question has been modified to accurately explain this point.
-
Baker over 2 years@Zenko re: I think performance-wise they can be identical if you remove the
await
from each line on your 2nd code example. (You mention each future is independent of the others and order doesn't matter?) From a debugging and error-handling perspective, I would think the 2nd is easier to deal with. On a list of futures in.wait
, any error will cause the future to return with that error. Any others are discarded: see api.flutter.dev/flutter/dart-async/Future/wait.html -
Zenko over 2 years(You mention each future is independent of the others and order doesn't matter?) <== Yes
-
Zenko over 2 yearsThe error handling perspective is a good one, thanks. However, if you remove the
await
from each line, then will thetry catch
block (inmain.dart
) going to catch the error? Although we do have thetry catch
block inside each of those method calls respectively, but the error message may not be shown to the user. Alternatively, we have to pass the context of the build and it will be messy. -
Baker over 2 years
async / await
(or lack thereof) shouldn't affect the scope oftry/catch
blocks dart.dev/codelabs/async-await#handling-errors. What changes by removingawait
is the order of completion, which is no longer guaranteed to be as written. So any of the async calls could throw and you'll have to decide whether to catch it locally orrethrow
for handling higher up, say in main.dart. You still get to decide where & when to notify the user of the exception.