Skip to content

Справочник API

defineWorkerHandler()

Сторона воркера. Автоматически подключает протокол сообщений run/cancel — вам нужно написать только саму функцию-обработчик.

ts
import { defineWorkerHandler, type WorkerContext } from 'vue-worker-kit/worker'

export default defineWorkerHandler(async (input: In, ctx: WorkerContext): Promise<Out> => {
  // ...
})

ctx: WorkerContext:

ПолеТипОписание
signalAbortSignalСрабатывает, когда задача отменена с основного потока — проверка опциональна, отмена кооперативная
reportProgress(value)(0..1) => voidОтправляет прогресс на основной поток, ограничено ~20 сообщениями/сек
transfer(...transferables)(...Transferable[]) => voidПомечает объекты для отправки обратно с результатом без копирования (zero-copy) вместо структурного клонирования — см. Передача владения
reportChunk(chunk)(chunk: unknown) => voidОтправляет промежуточный результат без ограничения частоты — см. Потоковая передача результатов

defineWorkerHandler запускает цикл обработки сообщений только тогда, когда действительно выполняется в глобальной области выделенного или общего воркера (проверяется через self instanceof DedicatedWorkerGlobalScope/SharedWorkerGlobalScope). Импорт этого файла где-либо ещё — например, случайно из основного бандла — ничего не делает. Один и тот же файл работает и с new Worker(...) (через useWorker()/createWorkerPool()), и с new SharedWorker(...) (через useSharedWorker()) — см. useSharedWorker().

Вызывать reportProgress(1) самостоятельно перед возвратом не нужно — финальное, не ограниченное по частоте обновление прогресса до 1 всегда отправляется прямо перед результатом, независимо от того, каким был последний ограниченный вызов. Без этого обработчик, который сообщает прогресс только в контрольных точках (например, каждые 5%), мог бы навсегда оставить progress основного потока застрявшим ниже 1, поскольку контрольная точка, ближайшая к концу, может попасть в окно ограничения предыдущего вызова и незаметно потеряться.

useWorker()

Composable основного потока, оборачивает один лениво создаваемый воркер.

ts
const { run, isRunning, progress, error, cancel, warmup } = useWorker<typeof import('./x.worker')>(
  () => new Worker(new URL('./x.worker.ts', import.meta.url), { type: 'module' }),
  { idleTimeout: 30_000, retries: 0 },
)

// Опционально: заранее создать воркер, не выполняя задачу (избегает задержки холодного старта при первом запуске)
await warmup()

const output = await run(input, { transfer: [input.buffer], signal: controller.signal })

Опции:

ОпцияТипПо умолчаниюОписание
idleTimeoutnumber | false30000Воркер самозавершается после стольки мс простоя (освобождает память); следующий run() прозрачно пересоздаёт его
retriesnumber0Автоматические повторы при отклонении — никогда не применяются к отменам (AbortError всегда отклоняется немедленно)
retryDelay(attempt: number) => numberЗадержка перед каждым повтором — см. Стратегия повторов с backoff
hardCancelOnAbortbooleanfalseПри abort() немедленно завершить и пересоздать воркер вместо ожидания кооперативной обработки через ctx.signal
cache{ cache: 'lru', maxCacheSize?: number }Мемоизирует результаты по входным данным — см. Мемоизация / кэш результатов
streamingbooleanfalseВключает ctx.reportChunk()/chunks — см. Потоковая передача результатов

Возвращает:

  • run(input, options?) => Promise<Output>options: { transfer?: Transferable[], signal?: AbortSignal }
  • isRunning: ComputedRef<boolean>, progress: ShallowRef<number>, error: ShallowRef<WorkerError | null>
  • cancel() — отменяет текущий вызов(ы) run(), не получившие собственный signal
  • warmup(): Promise<void> — заранее создаёт воркер без выполнения задачи (полезно, чтобы избежать задержки холодного старта)
  • chunks?: ShallowRef<unknown[]> — присутствует только при streaming: true (см. Потоковая передача результатов)
  • автоматический terminate() при onScopeDispose, если вызвано внутри активной effect-области

Вход run() пропускается через toRaw() перед отправкой — значение ref/reactive, прочитанное прямо из компонента (() => list.value), не клонируется структурно как живой Proxy, поэтому отправляется именно сырой снапшот.

createWorkerPool() / useWorkerPool()

Для множества мелких независимых задач (изменение размера сотен изображений и т. п.) — vue-worker-kit/pool.

ts
import { createWorkerPool } from 'vue-worker-kit/pool'

const pool = createWorkerPool<typeof import('./resize.worker')>(
  () => new Worker(new URL('./resize.worker.ts', import.meta.url), { type: 'module' }),
)

// Заранее создать все воркеры до размера пула (опционально, избегает задержки холодного старта на первых задачах)
await pool.warmup()

// Обработка массива с передачей по элементу и глобальным сигналом отмены
const thumbnails = await pool.map(files, {
  concurrency: 4,
  transfer: (file) => [file.buffer], // zero-copy для каждого элемента
  signal: abortController.signal, // отменить все выполняющиеся задачи
})

