Speed up your App with Web Workers

Watch “Get started with Web Workers” on egghead.io

I remember when I started learning about threads in Java. My college professor
pulled up iTunes, hit play on a song and said: “if it weren’t for threads, I
wouldn’t be able to click any of these buttons at the same time iTunes is
playing this music.”

JavaScript is single-threaded. This means that any JavaScript environment will
not run multiple lines of JavaScript in the same process simultaneously (the
browser handles audio-playback separate from the thread it gives you for your
JavaScript which is why your code can run while music is playing in the
browser). The single threaded-ness of JavaScript drastically simplifies a lot of
programming in JavaScript, but it does come with some drawbacks.

One of the most significant of these drawbacks comes in the form of user
experience. To illustrate my point, go ahead and open a new tab to twitter.com
and open your browser DevTools console. Then copy/paste this and hit enter:

while (true) {}

Can you interact with the web page anymore? No? That’s because your code is
keeping the JavaScript thread so busy just hanging out in that infinite while
loop that no other JavaScript can do anything. (If you’re stuck and you can’t
close that tab, my apologies. In Chrome you can stop the tab by going to “More
tools” -> “Task Manager” and selecting the tab and clicking “End Process”).

So the moral of the story is don’t use infinite loops in your code right? Well,
I think we all can agree on that, but I’ve got a stronger, more practical point
to this. What if you have some code that takes a long time to run? Maybe it’s…
I don’t know… Mining bitcoin or something. With some kinds of computations,
there’s only so much performance optimization you can do before you just hit the
limits of the machine that’s running your code. So are your users just stuck
with a really bad experience using your website whenever that code has to run?
No!

You know how you can have multiple tabs open in your browser? Each one of those
tabs is running the JavaScript for that page in its own thread. So just because
JavaScript is single-threaded, doesn’t mean the browser can’t spin up multiple
threads to run different JavaScript files.

Web Workers
are a browser standard that enables you to do just that! And you can even
communicate between those different threads (with some limitations, which we
won’t get into in this post).

Here you go:

<script src="main.js"></script>
// main.js
const worker = new Worker('worker.js')
worker.postMessage('Hello Worker')
worker.onmessage = e => {
  console.log('main.js: Message received from worker:', e.data)
}
// if you want to "uninstall" the web worker then use:
// worker.terminate()
// worker.js
this.onmessage = e => {
  console.log('worker.js: Message received from main script', e.data)
  this.postMessage('Hello main')
}

You can preview this here (open your console):
super-simple-web-worker.netlify.com

There you go. You can now run your bitcoin miner without locking up the main
thread! In fact, in the Chrome DevTools Sources tab, it shows that we have
another thread:

Chrome DevTools Sources tab showing a thread titled worker.js

You can even put a breakpoint in your code and debug it like you’re used to.
Neat!

I remember when Web Workers became a thing. And I guess it was longer ago than I
remember because IE10 supports Web Workers. So if you have to support a browser
that’s not supporting Web Workers then I’m sorry, I just don’t know what to tell
you.

For the rest of us, how do we go from this simple one-file setup to something
that will scale well/support/modules/etc? Well, my favorite solution to this is
workerize by
Jason Miller:

Workerize logo

It’s awesome, but even more awesome is the sibling project by Jason called
workerize-loader which is a
webpack loader for workerize which basically means you can put any module (and
the modules that it imports) into a webworker.

It’s really easy to use too. I teach about this in
my React Performance workshop. There are a few
exercises that show you how to optimize a client-side search input component
which allows you to do a filter of thousands of cities using
match-sorter and
Downshift.

We have a module that has the whole list of cities and exposes a getItems
function which accepts the user’s input and then returns an array of the
matching cities.

That workshop material uses react-scripts (create-react-app). Here’s some of
the code from my material:

// eslint-disable-next-line import/no-webpack-loader-syntax
import makeFilterCitiesWorker from 'workerize!./filter-cities'

const {getItems} = makeFilterCitiesWorker()

export {getItems}

The workerize! thing in the import statement is a fancy webpack syntax to
tell webpack to treat that module specially (specifically to pipe it through the
workerize-loader so Jason can do his magic on it to get it into a web worker).

Putting the getItems code into a web worker did wonders to speed up my demo.
One catch to this is that before getItems was synchronous, but communication
between the main thread and a worker thread is asynchronous, so I had to alter
my app code a little bit to handle the asynchrony, but it was totally worthwhile
and improved the user experience a lot.

I hope this helps you out! I have a feeling that we don’t use web workers as
much as we probably could/should, so profile your app and see whether there are
any hot-spots in your JavaScript code that could benefit from a separate thread.
Good luck!




Source link

مدونة تقنية تركز على نصائح التدوين ، وتحسين محركات البحث ، ووسائل التواصل الاجتماعي ، وأدوات الهاتف المحمول ، ونصائح الكمبيوتر ، وأدلة إرشادية ونصائح عامة ونصائح