Worker Runner is a tool to assist use Web Worker.
- Initialization
- Usage example
- ResolvedRunner
- Runner token
- ResolvedRunner as argument
- RunnerResolverLocal
- ResolvedRunner as method result
- Transfer data
- Usage with RxJs
- Warning
Declare your classes with the methods you need.
exportclassLibraryRunner{// An example of your Runner implementation// Variables will not be available to the ResolvedRunner,// but can be used internally by the Runner instancepublicbooks: string[];constructor(books: string[]){// Constructor can take argumentsthis.books=books;}publicaddBook(book: string): void{this.books.push(book);}// The method can return a resultpubliccheckBook(book: string): boolean{returnthis.books.includes(book);}// The method can return result asynchronouslypublicreserveBook(book: string,time: number): Promise<string>{returnnewPromise(resolve=>setTimeout(()=>resolve(book),time));}}Declare your instance of RunnerResolverClient in a Client (Main) area.
- Creating a
RunnerResolverClientwith a Runner list will slightly speed up the first resolving of theResolvedRunnerbecause the available methods will not be requested from the Host area. But this will also increase the size of the code loaded into the Client area. - You must wait until the asynchronous call to the
run()method completes.
// Client areaimport{RunnerResolverClient}from'@worker-runner/promise';constresolver=newRunnerResolverClient({connection: newWorker('./worker.js'),});resolver.run();// await asynchronousAnd also declare your instance of RunnerResolverHost in a Host (Worker) area.
- The
RunnerResolverHostinstance must have a list of Runner classes. This list may differ from the list used inRunnerResolverClient. - Call the
run()method.
// Host areaimport{RunnerResolverHost}from'@worker-runner/promise';newRunnerResolverHost({runners: [LibraryRunner]}).run();After you initialized RunnerResolverClient(in client area) and RunnerResolverHost(in host area), you can use the RunnerResolverClient instance to resolve instances of the Runner class that will be used in the client area and executed in the host area.
asyncfunctionmain(){constresolver=newRunnerResolverClient({connection: newWorker('./worker.js'),});awaitresolver.run();constlibraryRunner=awaitresolver.resolve(LibraryRunner,['Book №1']);awaitlibraryRunner.addBook('Book №2');constisExist=awaitlibraryRunner.checkBook('Book №2');console.log('Book №2 exist:',isExist);// => Book №2 exist: trueconstreservedBook=awaitlibraryRunner.reserveBook('Book №1',100);console.log('Reserve ended for:',reservedBook);// => Reserve ended for: Book №1awaitlibraryRunner.destroy();}main();Runner that was resolved by RunnerResolverClient has the same methods as the original Runner instance.
All called methods will be executed asynchronously and the result of the calculation will be obtained using Promise.ResolvedRunner also has a set of methods:
destroy()
Destroying and remove Runner instance from resolved Runners list inRunnerResolverClientandRunnerResolverHostinstance.disconnect()
Unsubscribe from runner, if the control object was the last, then runner will be automatically destroyed.cloneControl()
Returns a new control object for the same Runner instance.markForTransfer()
When a Runner is flagged for transfer, if it is used as argument or as method result, the original control will be transferred. The originalResolvedRunnerwill lose control. In this case, the transfer of theResolvedRunnerwill be faster because it will not take time to request a copy of the control. It is convenient to use as an automatic disconnect after returning the result of a method.
RunnerResolver takes a list of Runners as a parameter. For each such Runner, you can set your own identifier - a token. To do this, add an object with parameters to the list, for example:
constresolver=newRunnerResolverClient({// ...runners: [{runner: LibraryRunner,token: 'LibraryRunnerToken',},{token: 'LibraryPoolRunnerToken',}],});You can resolve the ResolvedRunner instance, by token. This will reduce the bundle size and load only the code you need into each area. (This configuration is optional for the RunnerResolverClient. You can use a token without preconfiguration.)
By default, if you have not set a token, then the class name will be used.
WARNING: It is recommended to set a token. Using the class name is not recommended, because after minification, the names of the same class may differ for different bundles (areas).
You can use the resolved instance as constructor or methods arguments. Resolved instance can be declared in another RunnerResolver and area.
exportclassLibraryPoolRunner{// ...constructor(...libraries: ResolvedRunner<LibraryRunner>[]){// ...}addLibrary(library: ResolvedRunner<LibraryRunner>): void{// ...}}// ...constlibraryRunners=awaitPromise.all([resolver1.resolve(LibraryRunner,[]),resolver2.resolve(LibraryRunner,[]),]);constlibraryPoolRunner=awaitresolver3.resolve(LibraryPoolRunner,libraryRunners[0]);awaitlibraryPoolRunner.addLibrary(libraryRunners[1]);The original Runner instance will run in the same area in which it was resolved / wrapped.
RunnerResolverLocal can be used to replace RunnerResolverClient to simplify debugging in development mode and for testing.
That allows to use a local Runner as method result or pass it as argument.
// ...constlocalResolver=newRunnerResolverLocal();constresolvedLibraryRunner=awaitlocalResolver.resolve(LibraryRunner,[]);constresolvedLibraryPoolRunner=awaitresolver.resolve(LibraryPoolRunner);resolvedLibraryPoolRunner.addLibrary(resolvedLibraryRunner);resolvedLibraryRunner.disconnect();// ...WARNING: Remember to call the disconnect() or destroy() method, as appropriate, to avoid memory leaks.
If the method returns the ResolvedRunner as the return value, then its control object will be copied and transferred.
exportclassLibraryPoolRunner{// ...publicresolveLibrary(id: number): ResolvedRunner<LibraryRunner>{returnthis.resolvedLibraryRunners[id];}}WARNING: If you want the ResolvedRunner to be automatically disconnected after returning the result of the method, in order to avoid memory leak, call the markForTransfer() method, in which case the control will not be copied, but transferred. At the same time, the original ResolvedRunnerwill lose control.
exportclassLibraryPoolRunner{// ...publicresolveLibrary(id: number): ResolvedRunner<LibraryRunner>{returnthis.localResolver.wrapRunner(this.libraryRunners[id]).markForTransfer();}}If you need to use Transferable data as an argument or as a result of a method,
wrap such data in the TransferRunnerData class.
exportclassArrayBufferRunner{// ...publicresolveLibrary(id: number,data: ArrayBuffer,): TransferRunnerData<{id: number,data: ArrayBuffer},ArrayBuffer>{// ...returnnewTransferRunnerData({ id, data},[data]);}}// ...constarrayBuffer=newArrayBuffer(8);// ...resolvedArrayBufferRunner.resolveLibrary(0,newTransferRunnerData(arrayBuffer,[arrayBuffer]),);You can also use RxJS Observable (or Subject) to receive events from Host area. To do this, use the @worker-runner/rx library.
exportclassLibraryRunner{privatenotification$=newSubject<string>();// ...publicnotification(): Observable<string>{returnthis.notification$;}}// ...constresolver=newRxRunnerResolverClient({runners: [LibraryRunner],connection: newWorker('./worker.js'),});awaitresolver.run();constlibraryRunner=awaitresolver.resolve(LibraryRunner);(awaitlibraryRunner.notification()).subscribe(()=>{// ...})- Internet Explorer requires polyfill for
Promise. - Unfortunately the generic type cannot handle the
interfacecorrectly, but it does work correctly withtype aliasandclass. Therefore, it is recommended to usetype aliasinstead ofinterfaceto describe the type of your method arguments. - The library is compiled using the ES2015 standard. If you need support for older browsers such as Internet Explorer, then you will need to recompile the library using ES5 and Promise polyfill. You can see an example of configuring webpack here.