const one = await pool.run(files[0])
  • pool.run(input, options?) — ставит задачу в очередь первому свободному воркеру, options: { transfer?: Transferable[], signal?: AbortSignal }
  • pool.map(items, options?) — обрабатывает массив с ограниченным параллелизмом, результаты в порядке входных данных. Опции:
    • concurrency?: number — максимум параллельных задач (по умолчанию: pool.size)
    • transfer?: (item: T) => Transferable[] — функция передачи zero-copy для каждого элемента
    • signal?: AbortSignal — глобальный сигнал отмены для всех элементов
  • pool.stats: ComputedRef<{ busy: number; idle: number; queued: number }> — реактивно, используется панелью devtools
  • pool.terminate() — завершает весь пул
  • pool.warmup(): Promise<void> — заранее создаёт все воркеры до size, не выполняя задачи
  • воркеры создаются лениво, до size, по мере поступления задач — не все сразу
  • size (опция) по умолчанию равен navigator.hardwareConcurrency — собственному числу логических ядер/потоков браузера на машине, где реально работает ваше приложение, а не числу, выбранному на этапе разработки. Передайте size явно, чтобы переопределить его (например, ограничить, или если navigator сообщает то, чему вы не доверяете — некоторые браузеры с усиленной приватностью ограничивают или округляют это значение). Откатывается к 4 там, где navigator не существует (SSR).
  • useWorkerPool() — тот же API с автоматическим завершением через onScopeDispose для использования прямо в setup()

useWorkerComputed()

vue-worker-kit/computed. computed(), который пересчитывается внутри воркера при каждом изменении своего реактивного источника, с автоматическим отбрасыванием устаревших запусков.

ts
import { useWorkerComputed } from 'vue-worker-kit/computed'

const sorted = useWorkerComputed<typeof import('./heavy-sort.worker')>(
  () => new Worker(new URL('./heavy-sort.worker.ts', import.meta.url), { type: 'module' }),
  () => list.value, // отслеживается как источник watchEffect
  { debounce: 150 },
)

// sorted.value — undefined до первого результата, затем последний АКТУАЛЬНЫЙ результат
// sorted.isRunning, sorted.error

Обработка гонок: каждый запуск получает внутренний номер поколения. Если источник меняется снова до прихода результата запуска, этот результат просто отбрасывается по прибытии (никогда не откатывает sorted.value к устаревшему значению), а ctx.signal вытесненного запуска отменяется (кооперативно — обработчик сам решает, проверять его или нет). debounce (мс) предотвращает запуск воркера на каждый реактивный тик (например, на каждое нажатие клавиши).

useSharedWorker()

vue-worker-kit/shared — переиспользует один SharedWorker для всех вкладок/окон одного источника, которые к нему подключаются, вместо одного воркера на вкладку.

ts
import { useSharedWorker } from 'vue-worker-kit/shared'

const { run, connect, disconnect, portCount } = useSharedWorker<typeof import('./shared.worker')>(
  () => new SharedWorker(new URL('./shared.worker.ts', import.meta.url), { type: 'module' }),
)

// Опционально — run() подключается лениво сам; вызовите это, чтобы подключиться заранее.
connect()

const result = await run(data)

// Закрывает порт этой вкладки. НЕ завершает воркер — остальные вкладки остаются подключены.
disconnect()
  • connect(): void — устанавливает соединение этой вкладки (идемпотентно; run() тоже вызывает его лениво, если вы пропустили этот шаг)
  • disconnect(): void — закрывает только порт этой вкладки; общий воркер продолжает работать для всех остальных подключённых вкладок. Вызывается автоматически при onScopeDispose, если используется внутри setup().
  • portCount: Ref<number> — количество вкладок, чьё подключение видел воркер, по последней рассылке от самого воркера. Best-effort: у MessagePort нет уведомления уровня платформы о том, что «другая сторона пропала», поэтому значение уменьшается только по кооперативному вызову disconnect() — упавшая или принудительно закрытая вкладка никогда не вычитается.
  • run(input, options?), isRunning, progress, error, cancel() — та же семантика, что и у useWorker()
  • Опции: retries, retryDelay, cache, streaming — как у useWorker(). Нет idleTimeout/hardCancelOnAbort: время жизни общего воркера не принадлежит какой-то одной вкладке, поэтому connect()/disconnect() — это весь жизненный цикл целиком, а не таймер простоя.
  • Поддержка браузеров: Chrome, Firefox, Edge Desktop. ❌ Не поддерживается в Safari iOS или Chrome Android — там конструктор SharedWorker вообще не существует. connect()/run() в этом случае бросают WorkerUnavailableError, так же как useWorker() при SSR.

Файл на стороне воркера — обычный модуль defineWorkerHandler(): тот же самый файл работает и с new Worker(...) (через useWorker()), и с new SharedWorker(...) (через useSharedWorker()); ему не нужно знать, под каким из них он выполняется.

ts
// shared.worker.ts
import { defineWorkerHandler } from 'vue-worker-kit/worker'

export default defineWorkerHandler(async (data: In, ctx) => {
  return processData(data)
})

Devtools

vue-worker-kit/devtools — самостоятельная отладочная панель, без зависимости от @vue/devtools-api (сохраняет пакет свободным от зависимостей).

ts
import { createWorkerActivityMonitor, WorkerActivityPanel } from 'vue-worker-kit/devtools'

const monitor = createWorkerActivityMonitor(pool) // или один экземпляр useWorker()/useSharedWorker()
vue
<WorkerActivityPanel :monitor="monitor" />

Показывает количество занятых/свободных воркеров, длину очереди, среднее время задачи и последние N ошибок — реактивно, на основе внутренней подписки (без опроса).