From: Jérôme Benoit Date: Sat, 27 Apr 2024 21:17:16 +0000 (+0200) Subject: feat: add task function properties support X-Git-Tag: v4.0.0~1^2~26 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=31847469b406e46688d8aafb880e250706dd8aee;p=poolifier.git feat: add task function properties support closes #843 Signed-off-by: Jérôme Benoit --- diff --git a/docs/api.md b/docs/api.md index 5e4eeb22..c62e7f50 100644 --- a/docs/api.md +++ b/docs/api.md @@ -11,7 +11,7 @@ - [`pool.hasTaskFunction(name)`](#poolhastaskfunctionname) - [`pool.addTaskFunction(name, fn)`](#pooladdtaskfunctionname-fn) - [`pool.removeTaskFunction(name)`](#poolremovetaskfunctionname) - - [`pool.listTaskFunctionNames()`](#poollisttaskfunctionnames) + - [`pool.listTaskFunctionsProperties()`](#poollisttaskfunctionsproperties) - [`pool.setDefaultTaskFunction(name)`](#poolsetdefaulttaskfunctionname) - [Pool options](#pool-options) - [Worker](#worker) @@ -19,7 +19,7 @@ - [`YourWorker.hasTaskFunction(name)`](#yourworkerhastaskfunctionname) - [`YourWorker.addTaskFunction(name, fn)`](#yourworkeraddtaskfunctionname-fn) - [`YourWorker.removeTaskFunction(name)`](#yourworkerremovetaskfunctionname) - - [`YourWorker.listTaskFunctionNames()`](#yourworkerlisttaskfunctionnames) + - [`YourWorker.listTaskFunctionsProperties()`](#yourworkerlisttaskfunctionsproperties) - [`YourWorker.setDefaultTaskFunction(name)`](#yourworkersetdefaulttaskfunctionname) ## Pool @@ -72,9 +72,9 @@ This method is available on both pool implementations and returns a boolean prom This method is available on both pool implementations and returns a boolean promise. -### `pool.listTaskFunctionNames()` +### `pool.listTaskFunctionsProperties()` -This method is available on both pool implementations and returns an array of the task function names. +This method is available on both pool implementations and returns an array of the task function properties. ### `pool.setDefaultTaskFunction(name)` @@ -186,9 +186,9 @@ This method is available on both worker implementations and returns `{ status: b This method is available on both worker implementations and returns `{ status: boolean, error?: Error }`. -#### `YourWorker.listTaskFunctionNames()` +#### `YourWorker.listTaskFunctionsProperties()` -This method is available on both worker implementations and returns an array of the task function names. +This method is available on both worker implementations and returns an array of the task function properties. #### `YourWorker.setDefaultTaskFunction(name)` diff --git a/src/deque.ts b/src/deque.ts index 52441a3e..5fb26770 100644 --- a/src/deque.ts +++ b/src/deque.ts @@ -187,6 +187,11 @@ export class Deque { } } + /** + * Increments the size of the deque. + * + * @returns The new size of the deque. + */ private incrementSize (): number { ++this.size if (this.size > this.maxSize) { diff --git a/src/pools/abstract-pool.ts b/src/pools/abstract-pool.ts index 01e621c2..88c04a85 100644 --- a/src/pools/abstract-pool.ts +++ b/src/pools/abstract-pool.ts @@ -7,10 +7,12 @@ import type { TransferListItem } from 'node:worker_threads' import type { MessageValue, PromiseResponseWrapper, - Task + Task, + TaskFunctionProperties } from '../utility-types.js' import { average, + buildTaskFunctionProperties, DEFAULT_TASK_NAME, EMPTY_FUNCTION, exponentialDelay, @@ -22,7 +24,10 @@ import { round, sleep } from '../utils.js' -import type { TaskFunction } from '../worker/task-functions.js' +import type { + TaskFunction, + TaskFunctionObject +} from '../worker/task-functions.js' import { KillBehaviors } from '../worker/worker-options.js' import { type IPool, @@ -101,9 +106,12 @@ export abstract class AbstractPool< /** * The task functions added at runtime map: * - `key`: The task function name. - * - `value`: The task function itself. + * - `value`: The task function object. */ - private readonly taskFunctions: Map> + private readonly taskFunctions: Map< + string, + TaskFunctionObject + > /** * Whether the pool is started or not. @@ -173,7 +181,7 @@ export abstract class AbstractPool< this.setupHook() - this.taskFunctions = new Map>() + this.taskFunctions = new Map>() this.started = false this.starting = false @@ -805,8 +813,10 @@ export abstract class AbstractPool< public hasTaskFunction (name: string): boolean { for (const workerNode of this.workerNodes) { if ( - Array.isArray(workerNode.info.taskFunctionNames) && - workerNode.info.taskFunctionNames.includes(name) + Array.isArray(workerNode.info.taskFunctionsProperties) && + workerNode.info.taskFunctionsProperties.some( + taskFunctionProperties => taskFunctionProperties.name === name + ) ) { return true } @@ -817,7 +827,7 @@ export abstract class AbstractPool< /** @inheritDoc */ public async addTaskFunction ( name: string, - fn: TaskFunction + fn: TaskFunction | TaskFunctionObject ): Promise { if (typeof name !== 'string') { throw new TypeError('name argument must be a string') @@ -825,13 +835,16 @@ export abstract class AbstractPool< if (typeof name === 'string' && name.trim().length === 0) { throw new TypeError('name argument must not be an empty string') } - if (typeof fn !== 'function') { - throw new TypeError('fn argument must be a function') + if (typeof fn === 'function') { + fn = { taskFunction: fn } satisfies TaskFunctionObject + } + if (typeof fn.taskFunction !== 'function') { + throw new TypeError('taskFunction property must be a function') } const opResult = await this.sendTaskFunctionOperationToWorkers({ taskFunctionOperation: 'add', - taskFunctionName: name, - taskFunction: fn.toString() + taskFunctionProperties: buildTaskFunctionProperties(name, fn), + taskFunction: fn.taskFunction.toString() }) this.taskFunctions.set(name, fn) return opResult @@ -846,7 +859,10 @@ export abstract class AbstractPool< } const opResult = await this.sendTaskFunctionOperationToWorkers({ taskFunctionOperation: 'remove', - taskFunctionName: name + taskFunctionProperties: buildTaskFunctionProperties( + name, + this.taskFunctions.get(name) + ) }) this.deleteTaskFunctionWorkerUsages(name) this.taskFunctions.delete(name) @@ -854,13 +870,13 @@ export abstract class AbstractPool< } /** @inheritDoc */ - public listTaskFunctionNames (): string[] { + public listTaskFunctionsProperties (): TaskFunctionProperties[] { for (const workerNode of this.workerNodes) { if ( - Array.isArray(workerNode.info.taskFunctionNames) && - workerNode.info.taskFunctionNames.length > 0 + Array.isArray(workerNode.info.taskFunctionsProperties) && + workerNode.info.taskFunctionsProperties.length > 0 ) { - return workerNode.info.taskFunctionNames + return workerNode.info.taskFunctionsProperties } } return [] @@ -870,7 +886,10 @@ export abstract class AbstractPool< public async setDefaultTaskFunction (name: string): Promise { return await this.sendTaskFunctionOperationToWorkers({ taskFunctionOperation: 'default', - taskFunctionName: name + taskFunctionProperties: buildTaskFunctionProperties( + name, + this.taskFunctions.get(name) + ) }) } @@ -1185,8 +1204,8 @@ export abstract class AbstractPool< const workerInfo = this.getWorkerInfo(workerNodeKey) return ( workerInfo != null && - Array.isArray(workerInfo.taskFunctionNames) && - workerInfo.taskFunctionNames.length > 2 + Array.isArray(workerInfo.taskFunctionsProperties) && + workerInfo.taskFunctionsProperties.length > 2 ) } @@ -1329,11 +1348,14 @@ export abstract class AbstractPool< checkActive: true }) if (this.taskFunctions.size > 0) { - for (const [taskFunctionName, taskFunction] of this.taskFunctions) { + for (const [taskFunctionName, taskFunctionObject] of this.taskFunctions) { this.sendTaskFunctionOperationToWorker(workerNodeKey, { taskFunctionOperation: 'add', - taskFunctionName, - taskFunction: taskFunction.toString() + taskFunctionProperties: buildTaskFunctionProperties( + taskFunctionName, + taskFunctionObject + ), + taskFunction: taskFunctionObject.taskFunction.toString() }).catch((error: unknown) => { this.emitter?.emit(PoolEvents.error, error) }) @@ -1587,10 +1609,10 @@ export abstract class AbstractPool< ) { workerInfo.stealing = false // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - for (const taskName of workerInfo.taskFunctionNames!) { + for (const taskFunctionProperties of workerInfo.taskFunctionsProperties!) { this.resetTaskSequentiallyStolenStatisticsTaskFunctionWorkerUsage( workerNodeKey, - taskName + taskFunctionProperties.name ) } this.resetTaskSequentiallyStolenStatisticsWorkerUsage(workerNodeKey) @@ -1726,21 +1748,21 @@ export abstract class AbstractPool< message: MessageValue ): void => { this.checkMessageWorkerId(message) - const { workerId, ready, taskId, taskFunctionNames } = message - if (ready != null && taskFunctionNames != null) { + const { workerId, ready, taskId, taskFunctionsProperties } = message + if (ready != null && taskFunctionsProperties != null) { // Worker ready response received from worker this.handleWorkerReadyResponse(message) - } else if (taskId != null) { - // Task execution response received from worker - this.handleTaskExecutionResponse(message) - } else if (taskFunctionNames != null) { - // Task function names message received from worker + } else if (taskFunctionsProperties != null) { + // Task function properties message received from worker const workerInfo = this.getWorkerInfo( this.getWorkerNodeKeyByWorkerId(workerId) ) if (workerInfo != null) { - workerInfo.taskFunctionNames = taskFunctionNames + workerInfo.taskFunctionsProperties = taskFunctionsProperties } + } else if (taskId != null) { + // Task execution response received from worker + this.handleTaskExecutionResponse(message) } } @@ -1752,14 +1774,14 @@ export abstract class AbstractPool< } private handleWorkerReadyResponse (message: MessageValue): void { - const { workerId, ready, taskFunctionNames } = message + const { workerId, ready, taskFunctionsProperties } = message if (ready == null || !ready) { throw new Error(`Worker ${workerId} failed to initialize`) } const workerNode = this.workerNodes[this.getWorkerNodeKeyByWorkerId(workerId)] workerNode.info.ready = ready - workerNode.info.taskFunctionNames = taskFunctionNames + workerNode.info.taskFunctionsProperties = taskFunctionsProperties this.checkAndEmitReadyEvent() } diff --git a/src/pools/pool.ts b/src/pools/pool.ts index bcf7d938..9eec76b9 100644 --- a/src/pools/pool.ts +++ b/src/pools/pool.ts @@ -2,6 +2,7 @@ import type { ClusterSettings } from 'node:cluster' import type { EventEmitterAsyncResource } from 'node:events' import type { TransferListItem, WorkerOptions } from 'node:worker_threads' +import type { TaskFunctionProperties } from '../utility-types.js' import type { TaskFunction } from '../worker/task-functions.js' import type { WorkerChoiceStrategy, @@ -321,11 +322,11 @@ export interface IPool< */ readonly removeTaskFunction: (name: string) => Promise /** - * Lists the names of task function available in this pool. + * Lists the properties of task functions available in this pool. * - * @returns The names of task function available in this pool. + * @returns The properties of task functions available in this pool. */ - readonly listTaskFunctionNames: () => string[] + readonly listTaskFunctionsProperties: () => TaskFunctionProperties[] /** * Sets the default task function in this pool. * diff --git a/src/pools/worker-node.ts b/src/pools/worker-node.ts index 1a4df6cd..a2878d89 100644 --- a/src/pools/worker-node.ts +++ b/src/pools/worker-node.ts @@ -169,21 +169,21 @@ export class WorkerNode /** @inheritdoc */ public getTaskFunctionWorkerUsage (name: string): WorkerUsage | undefined { - if (!Array.isArray(this.info.taskFunctionNames)) { + if (!Array.isArray(this.info.taskFunctionsProperties)) { throw new Error( - `Cannot get task function worker usage for task function name '${name}' when task function names list is not yet defined` + `Cannot get task function worker usage for task function name '${name}' when task function properties list is not yet defined` ) } if ( - Array.isArray(this.info.taskFunctionNames) && - this.info.taskFunctionNames.length < 3 + Array.isArray(this.info.taskFunctionsProperties) && + this.info.taskFunctionsProperties.length < 3 ) { throw new Error( - `Cannot get task function worker usage for task function name '${name}' when task function names list has less than 3 elements` + `Cannot get task function worker usage for task function name '${name}' when task function properties list has less than 3 elements` ) } if (name === DEFAULT_TASK_NAME) { - name = this.info.taskFunctionNames[1] + name = this.info.taskFunctionsProperties[1].name } if (!this.taskFunctionsUsage.has(name)) { this.taskFunctionsUsage.set(name, this.initTaskFunctionWorkerUsage(name)) @@ -262,7 +262,7 @@ export class WorkerNode if ( (task.name === DEFAULT_TASK_NAME && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - name === this.info.taskFunctionNames![1]) || + name === this.info.taskFunctionsProperties![1].name) || (task.name !== DEFAULT_TASK_NAME && name === task.name) ) { ++taskFunctionQueueSize diff --git a/src/pools/worker.ts b/src/pools/worker.ts index 12a2e321..ac6718fb 100644 --- a/src/pools/worker.ts +++ b/src/pools/worker.ts @@ -2,7 +2,7 @@ import type { EventEmitter } from 'node:events' import type { MessageChannel, WorkerOptions } from 'node:worker_threads' import type { CircularArray } from '../circular-array.js' -import type { Task } from '../utility-types.js' +import type { Task, TaskFunctionProperties } from '../utility-types.js' /** * Callback invoked when the worker has started successfully. @@ -173,9 +173,9 @@ export interface WorkerInfo { */ stealing: boolean /** - * Task function names. + * Task functions properties. */ - taskFunctionNames?: string[] + taskFunctionsProperties?: TaskFunctionProperties[] } /** diff --git a/src/utility-types.ts b/src/utility-types.ts index b3cea1a1..10d9183c 100644 --- a/src/utility-types.ts +++ b/src/utility-types.ts @@ -2,6 +2,7 @@ import type { AsyncResource } from 'node:async_hooks' import type { EventLoopUtilization } from 'node:perf_hooks' import type { MessagePort, TransferListItem } from 'node:worker_threads' +import type { WorkerChoiceStrategy } from './pools/selection-strategies/selection-strategies-types.js' import type { KillBehavior } from './worker/worker-options.js' /** @@ -64,6 +65,26 @@ export interface WorkerStatistics { readonly elu: boolean } +/** + * Task function properties. + * + * @internal + */ +export interface TaskFunctionProperties { + /** + * Task function name. + */ + name: string + /** + * Task function priority. Lower values have higher priority. + */ + priority?: number + /** + * Task function worker choice strategy. + */ + strategy?: WorkerChoiceStrategy +} + /** * Message object that is passed as a task between main worker and worker. * @@ -130,17 +151,17 @@ export interface MessageValue */ readonly taskFunctionOperationStatus?: boolean /** - * Task function serialized to string. + * Task function properties. */ - readonly taskFunction?: string + readonly taskFunctionProperties?: TaskFunctionProperties /** - * Task function name. + * Task function serialized to string. */ - readonly taskFunctionName?: string + readonly taskFunction?: string /** - * Task function names. + * Task functions properties. */ - readonly taskFunctionNames?: string[] + readonly taskFunctionsProperties?: TaskFunctionProperties[] /** * Whether the worker computes the given statistics or not. */ diff --git a/src/utils.ts b/src/utils.ts index c568a29a..7e1c8ef8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,8 @@ import { getRandomValues } from 'node:crypto' import * as os from 'node:os' +import type { TaskFunctionProperties } from './utility-types.js' +import type { TaskFunctionObject } from './worker/task-functions.js' import type { KillBehavior } from './worker/worker-options.js' /** @@ -220,3 +222,18 @@ export const once = ( return result } } + +export const buildTaskFunctionProperties = ( + name: string, + taskFunctionObject: TaskFunctionObject | undefined +): TaskFunctionProperties => { + return { + name, + ...(taskFunctionObject?.priority != null && { + priority: taskFunctionObject.priority + }), + ...(taskFunctionObject?.strategy != null && { + strategy: taskFunctionObject.strategy + }) + } +} diff --git a/src/worker/abstract-worker.ts b/src/worker/abstract-worker.ts index 12307f47..f328bcfb 100644 --- a/src/worker/abstract-worker.ts +++ b/src/worker/abstract-worker.ts @@ -5,10 +5,12 @@ import type { MessagePort } from 'node:worker_threads' import type { MessageValue, Task, + TaskFunctionProperties, TaskPerformance, WorkerStatistics } from '../utility-types.js' import { + buildTaskFunctionProperties, DEFAULT_TASK_NAME, EMPTY_FUNCTION, isAsyncFunction, @@ -17,6 +19,7 @@ import { import type { TaskAsyncFunction, TaskFunction, + TaskFunctionObject, TaskFunctionOperationResult, TaskFunctions, TaskSyncFunction @@ -62,9 +65,9 @@ export abstract class AbstractWorker< */ protected abstract id: number /** - * Task function(s) processed by the worker when the pool's `execution` function is invoked. + * Task function object(s) processed by the worker when the pool's `execution` function is invoked. */ - protected taskFunctions!: Map> + protected taskFunctions!: Map> /** * Timestamp of the last task processed by this worker. */ @@ -122,27 +125,33 @@ export abstract class AbstractWorker< if (taskFunctions == null) { throw new Error('taskFunctions parameter is mandatory') } - this.taskFunctions = new Map>() + this.taskFunctions = new Map>() if (typeof taskFunctions === 'function') { - const boundFn = taskFunctions.bind(this) - this.taskFunctions.set(DEFAULT_TASK_NAME, boundFn) + const fnObj = { taskFunction: taskFunctions.bind(this) } + this.taskFunctions.set(DEFAULT_TASK_NAME, fnObj) this.taskFunctions.set( typeof taskFunctions.name === 'string' && taskFunctions.name.trim().length > 0 ? taskFunctions.name : 'fn1', - boundFn + fnObj ) } else if (isPlainObject(taskFunctions)) { let firstEntry = true - for (const [name, fn] of Object.entries(taskFunctions)) { - checkValidTaskFunctionEntry(name, fn) - const boundFn = fn.bind(this) + for (let [name, fnObj] of Object.entries(taskFunctions)) { + if (typeof fnObj === 'function') { + fnObj = { taskFunction: fnObj } satisfies TaskFunctionObject< + Data, + Response + > + } + checkValidTaskFunctionEntry(name, fnObj) + fnObj.taskFunction = fnObj.taskFunction.bind(this) if (firstEntry) { - this.taskFunctions.set(DEFAULT_TASK_NAME, boundFn) + this.taskFunctions.set(DEFAULT_TASK_NAME, fnObj) firstEntry = false } - this.taskFunctions.set(name, boundFn) + this.taskFunctions.set(name, fnObj) } if (firstEntry) { throw new Error('taskFunctions parameter object is empty') @@ -179,7 +188,7 @@ export abstract class AbstractWorker< */ public addTaskFunction ( name: string, - fn: TaskFunction + fn: TaskFunction | TaskFunctionObject ): TaskFunctionOperationResult { try { checkTaskFunctionName(name) @@ -188,18 +197,19 @@ export abstract class AbstractWorker< 'Cannot add a task function with the default reserved name' ) } - if (typeof fn !== 'function') { - throw new TypeError('fn parameter is not a function') + if (typeof fn === 'function') { + fn = { taskFunction: fn } satisfies TaskFunctionObject } - const boundFn = fn.bind(this) + checkValidTaskFunctionEntry(name, fn) + fn.taskFunction = fn.taskFunction.bind(this) if ( this.taskFunctions.get(name) === this.taskFunctions.get(DEFAULT_TASK_NAME) ) { - this.taskFunctions.set(DEFAULT_TASK_NAME, boundFn) + this.taskFunctions.set(DEFAULT_TASK_NAME, fn) } - this.taskFunctions.set(name, boundFn) - this.sendTaskFunctionNamesToMainWorker() + this.taskFunctions.set(name, fn) + this.sendTaskFunctionsPropertiesToMainWorker() return { status: true } } catch (error) { return { status: false, error: error as Error } @@ -229,7 +239,7 @@ export abstract class AbstractWorker< ) } const deleteStatus = this.taskFunctions.delete(name) - this.sendTaskFunctionNamesToMainWorker() + this.sendTaskFunctionsPropertiesToMainWorker() return { status: deleteStatus } } catch (error) { return { status: false, error: error as Error } @@ -237,28 +247,38 @@ export abstract class AbstractWorker< } /** - * Lists the names of the worker's task functions. + * Lists the properties of the worker's task functions. * - * @returns The names of the worker's task functions. + * @returns The properties of the worker's task functions. */ - public listTaskFunctionNames (): string[] { - const names = [...this.taskFunctions.keys()] + public listTaskFunctionsProperties (): TaskFunctionProperties[] { let defaultTaskFunctionName = DEFAULT_TASK_NAME - for (const [name, fn] of this.taskFunctions) { + for (const [name, fnObj] of this.taskFunctions) { if ( name !== DEFAULT_TASK_NAME && - fn === this.taskFunctions.get(DEFAULT_TASK_NAME) + fnObj === this.taskFunctions.get(DEFAULT_TASK_NAME) ) { defaultTaskFunctionName = name break } } + const taskFunctionsProperties: TaskFunctionProperties[] = [] + for (const [name, fnObj] of this.taskFunctions) { + if (name === DEFAULT_TASK_NAME || name === defaultTaskFunctionName) { + continue + } + taskFunctionsProperties.push(buildTaskFunctionProperties(name, fnObj)) + } return [ - names[names.indexOf(DEFAULT_TASK_NAME)], - defaultTaskFunctionName, - ...names.filter( - name => name !== DEFAULT_TASK_NAME && name !== defaultTaskFunctionName - ) + buildTaskFunctionProperties( + DEFAULT_TASK_NAME, + this.taskFunctions.get(DEFAULT_TASK_NAME) + ), + buildTaskFunctionProperties( + defaultTaskFunctionName, + this.taskFunctions.get(defaultTaskFunctionName) + ), + ...taskFunctionsProperties ] } @@ -283,7 +303,7 @@ export abstract class AbstractWorker< } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.taskFunctions.set(DEFAULT_TASK_NAME, this.taskFunctions.get(name)!) - this.sendTaskFunctionNamesToMainWorker() + this.sendTaskFunctionsPropertiesToMainWorker() return { status: true } } catch (error) { return { status: false, error: error as Error } @@ -325,29 +345,34 @@ export abstract class AbstractWorker< protected handleTaskFunctionOperationMessage ( message: MessageValue ): void { - const { taskFunctionOperation, taskFunctionName, taskFunction } = message - if (taskFunctionName == null) { + const { taskFunctionOperation, taskFunctionProperties, taskFunction } = + message + if (taskFunctionProperties == null) { throw new Error( - 'Cannot handle task function operation message without a task function name' + 'Cannot handle task function operation message without task function properties' ) } let response: TaskFunctionOperationResult switch (taskFunctionOperation) { case 'add': - response = this.addTaskFunction( - taskFunctionName, + response = this.addTaskFunction(taskFunctionProperties.name, { // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func - new Function(`return ${taskFunction}`)() as TaskFunction< - Data, - Response - > - ) + taskFunction: new Function( + `return ${taskFunction}` + )() as TaskFunction, + ...(taskFunctionProperties.priority != null && { + priority: taskFunctionProperties.priority + }), + ...(taskFunctionProperties.strategy != null && { + strategy: taskFunctionProperties.strategy + }) + }) break case 'remove': - response = this.removeTaskFunction(taskFunctionName) + response = this.removeTaskFunction(taskFunctionProperties.name) break case 'default': - response = this.setDefaultTaskFunction(taskFunctionName) + response = this.setDefaultTaskFunction(taskFunctionProperties.name) break default: response = { status: false, error: new Error('Unknown task operation') } @@ -356,11 +381,11 @@ export abstract class AbstractWorker< this.sendToMainWorker({ taskFunctionOperation, taskFunctionOperationStatus: response.status, - taskFunctionName, + taskFunctionProperties, ...(!response.status && response.error != null && { workerError: { - name: taskFunctionName, + name: taskFunctionProperties.name, message: this.handleError(response.error as Error | string) } }) @@ -466,11 +491,11 @@ export abstract class AbstractWorker< ): void /** - * Sends task function names to the main worker. + * Sends task functions properties to the main worker. */ - protected sendTaskFunctionNamesToMainWorker (): void { + protected sendTaskFunctionsPropertiesToMainWorker (): void { this.sendToMainWorker({ - taskFunctionNames: this.listTaskFunctionNames() + taskFunctionsProperties: this.listTaskFunctionsProperties() }) } @@ -504,7 +529,7 @@ export abstract class AbstractWorker< }) return } - const fn = this.taskFunctions.get(taskFunctionName) + const fn = this.taskFunctions.get(taskFunctionName)?.taskFunction if (isAsyncFunction(fn)) { this.runAsync(fn as TaskAsyncFunction, task) } else { diff --git a/src/worker/cluster-worker.ts b/src/worker/cluster-worker.ts index 325b7a41..847f2174 100644 --- a/src/worker/cluster-worker.ts +++ b/src/worker/cluster-worker.ts @@ -43,12 +43,12 @@ export class ClusterWorker< this.getMainWorker().on('message', this.messageListener.bind(this)) this.sendToMainWorker({ ready: true, - taskFunctionNames: this.listTaskFunctionNames() + taskFunctionsProperties: this.listTaskFunctionsProperties() }) } catch { this.sendToMainWorker({ ready: false, - taskFunctionNames: this.listTaskFunctionNames() + taskFunctionsProperties: this.listTaskFunctionsProperties() }) } } diff --git a/src/worker/task-functions.ts b/src/worker/task-functions.ts index 5518043e..8999b69d 100644 --- a/src/worker/task-functions.ts +++ b/src/worker/task-functions.ts @@ -1,3 +1,5 @@ +import type { WorkerChoiceStrategy } from '../pools/selection-strategies/selection-strategies-types.js' + /** * Task synchronous function that can be executed. * @@ -36,18 +38,38 @@ export type TaskFunction = | TaskSyncFunction | TaskAsyncFunction +/** + * Task function object. + * + * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data. + * @typeParam Response - Type of execution response. This can only be structured-cloneable data. + */ +export interface TaskFunctionObject { + /** + * Task function. + */ + taskFunction: TaskFunction + /** + * Task function priority. Lower values have higher priority. + */ + priority?: number + /** + * Task function worker choice strategy. + */ + strategy?: WorkerChoiceStrategy +} + /** * Tasks functions that can be executed. - * This object can contain synchronous or asynchronous functions. - * The key is the name of the function. - * The value is the function itself. + * The key is the name of the task function or task function object. + * The value is the function or task function object. * * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data. * @typeParam Response - Type of execution response. This can only be structured-cloneable data. */ export type TaskFunctions = Record< string, -TaskFunction +TaskFunction | TaskFunctionObject > /** diff --git a/src/worker/thread-worker.ts b/src/worker/thread-worker.ts index 7115d715..bf8647c5 100644 --- a/src/worker/thread-worker.ts +++ b/src/worker/thread-worker.ts @@ -58,12 +58,12 @@ export class ThreadWorker< this.port.on('message', this.messageListener.bind(this)) this.sendToMainWorker({ ready: true, - taskFunctionNames: this.listTaskFunctionNames() + taskFunctionsProperties: this.listTaskFunctionsProperties() }) } catch { this.sendToMainWorker({ ready: false, - taskFunctionNames: this.listTaskFunctionNames() + taskFunctionsProperties: this.listTaskFunctionsProperties() }) } } diff --git a/src/worker/utils.ts b/src/worker/utils.ts index d8c4c3e9..486d92e7 100644 --- a/src/worker/utils.ts +++ b/src/worker/utils.ts @@ -1,5 +1,6 @@ +import { checkValidWorkerChoiceStrategy } from '../pools/utils.js' import { isPlainObject } from '../utils.js' -import type { TaskFunction } from './task-functions.js' +import type { TaskFunctionObject } from './task-functions.js' import { KillBehaviors, type WorkerOptions } from './worker-options.js' export const checkValidWorkerOptions = ( @@ -34,7 +35,7 @@ export const checkValidWorkerOptions = ( export const checkValidTaskFunctionEntry = ( name: string, - fn: TaskFunction + fnObj: TaskFunctionObject ): void => { if (typeof name !== 'string') { throw new TypeError('A taskFunctions parameter object key is not a string') @@ -44,11 +45,18 @@ export const checkValidTaskFunctionEntry = ( 'A taskFunctions parameter object key is an empty string' ) } - if (typeof fn !== 'function') { + if (typeof fnObj.taskFunction !== 'function') { throw new TypeError( - 'A taskFunctions parameter object value is not a function' + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + `taskFunction object 'taskFunction' property '${fnObj.taskFunction}' is not a function` ) } + if (fnObj.priority != null && !Number.isSafeInteger(fnObj.priority)) { + throw new TypeError( + `taskFunction object 'priority' property '${fnObj.priority}' is not an integer` + ) + } + checkValidWorkerChoiceStrategy(fnObj.strategy) } export const checkTaskFunctionName = (name: string): void => { diff --git a/tests/pools/abstract-pool.test.mjs b/tests/pools/abstract-pool.test.mjs index 7054c46a..50656e85 100644 --- a/tests/pools/abstract-pool.test.mjs +++ b/tests/pools/abstract-pool.test.mjs @@ -1363,14 +1363,14 @@ describe('Abstract pool test suite', () => { new TypeError('name argument must not be an empty string') ) await expect(dynamicThreadPool.addTaskFunction('test', 0)).rejects.toThrow( - new TypeError('fn argument must be a function') + new TypeError('taskFunction property must be a function') ) await expect(dynamicThreadPool.addTaskFunction('test', '')).rejects.toThrow( - new TypeError('fn argument must be a function') + new TypeError('taskFunction property must be a function') ) - expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'test' + expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'test' } ]) const echoTaskFunction = data => { return data @@ -1379,13 +1379,13 @@ describe('Abstract pool test suite', () => { dynamicThreadPool.addTaskFunction('echo', echoTaskFunction) ).resolves.toBe(true) expect(dynamicThreadPool.taskFunctions.size).toBe(1) - expect(dynamicThreadPool.taskFunctions.get('echo')).toStrictEqual( - echoTaskFunction - ) - expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'test', - 'echo' + expect(dynamicThreadPool.taskFunctions.get('echo')).toStrictEqual({ + taskFunction: echoTaskFunction + }) + expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'test' }, + { name: 'echo' } ]) const taskFunctionData = { test: 'test' } const echoResult = await dynamicThreadPool.execute(taskFunctionData, 'echo') @@ -1426,9 +1426,9 @@ describe('Abstract pool test suite', () => { './tests/worker-files/thread/testWorker.mjs' ) await waitPoolEvents(dynamicThreadPool, PoolEvents.ready, 1) - expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'test' + expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'test' } ]) await expect(dynamicThreadPool.removeTaskFunction('test')).rejects.toThrow( new Error('Cannot remove a task function not handled on the pool side') @@ -1438,22 +1438,22 @@ describe('Abstract pool test suite', () => { } await dynamicThreadPool.addTaskFunction('echo', echoTaskFunction) expect(dynamicThreadPool.taskFunctions.size).toBe(1) - expect(dynamicThreadPool.taskFunctions.get('echo')).toStrictEqual( - echoTaskFunction - ) - expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'test', - 'echo' + expect(dynamicThreadPool.taskFunctions.get('echo')).toStrictEqual({ + taskFunction: echoTaskFunction + }) + expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'test' }, + { name: 'echo' } ]) await expect(dynamicThreadPool.removeTaskFunction('echo')).resolves.toBe( true ) expect(dynamicThreadPool.taskFunctions.size).toBe(0) expect(dynamicThreadPool.taskFunctions.get('echo')).toBeUndefined() - expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'test' + expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'test' } ]) await dynamicThreadPool.destroy() }) @@ -1465,11 +1465,11 @@ describe('Abstract pool test suite', () => { './tests/worker-files/thread/testMultipleTaskFunctionsWorker.mjs' ) await waitPoolEvents(dynamicThreadPool, PoolEvents.ready, 1) - expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'jsonIntegerSerialization', - 'factorial', - 'fibonacci' + expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'jsonIntegerSerialization' }, + { name: 'factorial' }, + { name: 'fibonacci' } ]) await dynamicThreadPool.destroy() const fixedClusterPool = new FixedClusterPool( @@ -1477,11 +1477,11 @@ describe('Abstract pool test suite', () => { './tests/worker-files/cluster/testMultipleTaskFunctionsWorker.cjs' ) await waitPoolEvents(fixedClusterPool, PoolEvents.ready, 1) - expect(fixedClusterPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'jsonIntegerSerialization', - 'factorial', - 'fibonacci' + expect(fixedClusterPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'jsonIntegerSerialization' }, + { name: 'factorial' }, + { name: 'fibonacci' } ]) await fixedClusterPool.destroy() }) @@ -1513,29 +1513,29 @@ describe('Abstract pool test suite', () => { `Task function operation 'default' failed on worker ${workerId} with error: 'Error: Cannot set the default task function to a non-existing task function'` ) ) - expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'jsonIntegerSerialization', - 'factorial', - 'fibonacci' + expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'jsonIntegerSerialization' }, + { name: 'factorial' }, + { name: 'fibonacci' } ]) await expect( dynamicThreadPool.setDefaultTaskFunction('factorial') ).resolves.toBe(true) - expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'factorial', - 'jsonIntegerSerialization', - 'fibonacci' + expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'factorial' }, + { name: 'jsonIntegerSerialization' }, + { name: 'fibonacci' } ]) await expect( dynamicThreadPool.setDefaultTaskFunction('fibonacci') ).resolves.toBe(true) - expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'fibonacci', - 'jsonIntegerSerialization', - 'factorial' + expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'fibonacci' }, + { name: 'jsonIntegerSerialization' }, + { name: 'factorial' } ]) await dynamicThreadPool.destroy() }) @@ -1558,15 +1558,17 @@ describe('Abstract pool test suite', () => { expect(pool.info.executingTasks).toBe(0) expect(pool.info.executedTasks).toBe(4) for (const workerNode of pool.workerNodes) { - expect(workerNode.info.taskFunctionNames).toStrictEqual([ - DEFAULT_TASK_NAME, - 'jsonIntegerSerialization', - 'factorial', - 'fibonacci' + expect(workerNode.info.taskFunctionsProperties).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'jsonIntegerSerialization' }, + { name: 'factorial' }, + { name: 'fibonacci' } ]) expect(workerNode.taskFunctionsUsage.size).toBe(3) - for (const name of pool.listTaskFunctionNames()) { - expect(workerNode.getTaskFunctionWorkerUsage(name)).toStrictEqual({ + for (const taskFunctionProperties of pool.listTaskFunctionsProperties()) { + expect( + workerNode.getTaskFunctionWorkerUsage(taskFunctionProperties.name) + ).toStrictEqual({ tasks: { executed: expect.any(Number), executing: 0, @@ -1591,14 +1593,15 @@ describe('Abstract pool test suite', () => { } }) expect( - workerNode.getTaskFunctionWorkerUsage(name).tasks.executed + workerNode.getTaskFunctionWorkerUsage(taskFunctionProperties.name) + .tasks.executed ).toBeGreaterThan(0) } expect( workerNode.getTaskFunctionWorkerUsage(DEFAULT_TASK_NAME) ).toStrictEqual( workerNode.getTaskFunctionWorkerUsage( - workerNode.info.taskFunctionNames[1] + workerNode.info.taskFunctionsProperties[1].name ) ) } @@ -1628,13 +1631,17 @@ describe('Abstract pool test suite', () => { await expect( pool.sendTaskFunctionOperationToWorker(workerNodeKey, { taskFunctionOperation: 'add', - taskFunctionName: 'empty', + taskFunctionProperties: { name: 'empty' }, taskFunction: (() => {}).toString() }) ).resolves.toBe(true) expect( - pool.workerNodes[workerNodeKey].info.taskFunctionNames - ).toStrictEqual([DEFAULT_TASK_NAME, 'test', 'empty']) + pool.workerNodes[workerNodeKey].info.taskFunctionsProperties + ).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'test' }, + { name: 'empty' } + ]) await pool.destroy() }) @@ -1647,15 +1654,15 @@ describe('Abstract pool test suite', () => { await expect( pool.sendTaskFunctionOperationToWorkers({ taskFunctionOperation: 'add', - taskFunctionName: 'empty', + taskFunctionProperties: { name: 'empty' }, taskFunction: (() => {}).toString() }) ).resolves.toBe(true) for (const workerNode of pool.workerNodes) { - expect(workerNode.info.taskFunctionNames).toStrictEqual([ - DEFAULT_TASK_NAME, - 'test', - 'empty' + expect(workerNode.info.taskFunctionsProperties).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'test' }, + { name: 'empty' } ]) } await pool.destroy() diff --git a/tests/pools/worker-node.test.mjs b/tests/pools/worker-node.test.mjs index 5633530f..6aa889ba 100644 --- a/tests/pools/worker-node.test.mjs +++ b/tests/pools/worker-node.test.mjs @@ -214,18 +214,25 @@ describe('Worker node test suite', () => { threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction') ).toThrow( new TypeError( - "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list is not yet defined" + "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function properties list is not yet defined" ) ) - threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1'] + threadWorkerNode.info.taskFunctionsProperties = [ + { name: DEFAULT_TASK_NAME }, + { name: 'fn1' } + ] expect(() => threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction') ).toThrow( new TypeError( - "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list has less than 3 elements" + "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function properties list has less than 3 elements" ) ) - threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1', 'fn2'] + threadWorkerNode.info.taskFunctionsProperties = [ + { name: DEFAULT_TASK_NAME }, + { name: 'fn1' }, + { name: 'fn2' } + ] expect( threadWorkerNode.getTaskFunctionWorkerUsage(DEFAULT_TASK_NAME) ).toStrictEqual({ @@ -304,10 +311,10 @@ describe('Worker node test suite', () => { }) it('Worker node deleteTaskFunctionWorkerUsage()', () => { - expect(threadWorkerNode.info.taskFunctionNames).toStrictEqual([ - DEFAULT_TASK_NAME, - 'fn1', - 'fn2' + expect(threadWorkerNode.info.taskFunctionsProperties).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'fn1' }, + { name: 'fn2' } ]) expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2) expect( diff --git a/tests/worker/abstract-worker.test.mjs b/tests/worker/abstract-worker.test.mjs index 781f7790..cb91f696 100644 --- a/tests/worker/abstract-worker.test.mjs +++ b/tests/worker/abstract-worker.test.mjs @@ -132,8 +132,8 @@ describe('Abstract worker test suite', () => { it('Verify that taskFunctions parameter with unique function is taken', () => { const worker = new ThreadWorker(() => {}) - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) expect(worker.taskFunctions.size).toBe(2) expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( worker.taskFunctions.get('fn1') @@ -149,7 +149,9 @@ describe('Abstract worker test suite', () => { new TypeError('A taskFunctions parameter object key is an empty string') ) expect(() => new ThreadWorker({ fn1, fn2 })).toThrow( - new TypeError('A taskFunctions parameter object value is not a function') + new TypeError( + "taskFunction object 'taskFunction' property 'undefined' is not a function" + ) ) }) @@ -161,9 +163,9 @@ describe('Abstract worker test suite', () => { return 2 } const worker = new ClusterWorker({ fn1, fn2 }) - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Object) expect(worker.taskFunctions.size).toBe(3) expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( worker.taskFunctions.get('fn1') @@ -231,10 +233,12 @@ describe('Abstract worker test suite', () => { }) expect(worker.addTaskFunction('fn3', '')).toStrictEqual({ status: false, - error: new TypeError('fn parameter is not a function') + error: new TypeError( + "taskFunction object 'taskFunction' property 'undefined' is not a function" + ) }) - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) expect(worker.taskFunctions.size).toBe(2) expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( worker.taskFunctions.get('fn1') @@ -246,17 +250,17 @@ describe('Abstract worker test suite', () => { ) }) worker.addTaskFunction('fn2', fn2) - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Object) expect(worker.taskFunctions.size).toBe(3) expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( worker.taskFunctions.get('fn1') ) worker.addTaskFunction('fn1', fn1Replacement) - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Object) expect(worker.taskFunctions.size).toBe(3) expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( worker.taskFunctions.get('fn1') @@ -271,10 +275,10 @@ describe('Abstract worker test suite', () => { return 2 } const worker = new ClusterWorker({ fn1, fn2 }) - expect(worker.listTaskFunctionNames()).toStrictEqual([ - DEFAULT_TASK_NAME, - 'fn1', - 'fn2' + expect(worker.listTaskFunctionsProperties()).toStrictEqual([ + { name: DEFAULT_TASK_NAME }, + { name: 'fn1' }, + { name: 'fn2' } ]) }) @@ -294,9 +298,9 @@ describe('Abstract worker test suite', () => { status: false, error: new TypeError('name parameter is an empty string') }) - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Object) expect(worker.taskFunctions.size).toBe(3) expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( worker.taskFunctions.get('fn1') diff --git a/tests/worker/cluster-worker.test.mjs b/tests/worker/cluster-worker.test.mjs index 228a969a..9f4f8c06 100644 --- a/tests/worker/cluster-worker.test.mjs +++ b/tests/worker/cluster-worker.test.mjs @@ -52,9 +52,9 @@ describe('Cluster worker test suite', () => { status: false, error: new TypeError('name parameter is an empty string') }) - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Object) expect(worker.taskFunctions.size).toBe(3) expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( worker.taskFunctions.get('fn1') @@ -72,8 +72,8 @@ describe('Cluster worker test suite', () => { ) }) worker.removeTaskFunction('fn2') - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) expect(worker.taskFunctions.get('fn2')).toBeUndefined() expect(worker.taskFunctions.size).toBe(2) expect(worker.getMainWorker.calledTwice).toBe(true) diff --git a/tests/worker/thread-worker.test.mjs b/tests/worker/thread-worker.test.mjs index 6d694ec6..e1eaeb06 100644 --- a/tests/worker/thread-worker.test.mjs +++ b/tests/worker/thread-worker.test.mjs @@ -53,9 +53,9 @@ describe('Thread worker test suite', () => { status: false, error: new TypeError('name parameter is an empty string') }) - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Object) expect(worker.taskFunctions.size).toBe(3) expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( worker.taskFunctions.get('fn1') @@ -73,8 +73,8 @@ describe('Thread worker test suite', () => { ) }) worker.removeTaskFunction('fn2') - expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) - expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) + expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Object) + expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Object) expect(worker.taskFunctions.get('fn2')).toBeUndefined() expect(worker.taskFunctions.size).toBe(2) expect(worker.port.postMessage.calledOnce).toBe(true)