diff --git a/addons/im_livechat/static/src/models/messaging_initializer/messaging_initializer.js b/addons/im_livechat/static/src/models/messaging_initializer/messaging_initializer.js index 634fbb5dbfe..56e02d9b5de 100644 --- a/addons/im_livechat/static/src/models/messaging_initializer/messaging_initializer.js +++ b/addons/im_livechat/static/src/models/messaging_initializer/messaging_initializer.js @@ -2,6 +2,7 @@ odoo.define('im_livechat/static/src/models/messaging_initializer/messaging_initi 'use strict'; const { registerInstancePatchModel } = require('mail/static/src/model/model_core.js'); +const { executeGracefully } = require('mail/static/src/utils/utils.js'); registerInstancePatchModel('mail.messaging_initializer', 'im_livechat/static/src/models/messaging_initializer/messaging_initializer.js', { @@ -16,10 +17,7 @@ registerInstancePatchModel('mail.messaging_initializer', 'im_livechat/static/src async _initChannels(initMessagingData) { await this.async(() => this._super(initMessagingData)); const { channel_livechat = [] } = initMessagingData; - for (const data of channel_livechat) { - // there might be a lot of channels, insert each of them one by - // one asynchronously to avoid blocking the UI - await this.async(() => new Promise(resolve => setTimeout(resolve))); + return executeGracefully(channel_livechat.map(data => () => { const channel = this.env.models['mail.thread'].insert( this.env.models['mail.thread'].convertData(data), ); @@ -28,7 +26,7 @@ registerInstancePatchModel('mail.messaging_initializer', 'im_livechat/static/src if (!channel.isPinned) { channel.pin(); } - } + })); }, }); diff --git a/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js b/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js index da2f51757be..c22c197e87c 100644 --- a/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js +++ b/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js @@ -3,6 +3,7 @@ odoo.define('mail/static/src/models/messaging_initializer/messaging_initializer. const { registerNewModel } = require('mail/static/src/model/model_core.js'); const { one2one } = require('mail/static/src/model/model_field.js'); +const { executeGracefully } = require('mail/static/src/utils/utils.js'); function factory(dependencies) { @@ -118,7 +119,7 @@ function factory(dependencies) { // various suggestions in no particular order this._initCannedResponses(shortcodes); this._initCommands(commands); - await this.async(() => this._initMentionPartnerSuggestions(mention_partner_suggestions)); + this._initMentionPartnerSuggestions(mention_partner_suggestions); // channels when the rest of messaging is ready await this.async(() => this._initChannels(channel_slots)); // failures after channels @@ -149,10 +150,7 @@ function factory(dependencies) { channel_private_group = [], } = {}) { const channelsData = channel_channel.concat(channel_direct_message, channel_private_group); - for (const channelData of channelsData) { - // there might be a lot of channels, insert each of them one by - // one asynchronously to avoid blocking the UI - await this.async(() => new Promise(resolve => setTimeout(resolve))); + return executeGracefully(channelsData.map(channelData => () => { const convertedData = this.env.models['mail.thread'].convertData(channelData); if (!convertedData.members) { // channel_info does not return all members of channel for @@ -171,7 +169,7 @@ function factory(dependencies) { if (!channel.isPinned) { channel.pin(); } - } + })); } /** @@ -233,15 +231,12 @@ function factory(dependencies) { * @param {Object[]} mentionPartnerSuggestionsData */ async _initMentionPartnerSuggestions(mentionPartnerSuggestionsData) { - for (const suggestions of mentionPartnerSuggestionsData) { - for (const suggestion of suggestions) { - // there might be a lot of partners, insert each of them one - // by one asynchronously to avoid blocking the UI - await this.async(() => new Promise(resolve => setTimeout(resolve))); + return executeGracefully(mentionPartnerSuggestionsData.map(suggestions => () => { + return executeGracefully(suggestions.map(suggestion => () => { const { email, id, name } = suggestion; this.env.models['mail.partner'].insert({ email, id, name }); - } - } + })); + })); } /** diff --git a/addons/mail/static/src/utils/utils.js b/addons/mail/static/src/utils/utils.js index 8112eeca980..f502fc8f326 100644 --- a/addons/mail/static/src/utils/utils.js +++ b/addons/mail/static/src/utils/utils.js @@ -14,6 +14,25 @@ const { const classPatchMap = new WeakMap(); const eventHandledWeakMap = new WeakMap(); +/** + * Executes the provided functions in order, but with a potential delay between + * them if they take too much time. This is done in order to avoid blocking the + * main thread for too long. + * + * @param {function[]} functions + * @param {integer} [maxTimeFrame=100] time (in ms) until a delay is introduced + */ +async function executeGracefully(functions, maxTimeFrame = 100) { + let startDate = new Date(); + for (const func of functions) { + if (new Date() - startDate > maxTimeFrame) { + await new Promise(resolve => setTimeout(resolve)); + startDate = new Date(); + } + await func(); + } +} + /** * Returns whether the given event has been handled with the given markName. * @@ -147,6 +166,7 @@ function unpatchInstanceMethods(Class, patchName) { //------------------------------------------------------------------------------ return { + executeGracefully, isEventHandled, markEventHandled, nextTick,