The promiseDAG()
function makes it easier to work with JavaScript’s promises.
It is a generalization of Promise.all
to directed acyclic graphcs (DAG’s).
The project is hosted on GitHub.
You can see it in action on the demo page.
This page tries to explain what the promiseDAG()
function does.
In JavaScript, promises are objects that represent the result of an asynchronous operation that may or may not have completed. If you are not familiar with promises, here are some good pages to read:
Here you’ll find promises on caniuse.com
(note: promiseDAG
uses arrow functions, which are currently not supported as widely as
promises are; in the future, promiseDAG
will stop using arrow functions for wider support).
A promise’s then
method can be used for convenient chaining of promises.
If multiple asynchronous tasks need to be completed sequentially,
with the output of one being fed as input to the next, Promise.prototype.then()
is ideal.
In the following snippet, three promises are chained (each just sleeps for a little while).
In other words, Promise.prototype.then()
is suited for this kind of situation (time in the diagram goes from left to right):
The standard
Promise.all()
in JavaScript constructs a promise from a list (well, iterable) of promises.
The constructed promise satisfies the following properties:
In the following snippet, three promises are started at the same time.
In other words, Promise.all()
is suited for this kind of situation (all the tasks are started at the same time):
The promiseDAG()
function generalizes both chaining of promises (using Promise.prototype.then()
) and simultaneous execution of promises (using Promise.all()
).
It allows the efficient execution of a network of tasks, or if you want, of a dependency graph of tasks.
In other words, promiseDAG()
is suited for the above kinds of situations, but also for this one:
More precisely, the function promiseDAG()
can be used to execute any directed acyclic graph
of asynchronous tasks (the DAG
stands for directed acyclic graph).
Let’s take the example network in the picture above. The tasks are
The snippet shows the code to run these tasks in the correct order, efficiently.
When this code is run, the following happens:
p
is resolved. It returns as value a list
containing the return values of each of the tasks, in order.p
gets rejected (and no new tasks
are started after this).The function promiseDAG
takes two arguments.
The function promiseDAG
returns a promise.
The promiseDAG
function does not guarantee that the tasks are executed in any particular order,
just that the order in which they are executed is a topological sorting of the dependency
graph given to it.
The tasks that you want to execute asynchronously using promiseDAG
need to be specified
as functions that return promises, not as promises. There is a very simple reason for this:
as soon as a promise is created, the asynchronous task has started (e.g. if you’re using fetch
to get data in the background, the creation of the promise starts the fetching).
Since we need to have the dependencies of a tasks resolved before we can start it,
promiseDAG
cannot accept promises as arguments (because it needs to be able to choose
when to start the task).
You’ll note in the code above that task 3 has two dependencies (task 1 and task 2),
and that the function task3
takes two arguments (value1
and value2
).
When task 1 and task 2 are finished, the function task3
is called to start the 3rd task,
and it gets the return values of the promises of task 1 and task 2 as arguments.
In general, the promise-creating function will be called with one argument for each of its dependencies. Each argument is the value returned by the promise corresponding to this dependency. Note that the order of the arguments is determined by the order in which the dependencies of a task are specified.
If you want to specify something as a dependency, but don’t need the corresponding value passed as an argument, it is best to specify this as the last dependency (or the last couple, if there are multiple), and just pretend that the return value doesn’t get passed to the function at all. Technically, your function will get called with too many arguments, but JavaScript ignores this.
Suppose you’re running a website with a video of the day. When a user visits the site, the following things need to happen:
The tasks and their interdependencies are illustrated here.
This snippet illustrates how to use promiseDAG()
to get these asynchronous tasks
executed efficiently.
Note that the async
keyword was used twice, in order to not have to construct
promises explicitly.