laminatefish
laminatefish

Reputation: 5246

Promise confusion using node (ES6) and Bluebird

I had an issue with values being assigned prior to an async call being complete, so I opted to use Promises (in this instance: Bluebird). Which I thought would solve the problem, but seeing as I'm not at all familiar with promises / or node for that matter, it's still doing the same thing.

How would I go about modifying my code below, to use promises correctly? And maintain a flow, so I can validate modules based upon a previous row ?

var Promise         = require('bluebird');
var db              = require('../shared/db');
var queryHelper     = require('../shared/queryHelper');
var schemas         = require('../schemas/schemas');
var _               = require('lodash');
var serverSettings  = require('../shared/coreServerSettings');

// Create new instance of mysql module
var connection      = new db();
var queryhelper     = new queryHelper();

// Promised methods
var queryAndWait = Promise.promisify(connection.query);

..snip..

queryAndWait(query + '; ' + queryFoundRows, params).then(function (result) {
  var payload = [];
  var site_access = [];
  var module_access = [];

  _.each(result[0], function (row) {
    var sites = row.site_access.split(',');
    _.each(sites, function (site_id) {
      site_access.push(site_id);
    });

    row.site_access = site_access;
    site_access = [];

    queryAndWait(queryModules, [row.priv_id]).then(function (result) {
      _.each(result, function (module) {
        var modulePriv = {
          priv_name: module.priv_name,
          priv_num: module.priv_num,
          enabled: module.enabled
        };

        module_access.push(modulePriv);
      });

      //console.log("am assigning " + JSON.stringify(module_access));
      row.module_access = module_access;
      module_access = []
    });

    payload.push(row);
  });
});

To clarify:

Sorry, modules are groups of products that users (row) have access to, which are calculated on the fly (bitwise - hence no join available and a separate query), the module query returns something like the following, which I then loop through and attach to the row prior to returning the payload:

[{"priv_name":"INTERACT","priv_num":1,"enabled":1},{"priv_name":"STAFF_ADMIN","priv_num":32,"enabled":1},{"priv_name":"INT_EDIT","priv_num":64,"enabled":0},{"priv_name":"FAILED_LOGIN","priv_num":128,"enabled":0},{"priv_name":"INT_TAGS","priv_num":256,"enabled":0},{"priv_name":"NC","priv_num":512,"enabled":0},{"priv_name":"CC_TIMETABLE_MGR","priv_num":1024,"enabled":0}]

Upvotes: 1

Views: 127

Answers (2)

Jesús Quintana
Jesús Quintana

Reputation: 1813

Promise help you to control de flow of the application in a simple way. I encourage you to read the basic principles about promise, at least the Promise A+ specification.

Your code could be written in many ways this is the way that I recommend because is modular:

var Promise         = require('bluebird');
var queryAndWait = Promise.promisify(connection.query);

function getRows(params) {
  return queryAndWait(query + '; ' + queryFoundRows, params)
          .then(function (result) {
            var site_access = [];
            _.each(result[0], function (row) {
              var sites = row.site_access.split(',');
              _.each(sites, function (site_id) {
                site_access.push(site_id);
              });
              row.site_access = site_access;
              site_access = [];
              return row;
            });
          });
}


function getModules(row) {
 return queryAndWait(queryModules, [row.priv_id])
         .then(function (result) {
          var payload = [];
          var module_access = [];
          _.each(result, function (module) {
            var modulePriv = {
              priv_name: module.priv_name,
              priv_num: module.priv_num,
              enabled: module.enabled
            };

            module_access.push(modulePriv);
          });              
          row.module_access = module_access;

          payload.push(row);
          return payload;
        });
}

getRows(params)
  .then(getModules)
  .then(function(payload) {
    // Do something with your payload
  })

I did the next:

  1. Separate each logic in his own function (Why?, Because you can reuse logic in another promise, export methods in another libraries, etc.)
  2. I return the promise in each function. The promise can be nested and are executed in order.
  3. Each intern promise resolve a value. getRows -> resolve rows, getModules -> resolve a payload

You can also manage the erros using the catch method of promises, for example:

getRows(params)
  .catch(function(err) {
    // Something went wrong with getRows
    // If i can't recovery must return a reject.
  })
  .then(getModules)
  .catch(function(err) {
    //Something went wrong with getModules
    // If i can't recovery must return a reject.
  })
  .then(function(payload) {
    // Do something with your payload
  })

Upvotes: 1

code-jaff
code-jaff

Reputation: 9330

yes, the inner queryAndWait will run independently for each row as you don't chain the promises.

Since you use bluebird you'll need something like this to maintain the flow

queryAndWait(query + '; ' + queryFoundRows, params).then(function (result) {
    return Promise.map(result[0], function (row) {
        var site_access = [];
        var module_access = [];

        var sites = row.site_access.split(',');
        _.each(sites, function (site_id) {
            site_access.push(site_id);
        });

        row.site_access = site_access;

        return queryAndWait(queryModules, [row.priv_id]).then(function (result) {
            _.each(result, function (module) {
                var modulePriv = {
                    priv_name: module.priv_name,
                    priv_num: module.priv_num,
                    enabled: module.enabled
                };

                module_access.push(modulePriv);
            });
            row.module_access = module_access;
            return row;
        });
    });
}).then(function(payload) {
    console.log(payload);
}).catch(function(error) {
    console.log(error);
});

Upvotes: 1

Related Questions