1

I'm trying to get the following simplified code to finish and then move on synchronously:

factors.forEach(function(factor, index){
  Factor.findOrCreate({function_name: factor}).exec(function(){
    sails.log('success');
  })
});

How do I do it?


So far I've tried:

  • Putting lines 2-4 in a separate function and calling that from inside the loop
  • Using the async.js async.series() call

But while it will execute the calls (print 'success'), it'll find or create a Factor in the DB very infrequently, so it has something to do with the asynchronous nature of the code.

  • Using promises to wait until all findOrCreate() calls return, then continuing

    var promises = [];
    factors.forEach(function(factor, index){
      promises.push(Factor.findOrCreate({function_name: factor}));
    });
    Q.all(promises).then(function(){
      sails.log('success');
    });
    

I've tried a few versions of this one, but I still can't get it to print 'success' - This seems like the right way to do it, but I'm not quite sure what's wrong.

2
  • Do you think the "infrequent working" is related to the fact that the queries are processed in parallel, instead of sequentially? Commented Jul 22, 2015 at 0:16
  • That is my assumption, which is why I thought to try async.series() and promises. All tests I've done seem to enforce that notion. Commented Jul 22, 2015 at 0:59

3 Answers 3

2

A simple method of doing this without using async or promises would be to manually keep track of how many records have been processed:

var ctr = 0;
factors.forEach(function(factor, index){
    Factor.findOrCreate({function_name: factor}).exec(function(err, factorObj){
        if (err) errorHandler(err);
        ctr ++;
        if (ctr === factors.length) {
            successCallback();
        }
    });
});

successCallback() will be called only after all Factor objects have been created.

Update

Alternatively, you could try this to avoid looping altogether:

Factor.findOrCreate(factors).exec(function(err, factorObjArray){
    callback(err, factorObjArray);
});
Sign up to request clarification or add additional context in comments.

9 Comments

This also seems correct, but when I ran it, I got the same results as the other answer: 2/5: Doesn't print anything or alter the database. 3/5: Error (E_UNKNOWN) :: Encountered an unexpected error : Could not connect to MySQL: Error: Pool is closed. at afterwards (.../node_modules/sails-mysql/lib/connections/spawn.js:72:13) at .../node_modules/sails-mysql/lib/connections/spawn.js:40:7 at process._tickDomainCallback (node.js:463:13) Details: Error: Could not connect to MySQL: Error: Pool is closed.
The errors you mentioned point to a database connection error. Try restarting your database or closing all open connections.
It doesn't have to do with Sails. Either your pool size is too small or some other application is hogging the connections. Did you try again after restarting your database?
I did both but it's not behaving any differently :/
Are the other parts of your code dealing with the database working fine?
|
2

There is a simple way of doing this using the async.js library forEachOf method.

Ex :

var async = require('async');
var promises = [];
async.forEachOf(factors,function(factor, key, callback){
  Factor.findOrCreate({function_name: factor}).exec(function(result,err){
    if (err) {
      callback(err);
    } else {
      promises.push(result);
      callback();
    }
  });
}, function(err){
  if (err) {
    console.log("Error: "+err);
  } else {
    console.log(promises);
  }
});

1 Comment

I ran this five times with the following results: 2/5: Doesn't print anything or alter the database. 3/5: Error (E_UNKNOWN) :: Encountered an unexpected error : Could not connect to MySQL: Error: Pool is closed. at afterwards (.../node_modules/sails-mysql/lib/connections/spawn.js:72:13) at .../node_modules/sails-mysql/lib/connections/spawn.js:40:7 at process._tickDomainCallback (node.js:463:13) Details: Error: Could not connect to MySQL: Error: Pool is closed.
1

You need to use something like Q, promises. But in a proper asyncronous way.

var cb = function(factor) {
  var deferred = Q.defer();
  Factor.findOrCreate({function_name: factor})
    .exec(function(err, factorObj) {
      if(err) deferred.reject(err);
      deferred.resolve(factorObj);
    });
  return deferred.promise;
};

var promises = [];
Factor.forEach(function(f) {
  promises.push(cb(f));
});

Q.all(promises)
  .then(function() { // You can get the params too.
    console.log('WE ARE THE CHAMPIONS!!');
  })
  .fail(function() { // You can get the error params too.
    console.log('WE LOOSE');
  });

Hope it helps! Its only an example, Its been a few months since I stop using NodeJS and SailsJS (unfortunately).

1 Comment

Notice that sails does support promises, and you should avoid the deferred antipattern then.

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.