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.

4
  • 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.' Commented Jun 27, 2016 at 13:34
  • Your code doesn't show anything about "the model's required models". Commented Jun 27, 2016 at 13:46
  • Hey @Dnyanesh, thanks for your reply, I've updated my example to make it more understandable. Commented Jun 27, 2016 at 13:51
  • @NielsSteenbeek, I've updated the example to make dependencies clear. Commented Jun 27, 2016 at 13:59

3 Answers 3

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();

Sign up to request clarification or add additional context in comments.

2 Comments

FYI, Your function delay() is already implemented as Promise.delay() in bluebird.
Great! Thank you @TamasHegedus!
0

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

Check the series, parallel and queue methods.

1 Comment

Thanks, but how does that handle the dependencies?
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.

Comments

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.