Minimalistic event/promified driven web worker abstraction.
npm install event-worker --save
https://cdn.jsdelivr.net/npm/[email protected]/index.min.js
Basic example
In your main thread (main.js):
const EventWorker = require('event-worker')
const worker = new EventWorker('path/to/my/worker.js')
async function test(){
const user = await worker.emit('getUserById', { id: '30242' })
/*
{
id: '30242',
name: 'neil',
lastname: 'tyson degrasse'
}
*/
//...
And then in your web worker (worker.js) you can listen for that event and respond back with the requested data:
const EventWorker = require('event-worker')
const worker = new EventWorker()
worker.on('getUserById', async ({payload}) => {
let user = await getUser(payload.id)
return user // Respond back to the main thread with the data requested.
})
async function getUser(id){
let user = await fetchUserFromLocalDatabase(id)
if(user) return user
user = await fetchUserFromServer(id)
saveUserInLocaDatabase(user)
return user
}
If you want to keep your main thread running smoothly dividing the work load of expensive computational task between multiple web workers becomes easier.
From main thread (main.js):
const EventWorker = require('event-worker')
const workerPath = 'path/to/my/worker.js'
const workerPool = [
new EventWorker(workerPath),
new EventWorker(workerPath),
new EventWorker(workerPath)
]
const sum = (a, b) => a + b
const multiplyBy2InOtherThread = (worker, index) => worker.emit('multiply_by_2', index)
(async ()=>
(await Promise.all(
workerPool.map(multiplyBy2InOtherThread)
)).reduce(sum, 0)
)() // 6
From worker (worker.js):
importScripts('path/to/source/event-worker.js')
const worker = new EventWorker()
worker.on('multiply_by_2', ({payload}) => payload * 2 )
You can listen for events triggered by your workers.
From main thread (main.js):
//...
worker.on('interestingData', ({payload})=>{
doSomethingWithInterestingData(payload)
return 'Good job worker!'
})
//..
From worker (worker.js):
//...
const res = await worker.emit('interestingData', 'interestingString')
res // => 'Good job worker!!'
Instead of having a separate file for your worker, you can wrap your code inside a function and pass it as an argument to the constructor of EventWorker. This is a good option when prototyping.
From main (main.js):
const worker = new EventWorker(async (mainThread) => {
let res = await mainThread.emit('sayingHiFromWorker', 'Hi main thread!')
console.log(res) // Hello worker!
})
worker.on('sayingHiFromWorker', ({payload}) => {
console.log(payload) // Hi main thread!
return "Hello worker!"
})
When you inline functions it is easy to get confused by the execution context. If you try to access a variable that is outside the scope of the inline function it will fail.
const favoriteAnimal = 'chiguire'
const worker = new EventWorker(async (mainThread) => {
// This will get executed in a worker.
mainThread.on('onGetAnimals', ()=>{
console.log(favoriteAnimal) // fails. favoriteAnimal variable is not in the same execution context.
//...
})
})
Error handling works the same as you would expect from a promise executed in the same thread:
From main thread (main.js):
const EventWorker = require('event-worker')
const worker = new EventWorker('path/to/my/worker.js')
worker.emit('rejectThisCall')
.catch((reason) => {
console.log(`Rejected because: "${reason}" `)
})
From worker (worker.js):
importScripts('path/to/source/event-worker.js')
const worker = new EventWorker()
//throwing errors
worker.on('rejectThisCall', () => {
throw new Error()
})
// throwing async errors
worker.on('rejectThisCallAsync', async ()=> {
throw new Error()
})
Instead of embedding event-worker into your worker file with a module bundler, you can use the built in function importScripts
:
importScripts('path/to/source/event-worker.js')
const worker = new EventWorker()
// ...
EventWorker reference is injected into the global scope once it's loaded.
Creates a new instance
-
source
string | function | undefined
-
If a string is passed: It will assume it is the worker source file path.
-
If a function is passed it will get converted into a string an then transformed into a worker.
-
If nothing (undefined) is passed it will assume that the environment is the worker.
-
Emits a event.
-
eventName
String
-
data
Any
Registers for an event.
-
eventName
-
callback
function(object) => Promise<any>
Gets executed when eventName is emited.
- object
-
object.payload
any
Data sent from the event emitter to the listener.
-
- object
Immediately terminates the Worker. This does not offer the worker an opportunity to finish its operations; it is simply stopped at once.
- Removed
"resolve"
and"reject"
function properties in the"on"
callback.
All contributions are welcome.
MIT © Alvaro Bernal