Crystal
Crystal

Reputation: 29458

call multiple functions nodejs

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

Answers (3)

Mulan
Mulan

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

Paul
Paul

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

Michał Perłakowski
Michał Perłakowski

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

Related Questions