[Perf] API and Post Message Connector: Batch postMessage calls

f-u
This commit is contained in:
Alexander Zinchuk 2024-09-06 15:42:54 +02:00
parent 8b717b010f
commit 7b86865d67
8 changed files with 418 additions and 298 deletions

View File

@ -3,17 +3,17 @@ import type { TypedBroadcastChannel } from '../../../util/multitab';
import type { ApiInitialArgs, ApiOnProgress, OnApiUpdate } from '../../types'; import type { ApiInitialArgs, ApiOnProgress, OnApiUpdate } from '../../types';
import type { LocalDb } from '../localDb'; import type { LocalDb } from '../localDb';
import type { MethodArgs, MethodResponse, Methods } from '../methods/types'; import type { MethodArgs, MethodResponse, Methods } from '../methods/types';
import type { OriginRequest, ThenArg, WorkerMessageEvent } from './types'; import type { OriginPayload, ThenArg, WorkerMessageEvent } from './types';
import { DATA_BROADCAST_CHANNEL_NAME, DEBUG } from '../../../config'; import { DATA_BROADCAST_CHANNEL_NAME, DEBUG } from '../../../config';
import { logDebugMessage } from '../../../util/debugConsole'; import { logDebugMessage } from '../../../util/debugConsole';
import Deferred from '../../../util/Deferred'; import Deferred from '../../../util/Deferred';
import { getCurrentTabId, subscribeToMasterChange } from '../../../util/establishMultitabRole'; import { getCurrentTabId, subscribeToMasterChange } from '../../../util/establishMultitabRole';
import generateUniqueId from '../../../util/generateUniqueId'; import generateUniqueId from '../../../util/generateUniqueId';
import { pause } from '../../../util/schedulers'; import { pause, throttleWithTickEnd } from '../../../util/schedulers';
import { IS_MULTITAB_SUPPORTED } from '../../../util/windowEnvironment'; import { IS_MULTITAB_SUPPORTED } from '../../../util/windowEnvironment';
type RequestStates = { type RequestState = {
messageId: string; messageId: string;
resolve: Function; resolve: Function;
reject: Function; reject: Function;
@ -26,8 +26,12 @@ const HEALTH_CHECK_MIN_DELAY = 5 * 1000; // 5 sec
const NO_QUEUE_BEFORE_INIT = new Set(['destroy']); const NO_QUEUE_BEFORE_INIT = new Set(['destroy']);
let worker: Worker | undefined; let worker: Worker | undefined;
const requestStates = new Map<string, RequestStates>();
const requestStatesByCallback = new Map<AnyToVoidFunction, RequestStates>(); const requestStates = new Map<string, RequestState>();
const requestStatesByCallback = new Map<AnyToVoidFunction, RequestState>();
let pendingPayloads: OriginPayload[] = [];
const savedLocalDb: LocalDb = { const savedLocalDb: LocalDb = {
chats: {}, chats: {},
users: {}, users: {},
@ -48,6 +52,17 @@ const channel = IS_MULTITAB_SUPPORTED
? new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME) as TypedBroadcastChannel ? new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME) as TypedBroadcastChannel
: undefined; : undefined;
const postMessagesOnTickEnd = throttleWithTickEnd(() => {
const payloads = pendingPayloads;
pendingPayloads = [];
worker?.postMessage({ payloads });
});
function postMessageOnTickEnd(payload: OriginPayload) {
pendingPayloads.push(payload);
postMessagesOnTickEnd();
}
export function initApiOnMasterTab(initialArgs: ApiInitialArgs) { export function initApiOnMasterTab(initialArgs: ApiInitialArgs) {
if (!channel) return; if (!channel) return;
@ -250,7 +265,7 @@ export function cancelApiProgress(progressCallback: ApiOnProgress) {
} }
export function cancelApiProgressMaster(messageId: string) { export function cancelApiProgressMaster(messageId: string) {
worker?.postMessage({ postMessageOnTickEnd({
type: 'cancelProgress', type: 'cancelProgress',
messageId, messageId,
}); });
@ -258,35 +273,36 @@ export function cancelApiProgressMaster(messageId: string) {
function subscribeToWorker(onUpdate: OnApiUpdate) { function subscribeToWorker(onUpdate: OnApiUpdate) {
worker?.addEventListener('message', ({ data }: WorkerMessageEvent) => { worker?.addEventListener('message', ({ data }: WorkerMessageEvent) => {
if (!data) return; data?.payloads.forEach((payload) => {
if (data.type === 'updates') { if (payload.type === 'updates') {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
let DEBUG_startAt: number | undefined; let DEBUG_startAt: number | undefined;
if (DEBUG) { if (DEBUG) {
DEBUG_startAt = performance.now(); DEBUG_startAt = performance.now();
} }
data.updates.forEach(onUpdate); payload.updates.forEach(onUpdate);
if (DEBUG) { if (DEBUG) {
const duration = performance.now() - DEBUG_startAt!; const duration = performance.now() - DEBUG_startAt!;
if (duration > 5) { if (duration > 5) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn(`[API] Slow updates processing: ${data.updates.length} updates in ${duration} ms`); console.warn(`[API] Slow updates processing: ${payload.updates.length} updates in ${duration} ms`);
} }
} }
} else if (data.type === 'methodResponse') { } else if (payload.type === 'methodResponse') {
handleMethodResponse(data); handleMethodResponse(payload);
} else if (data.type === 'methodCallback') { } else if (payload.type === 'methodCallback') {
handleMethodCallback(data); handleMethodCallback(payload);
} else if (data.type === 'unhandledError') { } else if (payload.type === 'unhandledError') {
throw new Error(data.error?.message); throw new Error(payload.error?.message);
} else if (data.type === 'sendBeacon') { } else if (payload.type === 'sendBeacon') {
navigator.sendBeacon(data.url, data.data); navigator.sendBeacon(payload.url, payload.data);
} else if (data.type === 'debugLog') { } else if (payload.type === 'debugLog') {
logDebugMessage(data.level, ...data.args); logDebugMessage(payload.level, ...payload.args);
} }
}); });
});
} }
export function handleMethodResponse(data: { export function handleMethodResponse(data: {
@ -323,7 +339,7 @@ function makeRequestToMaster(message: {
...message, ...message,
}; };
const requestState = { messageId } as RequestStates; const requestState = { messageId } as RequestState;
// Re-wrap type because of `postMessage` // Re-wrap type because of `postMessage`
const promise: Promise<MethodResponse<keyof Methods>> = new Promise((resolve, reject) => { const promise: Promise<MethodResponse<keyof Methods>> = new Promise((resolve, reject) => {
@ -355,14 +371,14 @@ function makeRequestToMaster(message: {
return promise; return promise;
} }
function makeRequest(message: OriginRequest) { function makeRequest(message: OriginPayload) {
const messageId = generateUniqueId(); const messageId = generateUniqueId();
const payload: OriginRequest = { const payload: OriginPayload = {
messageId, messageId,
...message, ...message,
}; };
const requestState = { messageId } as RequestStates; const requestState = { messageId } as RequestState;
// Re-wrap type because of `postMessage` // Re-wrap type because of `postMessage`
const promise: Promise<MethodResponse<keyof Methods>> = new Promise((resolve, reject) => { const promise: Promise<MethodResponse<keyof Methods>> = new Promise((resolve, reject) => {
@ -391,7 +407,7 @@ function makeRequest(message: OriginRequest) {
} }
}); });
worker?.postMessage(payload); postMessageOnTickEnd(payload);
return promise; return promise;
} }

View File

@ -5,59 +5,85 @@ import type { MethodArgs, MethodResponse, Methods } from '../methods/types';
export type ThenArg<T> = T extends Promise<infer U> ? U : T; export type ThenArg<T> = T extends Promise<infer U> ? U : T;
export type WorkerMessageData = { export type WorkerPayload =
{
type: 'updates'; type: 'updates';
updates: ApiUpdate[]; updates: ApiUpdate[];
} | { }
|
{
type: 'methodResponse'; type: 'methodResponse';
messageId: string; messageId: string;
response?: ThenArg<MethodResponse<keyof Methods>>; response?: ThenArg<MethodResponse<keyof Methods>>;
error?: { message: string }; error?: { message: string };
} | { }
|
{
type: 'methodCallback'; type: 'methodCallback';
messageId: string; messageId: string;
callbackArgs: any[]; callbackArgs: any[];
} | { }
|
{
type: 'unhandledError'; type: 'unhandledError';
error?: { message: string }; error?: { message: string };
} | { }
|
{
type: 'sendBeacon'; type: 'sendBeacon';
url: string; url: string;
data: ArrayBuffer; data: ArrayBuffer;
} | { }
|
{
type: 'debugLog'; type: 'debugLog';
level: DebugLevel; level: DebugLevel;
args: any[]; args: any[];
};
export type WorkerMessageData = {
payloads: WorkerPayload[];
}; };
export interface WorkerMessageEvent { export type WorkerMessageEvent = {
data: WorkerMessageData; data: WorkerMessageData;
} };
export type OriginRequest = { export type OriginPayload =
{
type: 'initApi'; type: 'initApi';
messageId?: string; messageId?: string;
args: [ApiInitialArgs, LocalDb]; args: [ApiInitialArgs, LocalDb];
} | { }
|
{
type: 'callMethod'; type: 'callMethod';
messageId?: string; messageId?: string;
name: keyof Methods; name: keyof Methods;
args: MethodArgs<keyof Methods>; args: MethodArgs<keyof Methods>;
withCallback?: boolean; withCallback?: boolean;
} | { }
|
{
type: 'ping'; type: 'ping';
messageId?: string; messageId?: string;
} | { }
|
{
type: 'toggleDebugMode'; type: 'toggleDebugMode';
messageId?: string; messageId?: string;
isEnabled?: boolean; isEnabled?: boolean;
}; }
|
export type OriginMessageData = OriginRequest | { {
type: 'cancelProgress'; type: 'cancelProgress';
messageId: string; messageId: string;
};
export type OriginMessageData = {
payloads: OriginPayload[];
}; };
export interface OriginMessageEvent { export type OriginMessageEvent = {
data: OriginMessageData; data: OriginMessageData;
} };

View File

@ -2,7 +2,7 @@
import type { DebugLevel } from '../../../util/debugConsole'; import type { DebugLevel } from '../../../util/debugConsole';
import type { ApiOnProgress, ApiUpdate } from '../../types'; import type { ApiOnProgress, ApiUpdate } from '../../types';
import type { OriginMessageEvent, WorkerMessageData } from './types'; import type { OriginMessageEvent, WorkerPayload } from './types';
import { DEBUG } from '../../../config'; import { DEBUG } from '../../../config';
import { DEBUG_LEVELS } from '../../../util/debugConsole'; import { DEBUG_LEVELS } from '../../../util/debugConsole';
@ -39,18 +39,20 @@ function disableDebugLog() {
handleErrors(); handleErrors();
let pendingPayloads: WorkerPayload[] = [];
let pendingTransferables: Transferable[] = [];
const callbackState = new Map<string, ApiOnProgress>(); const callbackState = new Map<string, ApiOnProgress>();
if (DEBUG) { if (DEBUG) {
console.log('>>> FINISH LOAD WORKER'); console.log('>>> FINISH LOAD WORKER');
} }
onmessage = async (message: OriginMessageEvent) => { onmessage = ({ data }: OriginMessageEvent) => {
const { data } = message; data.payloads.forEach(async (payload) => {
switch (payload.type) {
switch (data.type) {
case 'initApi': { case 'initApi': {
const { messageId, args } = data; const { messageId, args } = payload;
await initApi(onUpdate, args[0], args[1]); await initApi(onUpdate, args[0], args[1]);
if (messageId) { if (messageId) {
sendToOrigin({ sendToOrigin({
@ -64,7 +66,7 @@ onmessage = async (message: OriginMessageEvent) => {
case 'callMethod': { case 'callMethod': {
const { const {
messageId, name, args, withCallback, messageId, name, args, withCallback,
} = data; } = payload;
try { try {
if (messageId && withCallback) { if (messageId && withCallback) {
const callback = (...callbackArgs: any[]) => { const callback = (...callbackArgs: any[]) => {
@ -118,7 +120,7 @@ onmessage = async (message: OriginMessageEvent) => {
break; break;
} }
case 'cancelProgress': { case 'cancelProgress': {
const callback = callbackState.get(data.messageId); const callback = callbackState.get(payload.messageId);
if (callback) { if (callback) {
cancelApiProgress(callback); cancelApiProgress(callback);
} }
@ -128,19 +130,20 @@ onmessage = async (message: OriginMessageEvent) => {
case 'ping': { case 'ping': {
sendToOrigin({ sendToOrigin({
type: 'methodResponse', type: 'methodResponse',
messageId: data.messageId!, messageId: payload.messageId!,
}); });
break; break;
} }
case 'toggleDebugMode': { case 'toggleDebugMode': {
if (data.isEnabled) { if (payload.isEnabled) {
enableDebugLog(); enableDebugLog();
} else { } else {
disableDebugLog(); disableDebugLog();
} }
} }
} }
});
}; };
function handleErrors() { function handleErrors() {
@ -176,10 +179,26 @@ function onUpdate(update: ApiUpdate) {
sendUpdatesOnTickEnd(); sendUpdatesOnTickEnd();
} }
function sendToOrigin(data: WorkerMessageData, arrayBuffer?: ArrayBuffer) { const sendToOriginOnTickEnd = throttleWithTickEnd(() => {
if (arrayBuffer) { const data = { payloads: pendingPayloads };
postMessage(data, [arrayBuffer]); const transferables = pendingTransferables;
pendingPayloads = [];
pendingTransferables = [];
if (transferables.length) {
postMessage(data, transferables);
} else { } else {
postMessage(data); postMessage(data);
} }
});
function sendToOrigin(payload: WorkerPayload, transferable?: Transferable) {
pendingPayloads.push(payload);
if (transferable) {
pendingTransferables.push(transferable);
}
sendToOriginOnTickEnd();
} }

View File

@ -173,6 +173,6 @@ const api = {
'rlottie:destroy': destroy, 'rlottie:destroy': destroy,
}; };
createWorkerInterface(api); createWorkerInterface(api, 'media');
export type RLottieApi = typeof api; export type RLottieApi = typeof api;

View File

@ -81,6 +81,6 @@ const api = {
'video-preview:destroy': destroy, 'video-preview:destroy': destroy,
}; };
createWorkerInterface(api); createWorkerInterface(api, 'media');
export type VideoPreviewApi = typeof api; export type VideoPreviewApi = typeof api;

View File

@ -1,4 +1,5 @@
import generateUniqueId from './generateUniqueId'; import generateUniqueId from './generateUniqueId';
import { throttleWithTickEnd } from './schedulers';
export interface CancellableCallback { export interface CancellableCallback {
( (
@ -8,16 +9,13 @@ export interface CancellableCallback {
isCanceled?: boolean; isCanceled?: boolean;
} }
type InitData = { type InitPayload = {
channel?: string;
type: 'init'; type: 'init';
messageId?: string; messageId?: string;
name: 'init';
args: any; args: any;
}; };
type CallMethodData = { type CallMethodPayload = {
channel?: string;
type: 'callMethod'; type: 'callMethod';
messageId?: string; messageId?: string;
name: string; name: string;
@ -25,12 +23,21 @@ type CallMethodData = {
withCallback?: boolean; withCallback?: boolean;
}; };
export type OriginMessageData = InitData | CallMethodData | { type CancelProgressPayload = {
channel?: string;
type: 'cancelProgress'; type: 'cancelProgress';
messageId: string; messageId: string;
}; };
export type OriginPayload =
InitPayload
| CallMethodPayload
| CancelProgressPayload;
export type OriginMessageData = {
channel?: string;
payloads: OriginPayload[];
};
export interface OriginMessageEvent { export interface OriginMessageEvent {
data: OriginMessageData; data: OriginMessageData;
} }
@ -39,32 +46,44 @@ export type ApiUpdate =
{ type: string } { type: string }
& any; & any;
export type WorkerMessageData = { export type WorkerPayload =
{
channel?: string; channel?: string;
type: 'update'; type: 'update';
update: ApiUpdate; update: ApiUpdate;
} | { }
|
{
channel?: string; channel?: string;
type: 'methodResponse'; type: 'methodResponse';
messageId: string; messageId: string;
response?: any; response?: any;
error?: { message: string }; error?: { message: string };
} | { }
|
{
channel?: string; channel?: string;
type: 'methodCallback'; type: 'methodCallback';
messageId: string; messageId: string;
callbackArgs: any[]; callbackArgs: any[];
} | { }
|
{
channel?: string; channel?: string;
type: 'unhandledError'; type: 'unhandledError';
error?: { message: string }; error?: { message: string };
};
export type WorkerMessageData = {
channel?: string;
payloads: WorkerPayload[];
}; };
export interface WorkerMessageEvent { export interface WorkerMessageEvent {
data: WorkerMessageData; data: WorkerMessageData;
} }
interface RequestStates { interface RequestState {
messageId: string; messageId: string;
resolve: Function; resolve: Function;
reject: Function; reject: Function;
@ -82,9 +101,11 @@ export type RequestTypes<T extends InputRequestTypes> = Values<{
}>; }>;
class ConnectorClass<T extends InputRequestTypes> { class ConnectorClass<T extends InputRequestTypes> {
private requestStates = new Map<string, RequestStates>(); private requestStates = new Map<string, RequestState>();
private requestStatesByCallback = new Map<AnyToVoidFunction, RequestStates>(); private requestStatesByCallback = new Map<AnyToVoidFunction, RequestState>();
private pendingPayloads: OriginPayload[] = [];
constructor( constructor(
public target: Worker, public target: Worker,
@ -98,7 +119,7 @@ class ConnectorClass<T extends InputRequestTypes> {
} }
init(...args: any[]) { init(...args: any[]) {
this.postMessage({ this.postMessageOnTickEnd({
type: 'init', type: 'init',
args, args,
}); });
@ -108,13 +129,13 @@ class ConnectorClass<T extends InputRequestTypes> {
const { requestStates, requestStatesByCallback } = this; const { requestStates, requestStatesByCallback } = this;
const messageId = generateUniqueId(); const messageId = generateUniqueId();
const payload: CallMethodData = { const payload: CallMethodPayload = {
type: 'callMethod', type: 'callMethod',
messageId, messageId,
...messageData, ...messageData,
}; };
const requestState = { messageId } as RequestStates; const requestState = { messageId } as RequestState;
// Re-wrap type because of `postMessage` // Re-wrap type because of `postMessage`
const promise: Promise<any> = new Promise((resolve, reject) => { const promise: Promise<any> = new Promise((resolve, reject) => {
@ -140,7 +161,7 @@ class ConnectorClass<T extends InputRequestTypes> {
} }
}); });
this.postMessage(payload); this.postMessageOnTickEnd(payload);
return promise; return promise;
} }
@ -153,7 +174,7 @@ class ConnectorClass<T extends InputRequestTypes> {
return; return;
} }
this.postMessage({ this.postMessageOnTickEnd({
type: 'cancelProgress', type: 'cancelProgress',
messageId, messageId,
}); });
@ -165,31 +186,43 @@ class ConnectorClass<T extends InputRequestTypes> {
return; return;
} }
if (data.type === 'update' && this.onUpdate) { data.payloads.forEach((payload) => {
this.onUpdate(data.update); if (payload.type === 'update' && this.onUpdate) {
this.onUpdate(payload.update);
} }
if (data.type === 'methodResponse') { if (payload.type === 'methodResponse') {
const requestState = requestStates.get(data.messageId); const requestState = requestStates.get(payload.messageId);
if (requestState) { if (requestState) {
if (data.error) { if (payload.error) {
requestState.reject(data.error); requestState.reject(payload.error);
} else { } else {
requestState.resolve(data.response); requestState.resolve(payload.response);
} }
} }
} else if (data.type === 'methodCallback') { } else if (payload.type === 'methodCallback') {
const requestState = requestStates.get(data.messageId); const requestState = requestStates.get(payload.messageId);
requestState?.callback?.(...data.callbackArgs); requestState?.callback?.(...payload.callbackArgs);
} else if (data.type === 'unhandledError') { } else if (payload.type === 'unhandledError') {
throw new Error(data.error?.message); throw new Error(payload.error?.message);
} }
});
} }
private postMessage(data: AnyLiteral) { private postMessageOnTickEnd(payload: OriginPayload) {
data.channel = this.channel; this.pendingPayloads.push(payload);
this.postMessagesOnTickEnd();
this.target.postMessage(data);
} }
private postMessagesOnTickEnd = throttleWithTickEnd(() => {
const payloads = this.pendingPayloads;
this.pendingPayloads = [];
this.target.postMessage({
channel: this.channel,
payloads,
});
});
} }
export function createConnector<T extends InputRequestTypes>( export function createConnector<T extends InputRequestTypes>(

View File

@ -1,10 +1,14 @@
import type { import type {
ApiUpdate, ApiUpdate,
CancellableCallback, OriginMessageData, OriginMessageEvent, WorkerMessageData, CancellableCallback,
OriginMessageData,
OriginMessageEvent,
WorkerPayload,
} from './PostMessageConnector'; } from './PostMessageConnector';
import { DEBUG } from '../config'; import { DEBUG } from '../config';
import { createCallbackManager } from './callbacks'; import { createCallbackManager } from './callbacks';
import { throttleWithTickEnd } from './schedulers';
declare const self: WorkerGlobalScope; declare const self: WorkerGlobalScope;
@ -13,20 +17,37 @@ const callbackState = new Map<string, CancellableCallback>();
type ApiConfig = type ApiConfig =
((name: string, ...args: any[]) => any | [any, ArrayBuffer[]]) ((name: string, ...args: any[]) => any | [any, ArrayBuffer[]])
| Record<string, Function>; | Record<string, Function>;
type SendToOrigin = (data: WorkerMessageData, transferables?: Transferable[]) => void; type SendToOrigin = (data: WorkerPayload, transferables?: Transferable[]) => void;
const messageHandlers = createCallbackManager(); const messageHandlers = createCallbackManager();
onmessage = messageHandlers.runCallbacks; onmessage = messageHandlers.runCallbacks;
export function createWorkerInterface(api: ApiConfig, channel?: string) { export function createWorkerInterface(api: ApiConfig, channel?: string) {
function sendToOrigin(data: WorkerMessageData, transferables?: Transferable[]) { let pendingPayloads: WorkerPayload[] = [];
data.channel = channel; let pendingTransferables: Transferable[] = [];
if (transferables) { const sendToOriginOnTickEnd = throttleWithTickEnd(() => {
const data = { channel, payloads: pendingPayloads };
const transferables = pendingTransferables;
pendingPayloads = [];
pendingTransferables = [];
if (transferables.length) {
postMessage(data, transferables); postMessage(data, transferables);
} else { } else {
postMessage(data); postMessage(data);
} }
});
function sendToOrigin(payload: WorkerPayload, transferables?: Transferable[]) {
pendingPayloads.push(payload);
if (transferables) {
pendingTransferables.push(...transferables);
}
sendToOriginOnTickEnd();
} }
handleErrors(sendToOrigin); handleErrors(sendToOrigin);
@ -38,7 +59,7 @@ export function createWorkerInterface(api: ApiConfig, channel?: string) {
}); });
} }
async function onMessage( function onMessage(
api: ApiConfig, api: ApiConfig,
data: OriginMessageData, data: OriginMessageData,
sendToOrigin: SendToOrigin, sendToOrigin: SendToOrigin,
@ -53,20 +74,23 @@ async function onMessage(
}; };
} }
switch (data.type) { data.payloads.forEach(async (payload) => {
switch (payload.type) {
case 'init': { case 'init': {
const { args } = data; const { args } = payload;
const promise = typeof api === 'function' if (typeof api === 'function') {
? api('init', onUpdate, ...args) await api('init', onUpdate, ...args);
: api.init?.(onUpdate, ...args); } else {
await promise; await api.init?.(onUpdate, ...args);
}
break; break;
} }
case 'callMethod': { case 'callMethod': {
const { const {
messageId, name, args, withCallback, messageId, name, args, withCallback,
} = data; } = payload;
try { try {
if (typeof api !== 'function' && !api[name]) return; if (typeof api !== 'function' && !api[name]) return;
@ -122,8 +146,9 @@ async function onMessage(
break; break;
} }
case 'cancelProgress': { case 'cancelProgress': {
const callback = callbackState.get(data.messageId); const callback = callbackState.get(payload.messageId);
if (callback) { if (callback) {
callback.isCanceled = true; callback.isCanceled = true;
} }
@ -131,6 +156,7 @@ async function onMessage(
break; break;
} }
} }
});
} }
function isTransferable(obj: any) { function isTransferable(obj: any) {

View File

@ -18,7 +18,7 @@ export default function launchMediaWorkers() {
instances = new Array(MAX_WORKERS).fill(undefined).map( instances = new Array(MAX_WORKERS).fill(undefined).map(
() => { () => {
const worker = new Worker(new URL('../lib/mediaWorker/index.worker.ts', import.meta.url)); const worker = new Worker(new URL('../lib/mediaWorker/index.worker.ts', import.meta.url));
const connector = createConnector<MediaWorkerApi>(worker); const connector = createConnector<MediaWorkerApi>(worker, undefined, 'media');
return { worker, connector }; return { worker, connector };
}, },
); );