npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

work-smart

v1.0.0

Published

Make Web Workers fun again 🚀

Downloads

1

Readme

work-smart

Make Web Workers fun again 🚀

Features

  • 👌 Function based workers
  • ♾️ Easy to use event system instead of postMessage
  • 📲 Use callback functions
  • 📦 Export worker functions
  • 🟦 Typescript compatible

Why?

When you are using Web Workers without work-smart you

  • ➡️ Need to create a separate file for each worker
  • ➡️ Can't easily pass in data from the main thread
  • ➡️ Have to deal with postMessage() all the time
  • ➡️ Can't use a nice event system
  • ➡️ Can't pass in callback functions
  • ➡️ Can't call worker functions from the main thread

Installation

Expecting you are using a module bundler, run:

npm i -D work-smart

And you are good to go!

Usage

First import the smart function:

import smart from 'work-smart'

Because work-smart is designed to use function based worker, you create a worker like this:

const worker = smart((self) => {
    // This code will run in a web worker 👍
})

// Run the worker
worker.start()

Passing in arguments

Because the worker runs in a separate thread, it has no access to variables or functions defined outside of the function. However if you want to pass in variables or functions from the main thread, you can do that easily:

function myFunc() {
    console.log('🚀')
}

smart((self, myStr, myFunc) => {
    console.log(myStr)
    myFunc()
}, [
    'This variable is from the main scope!',
    myFunc
])

Just make sure that the functions don't rely on other functions outside of their scope.

Terminating a worker

If you want to stop a worker form doing what it's currently doing, you can terminate it.

Inside the worker:

smart(self => {
    // It's also best practice to add this add the end of each worker function
    self.terminate()
})

Inside the main thread:

const worker = smart(() => {})
worker.terminate()

The event system

If you want to share data between the worker and the main thread, you can use the event system. Here is an example script:

const worker = smart((self) => {
    self.on('my-event', () => {
        console.log('Event was dispatched')
    })
})

worker
    .start()
    .emit('my-event')

You can pass data too:

const worker = smart((self) => {
    self.on('my-event', (data1, data2) => {
        console.log(data1, data2)
    })
})

worker
    .start()
    .emit('my-event', 123, 'hello')

Or define a listener in the main thread:

const worker = smart((self) => {
    self.emit('my-event', 123, 'hello')
})

worker
    .start()
    .on('my-event', (data1, data2) => {
        console.log(data1, data2)
    })

However, one of work-smart's key features are callback function

const worker = smart((self) => {
    self.emit('do-something-in-main', (arg) => {
        // This is a callback function
        console.log('CB:', arg)
    })
})
worker.on('do-something-in-main', (cb) => {
    cb('Test!')
})
worker.start()

Callback functions are executed where they were defined, but can use arguments from the other thread. If you have used socket.io, this is going to feel familiar.

You can also get the return value of a callback function by awaiting them:

const worker = smart((self) => {
    self.emit('do-something-in-main', () => {
        // This is a callback function
        return 'Test!'
    })
})
worker.on('do-something-in-main', async (cb) => {
    console.log(await cb())
})
worker.start()

This even works with async functions:

const worker = smart((self) => {
    const sleep = ms => new Promise(r => setTimeout(r, ms))
    
    self.emit('do-something-in-main', async () => {
        // This is a callback function
        await sleep(1000)
        return 'Test!'
    })
})
worker.on('do-something-in-main', async (cb) => {
    console.log(await cb())
})
worker.start()

Worker functions

You might want your worker to different tasks, whenever requested by the main thread. Instead of having to deal with events, you could also create worker functions, which can be called by the main thread:

const worker = smart(self => {
    // Initialize a function called myfunc
    self.fn('myfunc', (arg) => {
        console.log(arg)
    })
})
worker.start()

// Call the myfunc function with "Hello World!" as an argument
worker.fn('myfunc').call('Hello World!')

As always you can use return values by awaiting the function call.

const worker = smart(self => {
    // Initialize a function called myfunc
    self.fn('myfunc', (arg) => {
       return arg
    })
})
worker.start()

// Call the myfunc function with "Hello World!" as an argument
console.log(await worker.fn('myfunc').call('Hello World!'))

This will work with async functions too, but you probably are already aware of this.

ESM mode

If you want you worker to be of type module, you can just pass true as the first argument to the worker.start function:

worker.start(true)

However, I didn't notice a difference between those two yet.

How does it work?

When you create a SmartWorker, a data url will be created based on your worker function. As soon as you call worker.start(), a Web Worker get's created based on this url.

All of the other nice features are just fancy API wrappers for postMessage(). All your events and data will be encoded into the WSETP (Work Smart Event Transfer Protocol). If you want to read more about that, take a look at the specification. Your data will be serialized using JSON, and for callback functions a wrapper is created, which stores a call id and transmits arguments and return values over the WSETP. Even Worker Functions are just an abstraction of the WSETP.