They were my favorite at AGT :)

And ?

Recently I was working on a client-side project that could benefit from a module like pattern with a bunch of async script tags to be executed in some desired order. This read is about a small attempt to bring some of that functionality with only ES5.

If you happen to work with Javascript in the last couple of years, probably you have heard of ES6 import/exports, AMD and CommonJS (or UMD) in one way or another. The thing is if you were to use the ‘fancy’ ES6 syntax, at some point you will need Babel to transpile that to regular ‘require’ modules with CommonJS which in turn will need another tool like Browserify or Rollup to bundle them so that you can use that in the browser.

So what ?

In some cases you just want some basic functionality without dealing with any other third party scripts with minimal overhead = nothing fancy:

Throw a bunch of scripts that ‘should’ in theory execute with the order you want while taking advantage of the ‘async’ attribute if possible.

Minimum leak to global scope

Share objects/primitives between each script even though each script tag has everything wrapped in an iief (immediately invoked function expression).

Is that a replacement ?

Noway. ES6 import/export or CommonJS handle way more complicated situations with edge cases. However if you want a small script that can handle a task queue while sharing variables within closures, this might get you going.

The script

I will give an example use case further down the article but I might as well throw the whole rascal here as it is only 68 lines:

Check it out at Github

The idea is to store exported functions/objects within another object that manages both execution order and exported variables. Functions are executed based on their _taskqWaitFor property while being passed exported variables in the order specified in their arguments.

Example

For the following example suppose we have the taskq object available at global scope with 3 script tags loaded asynchronously somewhere in our html file:

<script type=”text/javascript” src=”./test2.js” charset=”UTF-8" async></script> …

<script type=”text/javascript” src=”./test0.js” charset=”UTF-8" async></script> …

<script type=”text/javascript” src=”./test1.js” charset=”UTF-8" async></script>

Our expectation is that the scripts execute in the order test0.js > test1.js >test2.js with some variables shared between them. Let’s look at test0.js :

test0.js

Note that each script has an outer iief which wraps everything enclosed. I have defined someObject with 2 properties. Next we define the function to be exported. Since functions are first class objects in js, we can attach properties directly to them. 3 properties can be useful here:

an identifier for the function.( _taskqId )

) an array that lists references to other functions that must be executed in order for the current function to execute.( _taskqWaitFor )

) a DOMString that will refer to this via exports[“someString”] (_taskqScope)

In our example we set the _taskqId property to 0 for identification of this function. There is no _taskqWaitFor field as this will be the first function to be invoked. To add the function to the queue, we use the taskq.push method. This way, at the window load event, queued functions will be sorted (take a look at lines 32–42 at the main taskq.js file to see how the sorting is handled) and then invoked.

Within the test0 function, we also use another method: taskq.export

So we are exporting the someObject to be used later by test1.js:

test1.js

At lines 16–18 we set the id, waiting order and the scope of test1 function. Everything inside the iief is invoked before the test1 (since it is pushed, not called at line 20), so we can use that to our advantage and define some variables here to be later used. Here we defined some private object at line 2 and exported it as refToThis at line 19. Since we specified the scope at line 18, at the time function test1 is invoked the value of this will be somePrivateObject.

Also beware that test1 function has access to the previously exported someEntity variable. To refer to this exported value, the argument is specified at line 6 with the same name.

We can also do funky things like exporting self. At line 14, the function exports itself as testItem. Of course this will allow us to redefine the scope of this as we will see in test2.js (or export a bound version with bind):

test2.js

Here we gave the test function a special keyword loadend. Keywords like loadend or loadstart (see lines 23–24 at the main taskq.js script) control the execution order without needing to specify the _taskqWaitFor array. But we defined it here anyway to be explicit. The test function also has access to the previously self exported test1 function. This is referred as testItem argument at line 2 because previously we exported it with that name at line 14 of test1.js. Note that when we execute the testItem, value of this is NOT somePrivateObject but the window object.