JavaScript Promise Dependency Handling

This is an example how you can asynchronously traverse a directed acyclic graph, without evaluating the individual nodes multiple times. Be careful, cycles in the dependency graph cause a deadlock in this implementation.

function Model(name, requires) {
  this.name = name;
  this.requires = requires;
};


// this function is available as `Promise.delay` when using bluebird
function delay(x, v) {
  return new Promise(resolve => {
    setTimeout(() => { resolve(v); }, x);
  });
}

Model.prototype.process = function () {
  console.log('started processing: ', this.name);
  return delay(Math.random() * 100 + 100).then(() => {
    console.log('finished processing: ', this.name);
  });
};

function Processor(models) {
  this.processMap = {};
  this.models = models;
  
  models.forEach(m => {
    this.processMap[m.name] = {
      promise: null,
      model: m
    };
  });
}

Processor.prototype.processDependencies = function(model) {
  return Promise.all(model.requires.map(r => this.processByName(r)));
};

Processor.prototype.process = function(model) {
  const process = this.processMap[model.name];
  if (!process.promise) {
    process.promise = this.processDependencies(model)
      .then(() => model.process());
  }
  return process.promise;
};

Processor.prototype.processByName = function(modelName) {
  return this.process(this.processMap[modelName].model);
};


function test() {
  const models = [
    new Model('bottom', []),
    new Model('mid a', ['bottom']),
    new Model('mid b', ['bottom']),
    new Model('top', ['mid a', 'mid b'])
  ];
  
  const processor = new Processor(models);

  Promise.all(
    models.map(m => processor.process(m))
  ).then(allResults => {
    console.log("All process finished");
  }, e => {
    console.error(e);
  });
}

test();