Reputation: 29458
I just started with nodejs and I basically have two functions I want to execute and based on the success of each of those functions, or error of those functions, I want the wrapper function to generate an overall status code. I looked at the async package and I got it working in the happy path where everything succeeds and my last callback gets a success. But what if one fails? I see that the error gets sent to the last callback of async.waterfall, but the second function never gets run since there was an error and I do want to know the results of both functions. I've tried, series, parallel, and waterfall, and from what I can tell, they all call the final callback once an error is hit. Is there an idiomatic way to do this in Node?
Upvotes: 0
Views: 2486
Reputation: 135217
Given a generic asynchronous function ...
const double = (x, k) =>
k (x * 2)
double (1, console.log)
// 1 * 2
// => 2
compk
We can compose two asynchronous functions using compk
const double = (x, k) =>
k (x * 2)
const compk = f => g =>
(x, k) =>
f (x, y => g (y, k))
compk (double) (double) (1, console.log)
// (1 * 2) * 2
// 2 * 2
// => 4
composek
We can compose N asynchronous functions using composek
const double = (x, k) =>
k (x * 2)
const compk = f => g =>
(x, k) =>
f (x, y => g (y, k))
const composek = (f, ...fs) =>
(x, k) =>
f === undefined
? k (x)
: compk (composek (...fs)) (f) (x, k)
composek (double, double, double, double, double) (1, console.log)
// ((((1 * 2) * 2) * 2) * 2) * 2
// (((2 * 2) * 2) * 2) * 2
// ((4 * 2) * 2) * 2
// (8 * 2) * 2
// 16 * 2
// => 32
node-style continuation passing style
Node.JS has a convention where it uses error-first continuation passing style. If we want compose node-style cps functions and properly bubble the errors, a little more care needs to be applied
const nodedouble = (x, k) =>
x > 10
? k (Error('cannot double numbers over 10'))
: k (null, x * 2)
const nodedebug = (err, x) => {
if (err)
console.error('Error', err.message)
else
console.log(x)
}
nodedouble (2, nodedebug)
// 2 * 2
// => 4
nodedouble (12, nodedebug)
// 12 * 2
// => Error: cannot double numbers over 10
nodecompk
We can compose two node-style cps functions using nodecompk
const nodedouble = (x, k) =>
x > 10
? k (Error('cannot double numbers over 10'))
: k (null, x * 2)
const nodedebug = (err, x) => {
if (err)
console.error('Error', err.message)
else
console.log(x)
}
const nodecompk = f => g =>
(x, k) =>
f (x, (err, y) =>
err ? k (err) : g (y, (err, z) =>
err ? k (err) : k (null, z)))
nodecompk (nodedouble) (nodedouble) (1, nodedebug)
// (1 * 2) * 2
// 2 * 2
// => 4
nodecompk (nodedouble) (nodedouble) (6, nodedebug)
// (6 * 2) * 2
// 12 * 2
// => Error: cannot double numbers over 10
nodecomposek
We can compose N node-style cps functions using nodecomposek
const nodedouble = (x, k) =>
x > 10
? k (Error('cannot double numbers over 10'))
: k (null, x * 2)
const nodedebug = (err, x) => {
if (err)
console.error('Error', err.message)
else
console.log(x)
}
const nodecompk = f => g =>
(x, k) =>
f (x, (err, y) =>
err ? k (err) : g (y, (err, z) =>
err ? k (err) : k (null, z)))
const nodecomposek = (f,...fs) =>
(x, k) =>
f === undefined
? k (null, x)
: nodecompk (nodecomposek (...fs)) (f) (x, k)
nodecomposek (nodedouble, nodedouble, nodedouble) (2, nodedebug)
// ((2 * 2) * 2) * 2
// (4 * 2) * 2
// 8 * 2
// => 16
nodecomposek (nodedouble, nodedouble, nodedouble) (8, nodedebug)
// ((8 * 2) * 2) * 2
// (16 * 2) * 12
// => Error cannot double numbers over 10
Upvotes: 0
Reputation: 36319
The other answers will work fine, though I think they're not the way I'd do it.
Given that you're already interested in AsyncJS, I would consider using it still, but simply not propagating the error directly. Swallow the error and have the data part of the callback represent whatever status you want to convey, as your callback is effectively handling it.
For example, let's say you have a function that's part of your async series or parallel call:
...
function getModel((cb) => {
model.find({_id: 1}, (err, data) => {
if (err) return cb(null, { status: 500, message: 'Database connection error' });
if (!data) return cb(null, { status: 404, message: 'model 1 not found' });
return cb(null, { status: 200, model: data });
});
}),
...
Upvotes: 0
Reputation: 92521
Try this:
const doTwoThings = callback => {
let done = 0;
const results = [];
const check = n => (err, result) => {
done++;
if (!err) results[n] = result;
if (done === 2) callback(results);
}
doFirstThing(args, check(0));
doSecondThing(args, check(1));
}
Upvotes: 1