Pausing/resuming browser & app logic using Taskq.js

Ibrahim Tanyalcin
6 min readFeb 21, 2018

--

I found this somewhere in the internet, kinda represents the asynchronous nature, while all trees still pointing up..

This is the second series of my past post (since then Taskq changed a lot) about Taskq → a modern solution to dynamically load scripts and represent application logic while giving the ability to pause/resume execution.

Unexplored concept:

> requestAnimationFrame + Promise

In your browser, Taskq will take advantage of Promises and requestAnimationFrame (rAF). Can they be combined while loading and executing scripts? Turns out, absolutely!

Compatibility without transpiling & polyfills

The great thing about combining Promises & rAFs is that it gives you the ability to fallback to only rAFs on older browsers. This way we can take advantage of latest improvements in JavaScript while providing backwards compatibility without bloating the browser with polyfills. As of writing this article, version 2.1.4 of Taskq is only 6kB minimized!

Once advantage is that since functions are executed within a micro-task queue, they will be non-render blocking. To see full list of advantages, take a look in here.

With Taskq, you get a global taskq variable (or whatever data attribute you pass to the entry script), and by doing taskq.pause or taskq.resume you control the pace of execution including nested dynamic imports/loads.

Taskq operates on async scripts, which the browser will fetch asap, but you get to control the contents. I have outlined 12 key examples in the main Github repository if you want to familiarize yourself.

Today we will make a small app, without using any other library than Taskq.js, in plain js, that draws sin(x) between 0 to 2*PI, where all logic is expressed in async scripts that are dynamically and recursively reloaded while auto keeping the DOM clean.

The aim of this app is to demonstrate some of the techniques you can use with Taskq, the main app can perhaps be written short, or perhaps not.

You will find all the scripts you need in the below snippets, or in the Github repository (exampleMedium folder) or a live version here: http://ibrahimtanyalcin.com/taskq/

Since all the logic is expressed in script tags that are dynamically loaded, we will start with 0 stylesheets, almost no elements, and slowly populate the screen while pausing between scripts, and then we will start drawing the function while giving you the ability to pause/resume. Don’t forget you are not just blocking a function call, but also blocking browser execution of pending script queue! (Verify this with inspect element)

Nuff said, what does it look like?

This is what you should be seeing:

Let’s get started.

The HTML

The starting html is minimal. We don’t have much but we will add some elements including a stylesheet.

  • div.row for plotting the points
  • div.sideCar for console messages
  • buttons for pause/resume
  • a github link

We will add all of the above with dynamic script tags while controlling execution time and sharing variables between functions. The main taskq.js script is 2 folders up in this html, adjust it accordingly in your own fork.

Now let’s see the stylesheet:

We will also add this stylesheet with a separate script after the load event. This way, your load event doesn’t fire 1 min after the DOMContentLoaded fires :).

Now let’s see the main entry script → index.js

Here, you might see a couple of strange things:

  • there is an outer iief (immediately invoked function expression) enclosing the main function index.
  • the main function is not called but instead pushed to taskq.
  • the main function gets passed an exports argument.
  • whenever taskq.load is called, it returns a thennable object (which works in ie9+)
  • the thennable objects are passed an optional resolver argument, which you may use to resolve anytime to delay the execution of next then clause and other scripts waiting to be loaded.

Ok so let’s go over these concepts. First, the outer iief gets executes as soon as the browser fetches the async scripts contents. But when you call push, you are adding functions to the main thread which will ultimately be sorted (based on manually added _taskqId and _taskqWaitFor properties of the pushed function, which we do not need here since we only have 1 script tag → index.js .) and called with the arguments you specify.

Taskq automatically calls the pushed functions for you at the load event, and then empties its exports so that they can be garbage collected (provided that there are no other pointers). Later you can start a new thread by pushing functions like here, but in the end you need to call perform method:

taskq.push(f1).push(f2).push(f3).perform()

Anyway, back to exports. In Taskq, you can export any variable with an alias:

taskq.export({x:3}, “someVariable”);

Here at the end of the iief, I exported an empty object literal which we will use later for populating it with variables. I used only 1 variable here, you can use as many as you want, the arguments order does NOT matter, Taskq will pass the arguments with the correct order.

Within the index function we are loading resources and then waiting about 3 seconds in between while outputting messages. Load calls wait for each other. Another load encountered while current load not finished gets registered in the scriptQueue . You can also define more complex logic by using nested load calls, which I gave examples in the Github, but I will go for straight sequential calls here.

At the very end of the routine, we call “./draw.js” which ultimately loads itself. Since browsers cache the results it is not fetched again (unless you specifically configure headers from the server or do cache busting/incognito). Here I left everything default.

Through out index function we are outputting messages on the sidecar using exports.message function. So we load that first:

Above snippet’s iief gets executed which pushes the messenger function. Since this is a dynamic load call, Taskq will push this to immediate queue , which you can think of as a sub-branch. First the iief executes, then the pushed function executes and last all the thens execute for this subbranch. Only then the main thread continues.

Once messenger is called, a message property is added to the exports which we can use throughout index.

Next we load loadStyles.js:

At this point you will see a jump in the page since we added stylesheets. Within one of the then clauses, we set some properties step and counter in the shared exports object which draw.js will use to position points.

Now, let’s add a github icon:

And here is draw.js script:

The good thing here is that recursive calls to load does not blow the stack because these are pushed to queue to be processed later.

I normally work on data visualization projects (Here is one example project I have been working on: https://github.com/IbrahimTanyalcin/LEXICON , and here its publication: http://ieeexplore.ieee.org/document/8291800/ , I’d be grateful if you check them out.). So I was kinda inclined to use d3.js for plotting etc but since not everyone is familiar with that I decided to go bare bones minimum.

So we will basically use small divs with border-radius set to 100% to represent points :)

And finally some buttons to close the logic and enable pause/resume:

This one looks bit bulky but most of it is comments.

Put everything under one folder, and adjust the path of taskq and you are good to go! Alternatively check the live example: http://ibrahimtanyalcin.com/taskq/

The great thing about this approach is that if you think each dot as a different component/routine/application (because it is not just the orange dot, but entire script being re-run with updated parameters), it allows you to easily adapt, pause and change the flow without messing up with the global namespace.

At this point I would be grateful if you:

  • visit the Github repository and try it out. File bug reports if you bump into inconsistencies.
  • Feature requests that you think would be useful → so that I can add them to TODO list.
  • Can completely rant about it if you want:)) If you think it is shit, point out the stinky parts → help me make it better!
  • Do a performance test and report back to me!

Ty!

--

--

Ibrahim Tanyalcin

Postdoc in Bioinformatics. Former genetic engineer. All time geometry enthusiast. Interested in Viz. Works? mutaframe.com, i-pv.org, ibrahimtanyalcin.com/gwent