1

I have the following code example.

var Promise = require('bluebird');

var model = function (object) {
    this.name = object.name;
};

model.prototype.download = function () {
    var self = this;
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, Math.random() * 100)
    });
};

model.prototype.process = function () {
    var self = this;
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log('processed: ', self.name);
            resolve();
        }, Math.random() * 100)
    });
};



var models = [new model({
    name: 'user',
    requires: ['company']
}), new model({
    name: 'address',
    requires: ['user', 'company']
}), new model({
    name: 'company'
})];

Promise.map(models, function (model) {
    return model.download()
        .then(function () {
            return model.process();
        });
});

The required output of this code is:

processed: company // 1rst, because company model has no dependencies
processed: user // 2nd, because user requires company
processed: address // 3rd, because address requires company and user

I need to manage somehow the dependencies. The model.process function should be triggered only when all the process functions of the model's required models have already been resolved.

It's just a small example, I have a lot of models with multiple dependencies.

I need to trigger the download functions synchronously, and trigger the process function as soon as possible. I can not wait all the downloads to be resolved and call process after.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
Adam
  • 4,985
  • 2
  • 29
  • 61
  • What is your exact problem? You want to use promise to handle the above situation? And please provide more information about 'I can not wait all the downloads to be resolved and call process after.' – Dnyanesh Jun 27 '16 at 13:34
  • Your code doesn't show anything about "the model's required models". – Niels Steenbeek Jun 27 '16 at 13:46
  • Hey @Dnyanesh, thanks for your reply, I've updated my example to make it more understandable. – Adam Jun 27 '16 at 13:51
  • @NielsSteenbeek, I've updated the example to make dependencies clear. – Adam Jun 27 '16 at 13:59

3 Answers3

2

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();
Tamas Hegedus
  • 28,755
  • 12
  • 63
  • 97
0

The async module will do the job: https://github.com/caolan/async#control-flow

Check the series, parallel and queue methods.

0

As @ZdravkoTatarski mentioned, the caolan async has a control flow method called "auto" that works as a dependency tree for tasks (similar to Gulp).

See below an usage sample adapted to your use case.

//Using Promises
async.auto({
    company: function(callback) {
        console.log('in company');
        // async code to get some data
        callback(null, 'company data');
    },
    user: ['company', function(callback) {
        console.log('in user');
        // async code to get some data
        callback(null, 'user data');
    }],
    address: ['user', 'company', function(results, callback) {
        console.log('in address');
        // async code to process address for user&company
        callback(null, {user: results.user, company: results.company, address: 'address data'});
    }]
}).then(results => {
    console.log('results = ', results);
    // results = {
    //     user: 'user data',
    //     company: 'company data',
    //     address: {user: 'user data', company: 'company data', address: 'address data'}
    // }
}).catch(err => {
    console.log('err = ', err);
});

Full documentation here Caolan Async .auto.

zabdude
  • 1
  • 1