An open API service indexing awesome lists of open source software.

https://github.com/ibrahimtanyalcin/taskq

Async module loader with declerative dependency management in HTML and compatibility with ES6 import/export
https://github.com/ibrahimtanyalcin/taskq

async-modules es5 es5-javascript es6 html5 javascript javascript-library module-loader module-pattern promises queue script-loader task-manager task-runner umd-modules

Last synced: 24 days ago
JSON representation

Async module loader with declerative dependency management in HTML and compatibility with ES6 import/export

Awesome Lists containing this project

README

        

# taskq

[![Build Status](https://travis-ci.org/IbrahimTanyalcin/taskq.svg?branch=master)](https://travis-ci.org/IbrahimTanyalcin/taskq)
Patreon donate
Codacy Badge
Npm Badge
DOI


# Async Modules Supporting ES5 & ES6 with Control Flow

## ⇩ First, your 1 min cheatsheet ⇩
![cheatsheet](./cheatsheet.png)

# Navigation

If you want you can jump straight into the [**examples**](#examples-).

- ## [Advantages](#advantages-)
- ## [API](#api-)
- ## [Reading](#reading-)
- ## [What does it do?](#what-does-it-do-)
- ## [Usage](#usage-)
- ## [Changelog](#changelog-)
- ## [Examples](#examples-)

## Advantages [⏎](#advantages)

- 0 dependencies
- No polyfill required
- No transpiling required.
- No config file etc.
- About 6kB when minimized
- Will work on ie9+.
- Will play nice with other technologies/patterns you use in your page
- Non-render blocking
- You can pause/resume the main thread
- Aware of document state (hidden, minimized etc.)
- Fine grained control on execution of all imported scripts.
- Uses Promises combined with requestAnimationFrame(rAF). Falls back to rAF on older browsers (ie etc).
- Supports nested/single dynamic imports. Your main thread will wait until a module finishes dynamically importing other modules.
- Supports *then* , *catch* phrases for dynamic imports.
- You can do things that you cannot do with ES6 directly:
```
taskq.load("./scriptDynamic1.js")
.then(function(res){
res.init;
setTimeout(function(){
console.log("setTimeout executed");
console.log("finally resolving");
res(true);
},10000);
console.log("dynamic script 1 'then' executed");
})
/*Next then will not execute until the above is resolved*/
.then(function(res){
console.log("dynamic script 1 'then-2' executed");
});
/*Meanwhile the entire downstream thread will wait for these tasks to finish*/
```
- Uses async script tags
- Does not dictate anything about your app structure. Whether you want use separate async script tags, or bundle them.
- No modifying required to your existing scripts other than wrapping them around iief (immediately invoked function expression) and *pushing* them to the taskq object.

## API [⏎](#api)

There are some terms used throught out the documentation:

### Terms

- **iief**: immediately invoked function expression
- **main thread** : this refers to the list of functions pushed to the taskq object before calling *taskq.perform* method. This is called automatically on 'onload' event. Dynamically loaded scripts have their separete queue (immediateTasks) that is handled implicitly.
- **dynamic import/dynamic load** : this is to refer whenever you call *taskq.load* method to start loading scripts somehere within main thread (or outside later if you want). Everything you load is async but their execution can be controlled by you.
- **taskq**: this is the main taskq global object. Although you can change its name using the script attribute, its default is assumed here.
- **module pattern**: Although taskq only requires you to push the functions to be executed, to avoid leaking to global, a general module pattern is as follows:

```
/*outer iief*/
!function(){
function someFunctionYouWantToExecute (someArguments) {
/*some stuff like taskq.export, taskq.load*/
}
taskq.push(someFunctionYouWantToExecute);
}()
```

### Methods

> taskq.version()

Returns the version string.

> taskq.push(function)

Pushes the function to the main thread or the immediate thread (for dynamic imports) implicitly and return taskq it self, so you can do:

```
//Define functions
function f1(){...};
f1._taskqId = "f1";
function f2(){...};
f2._taskqId = "f2";
f2._taskqWaitFor = ["f1"];
function f3(){...};
f3._taskqId = "f3";
f3._taskqWaitFor = ["f2"];
//Push to queue
taskq.push(f1).push(f2).push(f3);

```
Pushed functions do not execute automatically, you will have to call *taskq.perform()* to start shifting and executing it. In your main HTML, perform is automatically called for you on 'onload' event.

If you push a variable that is not a function, it will be skipped and you will get a console message: "not a function ref".

> taskq.export(variable,aliasString)

Exports any type of variable with the given alias. These exported variables are available to the pushed functions. Suppose a *previouslyPushedFunction* in the main thread called *taskq.export({value:4},"someObject")*:

```
/*outer iief*/
!function(){
function someFunctionYouWantToExecute (someObject) {
/*someObject is available here*/
}
someFunctionYouWantToExecute.taskqWaitFor = ["previouslyPushedFunction"];
taskq.push(someFunctionYouWantToExecute);
}()
```

Arguments order does not matter.

Exported variables live until *taskq.perform* finishes executing all the functions in the main thread. If there are no more pointers somewhere else in your code, they can be garbage collected. Later you can repopulate the exports by calling *taskq.export* again. Next time you call perform, it will again clear and so on.

> taskq.load("./someScript.js")

Will pause the main thread, and start loading the given script. Its iief will be immediately executed and pushed functions will be added to the immediate queue to be executed. Returns a *thennable* object which you can attach then or catch clauses.

Other dynamic loads and the main thread will wait for this load to complete its iief, pushed functions and then/catch clauses.

> thennable.then(function(resolver){...})

Attaches the thennable a function to be executed, and return the thennable itself. Attached thens are executed in order. Functions within thens are passed an optional resolver argument. If you do not call *resolver.init;* , the next then clause will execute as soon as this then clause is executed. If you call *resolver.init;* , somewhere else within the current then clause you should call *resolver(true)* or *resolver(false)* to proceed to the next then.

When using resolver, the entire main thread and the rest of the then clauses will wait for it to resolve.

> thennable.catch(function(){...})

Attaches a catch clause to the thennable shall any of the thens resolve with a *falsey* value. Attaching multiple catch clauses overrides the previous one.

> resolver.init

Tells the current then clause to block rest of the thens and the main thread and wait until it is resolved.

> resolver(variable)

Converts the variable to "boolean" and resolves with that value. Returns always true unless you try to resolve more than once within the same then clause. You can only resolve once, resolving more than once does not have any effect -> only the first resolve value is recorded.

> resolve.value

Gives the boolean value the resolver resolved with. Cannot be set.

> taskq.pause;

Pauses the entire taskq main thread, thens etc. If any functions were called at the time pause was called such as pushed functions or setTimeout, they are executed and the rest is halted. When paused, taskq is still running but does not proceed.

> taskq.paused;

Returns true of false whether taskq is paused or not. Cannot be set manually.

> taskq.resume;

Resumes the taskq.

> taskq.running;

Returns true or false based on taskq running state. It will return false once the *taskq.perform()* method finished the main thread. If you start another main thread, it return true until perform completes again.

> taskq.perform()

Starts performing the pushed functions in the main thread. This is automatically called for you on the 'onload' event.

Later if you start another main thread by pushing functions to taskq, you should manually call this method in the end.

Perform will automatically clear all the exports once it is complete.

> taskq.flush("main"|"script")

This is automatically called by the *taskq.perform* method in the end. You normally should not call this method manually. If you pass "main" as argument, then all the pushed functions to the main thread and the exported variables will be cleared. If you pass "script", only the immediate tasks (pushed functions within dynamic imports) are cleared.

> taskq.minPause = Number

You can use this *setter* to set the minimum time in milliseconds between the execution of pushed functions in the main thread. You can also configure this by adding an "data-min-pause" attribute to the script tag of taskq.

## Reading [⏎](#reading)

I advise you to take a look at below Medium posts:
- [**Queued Async (pseudo-) modules with ES5**](https://medium.com/@ibowankenobi/queued-async-pseudo-modules-with-es5-812f99fed209)
- [**Pausing/resuming browser & app logic using Taskq.js**](https://medium.com/@ibowankenobi/pausing-resuming-browser-app-logic-using-taskq-js-884ec5a8ce86)
- [**Get that google PSI score higher**](https://medium.com/@ibowankenobi/get-that-google-psi-score-higher-28a7c992966e)

## What does it do? [⏎](#what-does-it-do)

This project has evolved to the degree that is now a full blown module system that can be used instead of ES6 import/export or other module proposals. Perhaps it can be better illustrated in answer I have written in Hashnode:

> ### [Modules in JavaScript confusion](https://hashnode.com/post/modules-in-javascript-confusion-cjfc32m6f000a0as2os9nhf75)

~~I was playing around with the idea of writing a module pattern for ES5, well not like a full-blown module definition but
a mini script to execute async scripts with clojures. So this will allow you to add async script tags and execute them with order
while not leaking references to the global scope. This is by no means a replacement for es6 module pattern/AMD/CommonJS and is experimental.~~

## Usage [⏎](#usage)

Take a look at this minimal html, which also is included in the repository:

```