Emile Paffard-Wray
Emile Paffard-Wray

Reputation: 1116

Create asynchronous waterfall from objects

Say I have an array of objects which have asynchronous methods:

[
  {
    partOne: function(input) {
      // Do something async
    },
    partTwo: function(result) {
      // Do something w/ result of partOne
    }
  },
  {
    partOne: function(resultOfPrevious) {
      // Do something async
    },
    partTwo: function(result) {
      // Do something w/ result of partOne
    }
  },
  {
    partOne: function(resultOfPrevious) {
      // Do something async
    },
    partTwo: function(result) {
      // Do something w/ result of partOne
    }
  }
]

I want to execute partOne of the first object with my input, pass the result (async) to the partTwo callback, then pass the result of partTwo as input to partOne of the next object and so on. The array may be of one or more objects. I'm wondering what the best pattern to execute this kind of code is?

It is somewhat similar to the waterfall method of async.js: https://caolan.github.io/async/docs.html#waterfall, but I wonder how I can do this without a library and possibly with cleaner code?

Not sure if async/await might help here?

Upvotes: 0

Views: 604

Answers (3)

Shardj
Shardj

Reputation: 1969

Assuming your array of objects given in the original question is under a variable called waterfall

let collector = [];
for (waterfallObj of waterfall) {
  let tempArr = Object.values(waterfallObj);//get the functions out of the object
  for (waterfallFunc of tempArr) {
    collector.push(waterfallFunc);
  }
}
//now you have your functions in order in collector
function recursiveCallback(i) {
  if (i>collector.length-1) {
    return;//if there are no more to call then return
  }

  collector[i]().then(function(){
    recursiveCallback(i+1);
  });
}

If you want the next function to do something with the previous functions value then simply change the then to then(function(passedValue and then use that passedValue in the recursiveCallback call within it

Upvotes: 0

kharhys
kharhys

Reputation: 257

Here is a simple function to invoke each asynchronous function in a stack

function drain(stack, initialArg) {
  stack.reduce(function (sectArg, section) {
    return Object.keys(section).reduce(async function (arg, key) { 
      return await section[key].call(null,arg)
    }, sectArg) 
  }, initialArg)
}

To use it ensure that each function in you stack returns a value

var stack = [
  {
    partOne: function(input) {
      // Do something async
      console.log('section one  partOne', input)
      return 'section one partOne output'
    },
    partTwo: function(result) {
      // Do something w/ result of partOne
      console.log('section one  partTwo', result)
      return 'section one partTwo output'
    }
  },
  {
    partOne: function(resultOfPrevious) {
      // Do something async
      console.log('section two  partOne', resultOfPrevious)
      return 'section two partOne output'
    },
    partTwo: function(result) {
      // Do something w/ result of partOne
      console.log('section two  partTwo', result)
      return 'section two  partTwo output'
    }
  },
  {
    partOne: function(resultOfPrevious) {
      // Do something async
      console.log('section three  partOne', resultOfPrevious)
      return 'section three partOne output'
    },
    partTwo: function(result) {
      // Do something w/ result of partOne
      console.log('section three  partTwo', result)
      return 'section three partTwo output'
    }
  }
]

So that you can invoke the stack like

drain(stack, 'initialArg')

See this jsfiddle: https://jsfiddle.net/kqj0rror/

Upvotes: 1

Are
Are

Reputation: 2241

Another option without collecting every callback to an array, using async/await:

async function processWaterfallObject (data, input) {
  let result = input

  for (let entry of data) {
     result = await entry.partOne(result)
     result = await entry.partTwo(result)
  }

  return result
}

This assumes that functions in your data array are either async or return a Promise.


async/await is currently supported by every major browser and is available in node since 7.6.0.

Upvotes: 2

Related Questions