Well, I know many of you will shout back on me and say we have
Web Workers, but for the simplicity of the article and to stay in line with the underlying idea, I’d refer to it as single threaded and refrain from using Web Workers. I’m aware that the same problem statements described in the article can be solved using web workers, but the approach I’m going to discuss is easier to reason about and gels well with your existing code base without much changes.
scroll etc) inside browsers. Now you might be wondering this would mean an utterly unresponsive UI if the underlying operations are expensive. And most of you at some point in your life, have already seen the browser alert for unresponsive script.
Here’s a demo of the problem statement.
If you run this code and click on the Show List button, the UI will get frozen and unresponsive until the
showProductList method and all its internal method calls and operations has completed. The users cannot interact with your page in the meantime. But why does this happen?
showProductList and the entire related call chain).
Now comes the interesting part, we can manipulate our execution by pushing parts of our operations on this queue (also known as
deferred execution) and allowing other user interactions to be queued and processed in the meantime. Result is a non-blocking, highly interactive UI. But wait, does this mean any complicated Web API to deal with low level message processing in the browsers. Well, no! Welcome the familiar
setTimeout will add a message to the queue after the time passed as a second argument. If there is no other message in the queue, the message is processed right away; however, if there are messages, the
setTimeout message will have to wait for other messages to be processed. The notion of
message here means a set of instructions that are required to be processed at a time. Having the idea of
defer execution, let’s explore how we can improve our user interaction and the related expensive computation.
Promiseis a powerful tool for making a highly responsive UI even with underlying large computations
First let’s try to break down the operations into chunks that can be processed independently. We would use
Promise in combination of
setTimeout to break each of the functions in the call chain into smaller operations.
We see how we converted
generateId into a deferred function that is using a combination of
setTimeout to totally defer the execution and still giving you the ability to perform controlled operations. Now let’s make a generic
defer function that will take a function and apply the same pattern to it.
Having this generic
defer function implementation, let’s try to break our application logic into smaller units.
Processing large set of data in smaller chunks and pushing them over the event loop, would give and immediate boost in the responsiveness of the UI
getRandomProducts can be made into a deferred function as
deferredGenerateId. However, we would want to further break the computation into smaller computations and compose the result. Let’s see how we do this.
Now let's have a look at how we break down the
getList function to process data in chunks.
And finally the
Here’s the final version, with a non-blocking UI.
We see that
setTimeout along with
Promise is a powerful tool for making a highly responsive UI even with underlying large computations. We can apply this pattern quickly without much disruption to the existing code to gain some responsiveness in the code. Of course, it’s not a holy grail and for other extensively complex scenarios, one should consider investing effort in implementing solutions using