Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(useWebWorkerFn): support local function dependencies #3899

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/core/useWebWorkerFn/index.md
Expand Up @@ -34,6 +34,22 @@ const { workerFn, workerStatus, workerTerminate } = useWebWorkerFn(
)
```

### With local dependencies

```ts {9-9}
import { useWebWorkerFn } from '@vueuse/core'

const pow = (a: number) => a * a

const { workerFn, workerStatus, workerTerminate } = useWebWorkerFn(
numbers => pow(numbers),
{
timeout: 50000,
localDependencies: [pow]
},
)
```

## Web Worker

Before you start using this function, we suggest you read the [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) documentation.
Expand Down
11 changes: 8 additions & 3 deletions packages/core/useWebWorkerFn/index.ts
Expand Up @@ -24,6 +24,10 @@ export interface UseWebWorkerOptions extends ConfigurableWindow {
* An array that contains the external dependencies needed to run the worker
*/
dependencies?: string[]
/**
* An array that contains the local dependencies needed to run the worker
*/
localDependencies?: Function[]
}

/**
Expand All @@ -36,6 +40,7 @@ export interface UseWebWorkerOptions extends ConfigurableWindow {
export function useWebWorkerFn<T extends (...fnArgs: any[]) => any>(fn: T, options: UseWebWorkerOptions = {}) {
const {
dependencies = [],
localDependencies = [],
timeout,
window = defaultWindow,
} = options
Expand All @@ -61,12 +66,12 @@ export function useWebWorkerFn<T extends (...fnArgs: any[]) => any>(fn: T, optio
tryOnScopeDispose(workerTerminate)

const generateWorker = () => {
const blobUrl = createWorkerBlobUrl(fn, dependencies)
const blobUrl = createWorkerBlobUrl(fn, dependencies, localDependencies)
const newWorker: Worker & { _url?: string } = new Worker(blobUrl)
newWorker._url = blobUrl

newWorker.onmessage = (e: MessageEvent) => {
const { resolve = () => {}, reject = () => {} } = promise.value
const { resolve = () => { }, reject = () => { } } = promise.value
const [status, result] = e.data as [WebWorkerStatus, ReturnType<T>]

switch (status) {
Expand All @@ -82,7 +87,7 @@ export function useWebWorkerFn<T extends (...fnArgs: any[]) => any>(fn: T, optio
}

newWorker.onerror = (e: ErrorEvent) => {
const { reject = () => {} } = promise.value
const { reject = () => { } } = promise.value
e.preventDefault()
reject(e)
workerTerminate('ERROR')
Expand Down
5 changes: 3 additions & 2 deletions packages/core/useWebWorkerFn/lib/createWorkerBlobUrl.ts
Expand Up @@ -6,6 +6,7 @@ import depsParser from './depsParser'
*
* @param fn the function to run with web worker
* @param deps array of strings, imported into the worker through "importScripts"
* @param localDeps array of function, local dependencies
*
* @returns a blob url, containing the code of "fn" as a string
*
Expand All @@ -15,8 +16,8 @@ import depsParser from './depsParser'
* .then(postMessage(['SUCCESS', result]))
* .catch(postMessage(['ERROR', error])"
*/
function createWorkerBlobUrl(fn: Function, deps: string[]) {
const blobCode = `${depsParser(deps)}; onmessage=(${jobRunner})(${fn})`
function createWorkerBlobUrl(fn: Function, deps: string[], localDeps: Function[]) {
const blobCode = `${depsParser(deps, localDeps)}; onmessage=(${jobRunner})(${fn})`
const blob = new Blob([blobCode], { type: 'text/javascript' })
const url = URL.createObjectURL(blob)
return url
Expand Down
19 changes: 16 additions & 3 deletions packages/core/useWebWorkerFn/lib/depsParser.ts
Expand Up @@ -4,18 +4,31 @@
* this string will then be passed as an argument to the "importScripts" function
*
* @param deps array of string
* @param localDeps array of function
* @returns a string composed by the concatenation of the array
* elements "deps" and "importScripts".
*
* @example
* depsParser(['demo1', 'demo2']) // return importScripts('demo1', 'demo2')
*/
function depsParser(deps: string[]) {
if (deps.length === 0)
function depsParser(deps: string[], localDeps: Function[]) {
if (deps.length === 0 && localDeps.length === 0)
return ''

const depsString = deps.map(dep => `'${dep}'`).toString()
return `importScripts(${depsString})`
const depsFunctionString = localDeps.filter(dep => typeof dep === 'function').map((fn) => {
const str = fn.toString()
if (str.trim().startsWith('function')) {
return str
}
else {
const name = fn.name
return `const ${name} = ${str}`
}
}).join(';')
const importString = `importScripts(${depsString});`

return `${depsString.trim() === '' ? '' : importString} ${depsFunctionString}`
}

export default depsParser