useWorker
Reactive worker for multiprocessing.
This composable provides a module to use complete and easy-to-use multiprocessing ⚡.
NativeScript runs in the main thread, just like native applications do. We also have the Workers module available to allow processing in a thread other than the user interface. This allows us to keep the UI thread free to hold animations and touch events and any interactions without any blocking.
This module facilitates the use of the workers module, launching the function that we pass to useWorker
to a worker and thus keeping the main thread free for the user.
This module is based on @nativescript-use/nativescript-task.
Understanding how the module works and configuration
With the default values this module will initialize a worker that will be available during the lifetime of the application, this is to save the worker startup time and launch the tasks to a worker that will always be available to execute tasks (in the documentation we will call it global worker or stickyWorker).
Additionally, this module manages the use of the global worker and if it is in use by a task, it will send its tasks to another worker that will create and terminate when said task is finished.
We can modify all this operation with the next configuration.
// app.ts | main.ts (entry file app)
import { Task } from "@nativescript-use/vue";
Task.globalWorkerConfig({
stickyWorker: true,
newWorkerIfGlobalIsUsed: true,
startGlobalWorker: true
});
// run app
stickyWorker
- defaulttrue
: When set to true the plugin always keeps a worker running to launch your tasks to this worker. This saves time when launching the task since initializing a worker takes time, by default it is true to launch each task as quickly as possible. If set to false the plugin will initialize a worker and terminate each task.newWorkerIfGlobalIsUsed
- defaulttrue
: WhenstickyWorker
is true and we have a worker always running, if we launch a task and the main worker is running with another task, a new worker will be created to launch this task and not wait for the previous task to finish. If you disable this flag and launch another task while one is running, it will have to wait until it reaches the beginning of the queue.startGlobalWorker
- defaulttrue
: Initialize the global worker when the configuration is set, it will be available when you launch your first task. If disabled, when the first task is launched it will have the worker creation delay.
Note: when the worker execution time is mentioned we are talking about about 200ms (it is almost nothing). It's not much, but this plugin prioritizes speed, which is why we keep a stickyWorker
always available.
Basic Usage
<script lang="ts" setup>
import { useWorker } from "@nativescript-use/vue";
import { onMounted, ref, watch } from "nativescript-vue";
const myReactiveData = ref({ title: "Reactive data in background", value: 1000 });
const {
result, // reactive return value
update, // reactive update value
isRunning, // reactive isRunning worker
isFinish, // reactive isFinish worker
error, // reactive string error value
runTask // function to run task
} = useWorker((ctx) => {
console.log("Running in background with data: " + JSON.stringify(ctx.state));
return ctx.state.value + 10;
}, { state: myReactiveData })
onMounted(() => {
runTask();
})
watch(result, () => {
//On change result
console.log("New result from background " + result.value);
setTimeout(() => {
myReactiveData.value = { title: "New Reactive data in background", value: Math.floor(Math.random() * (10000 - 1 + 1) + 1) }
runTask();
}, 500);
})
</script>
useWorker configuracion
{
state?: Ref<TState> | TState,
initialResult?: TResult,
runOnChangeState?: boolean,
attachToContextFunctions?: {},
inmediate?: boolean,
performance?: boolean,
on?: {
progressUpdate?: (update: { data: TUpdate }) => void
completed?: (data: TResult) => void,
error?: (error: string) => void,
}
}
state
: data that will be passed to the context of the function to be executed.initialResult
: is the default reactive objectresult
that will be returned when implementing theuseWorker
composable.runOnChangeState
: If set to true andstate
is Ref or Reactive the function will be executed with each change made to the object passedstate
.attachToContextFunctions
: allows adding functions external to the worker to the worker context. See inline-functions.inmediate
: If set to true the worker will run immediately.performance
: Utility to check how long a function takes outside the worker and inside it. Useful to check the function execution time and whether you should send this task to the background or not. Use this only in development mode.on:{ progressUpdate }
: Callback that we can invoke from the worker to send updates to the main thread before the worker has finished. See onProgressUpdate.on:{ completed }
: Callback that will be executed when the task has finished successfully.on:{ error }
: Callback that will be executed when the task has finished with an error.
Inline functions
See attachToContextFunctions
option.
<script lang="ts" setup>
import { useWorker } from "@nativescript-use/vue";
function fibonacci(num: number): number {
if (num <= 1) return 1;
return fibonacci(num - 1) + fibonacci(num - 2);
}
const { runTask, result } = useWorker((ctx) => {
return fibonacci(ctx.state);
}, { state: 43, attachToContextFunctions: { fibonacci } });
</script>
onProgressUpdate
See onProgressUpdate
option and reactive update
.
<script lang="ts" setup>
import { useWorker } from "@nativescript-use/vue";
const { runTask, update } = useWorker((ctx) => {
// Action in background
ctx.onProgressUpdate({ title: "Loading image", value: "10%" });
// Continue action in background
ctx.onProgressUpdate({ title: "Transforming image", value: "50%" });
// Continue action in background
ctx.onProgressUpdate({ title: "Saving image", value: "90%" });
// Finish action in background
return { finish: true };
}, {
state: { file: "file.png" },
on: {
progressUpdate(updateData) {
// This value would be the first execution: { title: "Loading image", value: "10%" }
console.log(updateData.data);
// update.value is the reactive object that will also have the value { title: "Loading image", value: "10%" }
console.log(update.value);
}
}
});
</script>
Using dependencies
We need to define a worker with the imports that we want to have defined in our tasks. We define the file globalWorker.ts|js
in the src|app
folder of our project, import the modules that we want to have available and pass them to the defineWorker
function that this library provides.
// /app/globalWorker.ts|js
import '@nativescript/core/globals';
import { defineWorker } from "@nativescript-use/vue";
import { myUtils } from '@utils';
import { otherLib } from 'other-lib';
defineWorker({ imports: { myUtils, otherLib } });
Now access the modules defined in the globalWorker file from the context.
import { useWorker } from "@nativescript-use/vue";
import { myUtils } from '@utils';
import { otherLib } from 'other-lib';
const { runTask, result } = useWorker((ctx) => {
return myUtils.someFunction(1000) + otherLib.otherFunction(ctx.state);
}, { state: 100 })
Source
Type declaration
import { Ref, ComputedRef } from "nativescript-vue";
import { TaskFunc } from '@nativescript-use/nativescript-task';
import { Ref, UnwrapRef, DeepReadonly } from 'nativescript-vue';
export * from "@nativescript-use/nativescript-task";
/**
* Reactive worker for multiprocessing.
*/
export declare function useWorker<TState, TResult, TUpdate>(fun: TaskFunc<UnwrapRef<TState>, TResult, TUpdate>, options?: {
state?: Ref<TState> | TState;
initialResult?: TResult;
runOnChangeState?: boolean;
attachToContextFunctions?: {};
inmediate?: boolean;
performance?: boolean;
on?: {
progressUpdate?: (update: {
data: TUpdate;
}) => void;
completed?: (data: TResult) => void;
error?: (error: string) => void;
};
}): {
result: Readonly<Ref<DeepReadonly<UnwrapRef<TResult>>>>;
update: Readonly<Ref< readonly data: DeepReadonly<TUpdate> }>>;
isRunning: Readonly<Ref<boolean>>;
isFinish: Readonly<Ref<boolean>>;
error: Readonly<Ref<string>>;
runTask: (newData?: Ref<TState> | TState) => void;
};