From ff83090e8dd0d4e023215463e21dfd4d7c92ce06 Mon Sep 17 00:00:00 2001 From: Tim Bradgate Date: Fri, 30 Jan 2026 01:34:11 +0000 Subject: [PATCH] Refactor frontend components to use Promise.all for parallel data fetching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Converts sequential await chains to parallel Promise.all calls in 10 Vue components, significantly improving page load times by running independent API calls concurrently. High priority refactors (largest impact): - ScriptEditor.vue: 14 sequential awaits → parallel groups - CueEditor.vue: 13 sequential awaits → parallel groups - ConfigMics.vue: 6 sequential awaits → single Promise.all Medium priority refactors: - ConfigSystem.vue: 4 parallel fetches - ConfigCharacters.vue: 2 parallel fetches - CharacterGroups.vue: 2 parallel fetches - PropsList.vue: 2 parallel fetches - SceneryList.vue: 2 parallel fetches Additional optimizations: - App.vue: Parallelize RBAC roles with WebSocket state check; parallelize user RBAC and settings after user fetch - ShowLiveView.vue: Parallelize session data and act list fetches Co-Authored-By: Claude Opus 4.5 --- client/src/App.vue | 14 +++--- client/src/views/show/ShowLiveView.vue | 5 +-- .../views/show/config/ConfigCharacters.vue | 3 +- client/src/views/show/config/ConfigMics.vue | 14 +++--- .../vue_components/config/ConfigSystem.vue | 10 +++-- .../config/characters/CharacterGroups.vue | 3 +- .../show/config/cues/CueEditor.vue | 44 +++++++++--------- .../show/config/script/ScriptEditor.vue | 45 ++++++++++--------- .../show/config/stage/PropsList.vue | 3 +- .../show/config/stage/SceneryList.vue | 3 +- 10 files changed, 68 insertions(+), 76 deletions(-) diff --git a/client/src/App.vue b/client/src/App.vue index 27fe71f4..8c243c9b 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -324,19 +324,15 @@ export default { async awaitWSConnect() { if (this.WEBSOCKET_HEALTHY) { clearTimeout(this.loadTimer); - await this.GET_RBAC_ROLES(); - // Check WebSocket state for any pending operations - if (this.WEBSOCKET_HAS_PENDING_OPERATIONS) { - await this.CHECK_WEBSOCKET_STATE(); - } + await Promise.all([ + this.GET_RBAC_ROLES(), + this.WEBSOCKET_HAS_PENDING_OPERATIONS ? this.CHECK_WEBSOCKET_STATE() : Promise.resolve(), + ]); - // Check for authentication via token first if (this.AUTH_TOKEN) { - // Then get user data await this.GET_CURRENT_USER(); - await this.GET_CURRENT_RBAC(); - await this.GET_USER_SETTINGS(); + await Promise.all([this.GET_CURRENT_RBAC(), this.GET_USER_SETTINGS()]); } if (this.SETTINGS.current_show != null) { diff --git a/client/src/views/show/ShowLiveView.vue b/client/src/views/show/ShowLiveView.vue index 18940bd9..b657fde7 100644 --- a/client/src/views/show/ShowLiveView.vue +++ b/client/src/views/show/ShowLiveView.vue @@ -166,16 +166,13 @@ export default { }, }, async mounted() { - await this.GET_SHOW_SESSION_DATA(); + await Promise.all([this.GET_SHOW_SESSION_DATA(), this.GET_ACT_LIST()]); this.loadedSessionData = true; if (this.CURRENT_SHOW_INTERVAL != null) { this.setupIntervalTimer(); } - // Load act list (needed for interval overlay heading) - await this.GET_ACT_LIST(); - // Setup elapsed time tracking this.updateElapsedTime(); this.startTime = this.createDateAsUTC( diff --git a/client/src/views/show/config/ConfigCharacters.vue b/client/src/views/show/config/ConfigCharacters.vue index bbb55407..74dd670c 100644 --- a/client/src/views/show/config/ConfigCharacters.vue +++ b/client/src/views/show/config/ConfigCharacters.vue @@ -218,8 +218,7 @@ export default { }, }, async mounted() { - await this.GET_CHARACTER_LIST(); - await this.GET_CAST_LIST(); + await Promise.all([this.GET_CHARACTER_LIST(), this.GET_CAST_LIST()]); }, methods: { resetNewForm() { diff --git a/client/src/views/show/config/ConfigMics.vue b/client/src/views/show/config/ConfigMics.vue index 671bc213..413ce742 100644 --- a/client/src/views/show/config/ConfigMics.vue +++ b/client/src/views/show/config/ConfigMics.vue @@ -52,12 +52,14 @@ export default { }; }, async mounted() { - await this.GET_SCENE_LIST(); - await this.GET_ACT_LIST(); - await this.GET_CHARACTER_LIST(); - await this.GET_CAST_LIST(); - await this.GET_MICROPHONE_LIST(); - await this.GET_MIC_ALLOCATIONS(); + await Promise.all([ + this.GET_SCENE_LIST(), + this.GET_ACT_LIST(), + this.GET_CHARACTER_LIST(), + this.GET_CAST_LIST(), + this.GET_MICROPHONE_LIST(), + this.GET_MIC_ALLOCATIONS(), + ]); this.loaded = true; }, methods: { diff --git a/client/src/vue_components/config/ConfigSystem.vue b/client/src/vue_components/config/ConfigSystem.vue index 8c3342e4..bbb3743b 100644 --- a/client/src/vue_components/config/ConfigSystem.vue +++ b/client/src/vue_components/config/ConfigSystem.vue @@ -301,10 +301,12 @@ export default { ...mapGetters(['SCRIPT_MODES']), }, async mounted() { - await this.getAvailableShows(); - await this.getConnectedClients(); - await this.GET_SCRIPT_MODES(); - await this.getVersionStatus(); + await Promise.all([ + this.getAvailableShows(), + this.getConnectedClients(), + this.GET_SCRIPT_MODES(), + this.getVersionStatus(), + ]); this.loading = false; // Start time update interval for reactive "time ago" display diff --git a/client/src/vue_components/show/config/characters/CharacterGroups.vue b/client/src/vue_components/show/config/characters/CharacterGroups.vue index e77ec8b4..3fea250d 100644 --- a/client/src/vue_components/show/config/characters/CharacterGroups.vue +++ b/client/src/vue_components/show/config/characters/CharacterGroups.vue @@ -207,8 +207,7 @@ export default { ...mapGetters(['CHARACTER_LIST', 'CHARACTER_GROUP_LIST', 'IS_SHOW_EDITOR']), }, async mounted() { - await this.GET_CHARACTER_LIST(); - await this.GET_CHARACTER_GROUP_LIST(); + await Promise.all([this.GET_CHARACTER_LIST(), this.GET_CHARACTER_GROUP_LIST()]); this.loading = false; }, methods: { diff --git a/client/src/vue_components/show/config/cues/CueEditor.vue b/client/src/vue_components/show/config/cues/CueEditor.vue index b1e3c8a4..c424147c 100644 --- a/client/src/vue_components/show/config/cues/CueEditor.vue +++ b/client/src/vue_components/show/config/cues/CueEditor.vue @@ -183,30 +183,28 @@ export default { }, }, async beforeMount() { - // Get the current user - await this.GET_CURRENT_USER(); - // Config status - await this.GET_SCRIPT_CONFIG_STATUS(); - // Show details - await this.GET_ACT_LIST(); - await this.GET_SCENE_LIST(); - await this.GET_CHARACTER_LIST(); - await this.GET_CHARACTER_GROUP_LIST(); - await this.GET_CUE_TYPES(); - await this.LOAD_CUES(); - await this.GET_CUTS(); - await this.GET_STAGE_DIRECTION_STYLES(); + await Promise.all([ + this.GET_CURRENT_USER().then(() => { + if (this.CURRENT_USER != null) { + return Promise.all([ + this.GET_STAGE_DIRECTION_STYLE_OVERRIDES(), + this.GET_CUE_COLOUR_OVERRIDES(), + ]); + } + return Promise.resolve(); + }), + this.GET_SCRIPT_CONFIG_STATUS(), + this.GET_ACT_LIST(), + this.GET_SCENE_LIST(), + this.GET_CHARACTER_LIST(), + this.GET_CHARACTER_GROUP_LIST(), + this.GET_CUE_TYPES(), + this.LOAD_CUES(), + this.GET_CUTS(), + this.GET_STAGE_DIRECTION_STYLES(), + this.getMaxScriptPage(), + ]); - // User related stuff - if (this.CURRENT_USER != null) { - await this.GET_STAGE_DIRECTION_STYLE_OVERRIDES(); - await this.GET_CUE_COLOUR_OVERRIDES(); - } - - // Get the max page of the saved version of the script - await this.getMaxScriptPage(); - - // Initialisation of page data // Initialisation of page data const storedPage = localStorage.getItem('cueEditPage'); if (storedPage != null) { diff --git a/client/src/vue_components/show/config/script/ScriptEditor.vue b/client/src/vue_components/show/config/script/ScriptEditor.vue index eb2ab219..9eb7386f 100644 --- a/client/src/vue_components/show/config/script/ScriptEditor.vue +++ b/client/src/vue_components/show/config/script/ScriptEditor.vue @@ -325,29 +325,30 @@ export default { }, }, async beforeMount() { - // Get the current user - await this.GET_CURRENT_USER(); - await this.GET_USER_SETTINGS(); - // Config status - await this.GET_SCRIPT_CONFIG_STATUS(); - // Show details - await this.GET_ACT_LIST(); - await this.GET_SCENE_LIST(); - await this.GET_CHARACTER_LIST(); - await this.GET_CHARACTER_GROUP_LIST(); - // Stage direction styles - await this.GET_STAGE_DIRECTION_STYLES(); - // User related stuff - if (this.CURRENT_USER != null) { - await this.GET_STAGE_DIRECTION_STYLE_OVERRIDES(); - await this.GET_CUE_COLOUR_OVERRIDES(); - } - // Handle script cuts - await this.GET_CUTS(); - this.resetCutsToSaved(); + await Promise.all([ + this.GET_CURRENT_USER() + .then(() => this.GET_USER_SETTINGS()) + .then(() => { + if (this.CURRENT_USER != null) { + return Promise.all([ + this.GET_STAGE_DIRECTION_STYLE_OVERRIDES(), + this.GET_CUE_COLOUR_OVERRIDES(), + ]); + } + return Promise.resolve(); + }), + this.GET_SCRIPT_CONFIG_STATUS(), + this.GET_ACT_LIST(), + this.GET_SCENE_LIST(), + this.GET_CHARACTER_LIST(), + this.GET_CHARACTER_GROUP_LIST(), + this.GET_STAGE_DIRECTION_STYLES(), + this.GET_CUTS(), + this.getMaxScriptPage(), + ]); - // Get the max page of the saved version of the script - await this.getMaxScriptPage(); + // Handle script cuts (depends on GET_CUTS completing) + this.resetCutsToSaved(); // Initialisation of page data const storedPage = localStorage.getItem('scriptEditPage'); diff --git a/client/src/vue_components/show/config/stage/PropsList.vue b/client/src/vue_components/show/config/stage/PropsList.vue index ef0b82bf..37fa495b 100644 --- a/client/src/vue_components/show/config/stage/PropsList.vue +++ b/client/src/vue_components/show/config/stage/PropsList.vue @@ -337,8 +337,7 @@ export default { ...mapGetters(['PROPS_LIST', 'PROP_TYPES', 'IS_SHOW_EDITOR', 'PROP_TYPE_BY_ID']), }, async mounted() { - await this.GET_PROP_TYPES(); - await this.GET_PROPS_LIST(); + await Promise.all([this.GET_PROP_TYPES(), this.GET_PROPS_LIST()]); }, methods: { resetNewPropTypeForm() { diff --git a/client/src/vue_components/show/config/stage/SceneryList.vue b/client/src/vue_components/show/config/stage/SceneryList.vue index 1ca44dab..b61363df 100644 --- a/client/src/vue_components/show/config/stage/SceneryList.vue +++ b/client/src/vue_components/show/config/stage/SceneryList.vue @@ -348,8 +348,7 @@ export default { ...mapGetters(['SCENERY_LIST', 'SCENERY_TYPES', 'IS_SHOW_EDITOR', 'SCENERY_TYPE_BY_ID']), }, async mounted() { - await this.GET_SCENERY_TYPES(); - await this.GET_SCENERY_LIST(); + await Promise.all([this.GET_SCENERY_TYPES(), this.GET_SCENERY_LIST()]); }, methods: { resetNewSceneryTypeForm() {