steppin
I don't find async's waterfall very useful because of it's requirement to pass on state to the next function. That makes the signature of each step unique, difficult to refactor, and error-prone.
async.waterfall uses eachOfSeries under-the-hood and so does this.
No external dependencies but it depends on (and bundles) async-es/eachOfSeries
under the hood.
Installing
npm install steppin --save
How it works
A task/step signature is constant and always (state, next)
. The signature of next is always (err, result)
and the contents of result become associated with the key passed to steppin.
var steppin = require('steppin');
function calculate(color, size, callback) {
setTimeout(() => callback(null, {
cost: 1299,
tax: 200,
total: 1499,
}));
}
steppin({
color: (state, next) => next(null, 'red'),
size: (state, next) => next(null, 'small'),
price: (state, next) => calculate(state.color, state.size, next),
}, (err, state) => {
console.log('state', JSON.stringify(state, null, 2));
});
Output:
{
"color": "red",
"size": "small",
"price": {
"cost": 1299,
"tax": 200,
"total": 1499
}
}
Optional input
let initialState = { currency: 'usd' };
steppin({}, initialState, callback)
No constraints on state
State is just a regular object and there is nothing stopping you from doing state.color = "red"
in a task instead of returning it to next.
However, if it's easily possible to avoid that you should. Avoiding that makes it easily understood what's happening, where everything is coming from, and how it can be reordered or refactored.
Why is this good
Because consistency is good and allows for other things. For example, with the consistency, you could easily implement a timeout wrapper around your tasks or any other wrapper at any point in the future:
var wrapInTimeout = (maxWait, step) => {
return (state, next) => {
var timeout = setTimeout(() => next(new Error('timeout')), maxWait);
var done = (err, result) => {
clearTimeout(timeout);
next(err, result);
};
step(state, done);
};
};
steppin({
one: wrapInTimeout(1000, (state, next) => {
thirdPartyServiceRequest(userId, next);
}),
})
Key ordering not guaranteed?
In theory, the hash passed to steppin does not have the execution order guaranteed. However, in practice, I'm unaware of any engines or environments that would not execute in the order defined.
If you're still concerned, you can refactor the above example into this and it'll work:
steppin([
(state, next) => next(null, 'red'),
(state, next) => next(null, 'small'),
(state, next) => calculate(state.color, state.size, next),
], (err, state) => {
console.log('state', JSON.stringify(state, null, 2));
});
The keys will be a string that is the index of the step in the array:
{
"0": "red",
"1": "small",
"2": {
"cost": 1299,
"tax": 200,
"total": 1499
}
}