promise-dag
Declarative, modular, minimal-latency asynchronous computations usings graphs of Promises.
Features:
- Express complex async workflows using a high-level data structure (instead of wiring promises by hand using
Promise.all()
etc.) - No dependencies. In particular, no dependency to a concrete implementation of Promises; you can plug in any Promise implementation (es6-promise, Q, bluebird, when.js, Angular's $q...)
- Lightweight (<1k minified and gzipped)
- Lazyness: the only steps which are computed are those you require (and those on which they depend).
- Automatic minimum latency: chaining promises by hand often yields suboptimal performance,
because some steps are run serially which could be run in parallel.
promise-dag
always chains promises so as to minimize latency.
Installation
This library is available on NPM as promise-dag
:
$ npm install promise-dag --save
Node / ES6
var promiseDag = ;
Browser
A browser version is available in js/browser/promise-dag(.min).js
.
It exports a global promiseDag
in window
.
Usage
var promiseDag = ; // describe the computation as steps which depend on previous steps.// in this cooking example, functions which return a promise are prefixed with `p_`.var mushroomPastaRecipe = hotWater: { return ; } rawPasta: { return ; } cookedPasta: 'hotWater''rawPasta' { return ; } slicedOnion: { return ; } slicedMushroom: { return ; } friedOnionAndMushroom: 'slicedOnion''slicedMushroom' { return ; } cream: { return ; } sauce: 'friedOnionAndMushroom''cream' { return ; } meal: 'cookedPasta' 'sauce' mix; // returns an object mapping each step name to a promise of the completed steppromiseDag;// {hotWater: Promise{...}, rawPasta: Promise{...}, ...} // you can specify which output steps you want to return. Only the dependencies of those steps will be run.promiseDag;// {cookedPasta: Promise{...}, sauce: Promise{...}} // Example 1: eating the whole mealpromiseDagmeal; // Example 2: if you are lazy, you can just cook the pasta. The rest of the cooking won't occur.promiseDagcookedPasta; // Example 3: if you don't feel like cooking the sauce yourself, you can buy it at the store insteadvar lazyRecipe = _;promiseDagmeal;
Using with non-standard Promise implementations
promiseDag.run()
will look for a standard Promise implementation by assuming a global Promise
object.
In environments where this implementation is not available / desirable, you can plug in any other Promise implementation using promiseDag.implement()
;
all you have to do is provide the Promise.resolved()
, Promise.reject()
and Promise.all()
functions.
promiseDag.implement()
returns a function which has the same behaviour as promiseDag.run
.
Here are some examples:
Bluebird
var Promise = ; var runBluebird = promiseDag
$q
Angular 1.x var run$q = promiseDag;
Q
var Q = ; var runQ = promiseDag;
when.js
var when = ; var runWhen = promiseDag;
Why a new library?
Prior to promise-dag
, a few libraries already existed which let you express async workflows as DAGs of promises,
see for example dagmise and qryq.
I was dissatisfied with these alternatives for the following reasons:
- They impose a concrete Promise implementation.
promise-dag
is compatible with any Promise implementation, without any code dependency. - They provide a fluent API (chaining methods).
I prefer a data-oriented API, which is more programmable and transparent, making it easy to combine computation graphs using ordinary
data-structure functions (
_.extend()
,_.pick()
, ...), as well as add custom instrumentation (profiling / tracing / logging / etc.).
Inspiration
ngRoute
's resolve clauses- Plumatic's Graph