0

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?

1
  • I recently/ wrote an answer which shows you how to do generic composition of async functions using various techniques: stackoverflow.com/a/44251219/633183 – @Paul's answer is great, but I think you might still find this interesting ^_^ Commented Jun 8, 2017 at 17:28

3 Answers 3

1

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));
}
Sign up to request clarification or add additional context in comments.

1 Comment

It almost looks like the boilerplate for a Promise.all implementation ^_^
0

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 });
   });
}),
...

2 Comments

I think that's a great answer since my code is already using that way forward and I just need to modify a few things. Thanks!
np. You can also look at the Waterfall method if subsequent functions need to know how previous functions went.
0

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

1 Comment

just sharing some techniques i find helpful/interesting – happy to discuss if there's any questions ^_^

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.