From 6e206184b6e2f1c79967f34f144aae70eaaa44d8 Mon Sep 17 00:00:00 2001 From: PepijnSenders Date: Tue, 17 Jan 2023 17:50:01 +0100 Subject: [PATCH] Run project config parse inside web worker --- .../project_config/parseDatafileWorker.ts | 33 +++++++++ .../project_config/project_config_manager.ts | 70 ++++++++++++++----- 2 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 packages/optimizely-sdk/lib/core/project_config/parseDatafileWorker.ts diff --git a/packages/optimizely-sdk/lib/core/project_config/parseDatafileWorker.ts b/packages/optimizely-sdk/lib/core/project_config/parseDatafileWorker.ts new file mode 100644 index 000000000..6eea981ab --- /dev/null +++ b/packages/optimizely-sdk/lib/core/project_config/parseDatafileWorker.ts @@ -0,0 +1,33 @@ +import { tryCreatingProjectConfig } from '.'; + +const messageListener = (e: MessageEvent<{ type: string; payload: { datafile: string | object } }>) => { + if (e.data.type === 'START_PARSE_DATAFILE') { + const { configObj, error } = tryCreatingProjectConfig({ + datafile: e.data.payload.datafile, + logger: { + log: (...args) => { + console.log(...args); + }, + }, + }); + + if (error) { + postMessage({ + type: 'DATAFILE_PARSE_ERROR', + payload: { + error: error.message, + parsedDatafile: null + } + }) + } else { + postMessage({ + type: 'DATAFILE_PARSE_SUCCESS', + payload: { + parsedDatafile: configObj + } + }) + } + } +}; + +addEventListener('message', messageListener); diff --git a/packages/optimizely-sdk/lib/core/project_config/project_config_manager.ts b/packages/optimizely-sdk/lib/core/project_config/project_config_manager.ts index aea5951bd..ebbbd7bbd 100644 --- a/packages/optimizely-sdk/lib/core/project_config/project_config_manager.ts +++ b/packages/optimizely-sdk/lib/core/project_config/project_config_manager.ts @@ -18,11 +18,7 @@ import { sprintf } from '../../utils/fns'; import { ERROR_MESSAGES } from '../../utils/enums'; import { createOptimizelyConfig } from '../optimizely_config'; -import { - OnReadyResult, - OptimizelyConfig, - DatafileManager, -} from '../../shared_types'; +import { OnReadyResult, OptimizelyConfig, DatafileManager } from '../../shared_types'; import { ProjectConfig, toDatafile, tryCreatingProjectConfig } from '../project_config'; const logger = getLogger(); @@ -31,12 +27,13 @@ const MODULE_NAME = 'PROJECT_CONFIG_MANAGER'; interface ProjectConfigManagerConfig { // TODO[OASIS-6649]: Don't use object type // eslint-disable-next-line @typescript-eslint/ban-types - datafile?: string | object, + datafile?: string | object; jsonSchemaValidator?: { - validate(jsonObject: unknown): boolean, + validate(jsonObject: unknown): boolean; }; - sdkKey?: string, - datafileManager?: DatafileManager + sdkKey?: string; + datafileManager?: DatafileManager; + useWebWorker?: boolean; } /** @@ -74,7 +71,9 @@ export class ProjectConfigManager { this.jsonSchemaValidator = config.jsonSchemaValidator; if (!config.datafile && !config.sdkKey) { - const datafileAndSdkKeyMissingError = new Error(sprintf(ERROR_MESSAGES.DATAFILE_AND_SDK_KEY_MISSING, MODULE_NAME)); + const datafileAndSdkKeyMissingError = new Error( + sprintf(ERROR_MESSAGES.DATAFILE_AND_SDK_KEY_MISSING, MODULE_NAME) + ); this.readyPromise = Promise.resolve({ success: false, reason: getErrorMessage(datafileAndSdkKeyMissingError), @@ -85,10 +84,14 @@ export class ProjectConfigManager { let handleNewDatafileException = null; if (config.datafile) { - handleNewDatafileException = this.handleNewDatafile(config.datafile); + if (config.useWebWorker) { + this.readyPromise = this.handleDatafileInWebWorker(config.datafile); + } else { + handleNewDatafileException = this.handleNewDatafile(config.datafile); + } } - if (config.sdkKey && config.datafileManager) { + if (config.sdkKey && config.datafileManager) { this.datafileManager = config.datafileManager; this.datafileManager.start(); this.readyPromise = this.datafileManager @@ -105,7 +108,7 @@ export class ProjectConfigManager { reason: getErrorMessage(handleNewDatafileException, 'Invalid datafile'), }); } - } catch (ex: any) { + } catch (ex) { logger.error(ex); this.readyPromise = Promise.resolve({ success: false, @@ -137,7 +140,7 @@ export class ProjectConfigManager { return { success: false, reason: getErrorMessage(null, 'Datafile manager is not provided'), - } + }; } /** @@ -180,7 +183,7 @@ export class ProjectConfigManager { const { configObj, error } = tryCreatingProjectConfig({ datafile: newDatafile, jsonSchemaValidator: this.jsonSchemaValidator, - logger: logger + logger: logger, }); if (error) { @@ -197,6 +200,41 @@ export class ProjectConfigManager { return error; } + private async handleDatafileInWebWorker(newDatafile: string | object): Promise { + const workerCode = ''; + const workerBlob = new Blob([workerCode]); + const workerUrl = URL.createObjectURL(workerBlob); + const worker = new Worker(workerUrl); + + return new Promise((resolve, reject) => { + worker.onmessage = ( + e: MessageEvent<{ type: string; payload: { parsedDatafile: ProjectConfig | null; error?: string } }> + ) => { + if (e.data.type === 'DATAFILE_PARSE_SUCCESS') { + this.configObj = e.data.payload.parsedDatafile; + + resolve({ + success: true, + }); + } + + if (e.data.type === 'DATAFILE_PARSE_ERROR') { + reject({ + success: false, + reason: e.data.payload.error, + }); + } + }; + + worker.postMessage({ + type: 'START_PARSE_DATAFILE', + payload: { + datafile: newDatafile, + }, + }); + }); + } + /** * Returns the current project config object, or null if no project config object * is available @@ -248,7 +286,7 @@ export class ProjectConfigManager { * @param {Function} listener * @return {Function} */ - onUpdate(listener: (config: ProjectConfig) => void): (() => void) { + onUpdate(listener: (config: ProjectConfig) => void): () => void { this.updateListeners.push(listener); return () => { const index = this.updateListeners.indexOf(listener);