Search code examples
javascriptnode.jsecmascript-6bluebirdes6-promise

What to return for Bluebird Promise?


happy new year!

I would be very thankful if anybody could give me a hint on an issue with Bluebird's promisify. Without further ado, the situation is as follows. I have this piece of code:

/**
* Set up /website endpoint.
* @param {Object} router A Koa router
*/
module.exports = function(router) {
/**
* POST /website
*
* Records the activity of following an external
* website URL.
*/
  router.post('/website', validator, (ctx, next) => co(function * () {
    var address = forwardedFor(ctx.request, ctx.request.headers);
    var orgId = /\/?([^\-]+).*/.exec(ctx.request.body.path)[1];

    var visit = {
      websiteUrl: ctx.request.body.url,
      path: ctx.request.body.path,
      clientIp: address.ip,
      userAgent: ctx.request.headers['user-agent'],
      organization: orgId
    };
    yield tracker.track('website_visit', visit);
    log.info('Tracked [%s] website visit from client %s@%s%s', visit.websiteUrl, address.ip, orgId, visit.path);

    ctx.status = 200;
    ctx.body = {
      success: true,
      timestamp: Date.now(),
      data: visit
    };
    return next();
  })());
};

which at certain point calls:

/**
* Register the `event` with the given `name`.
* @method track
* @param  {String} name  The name or type of the event to be saved.
* @param  {Object} event Payload to register as an event.
* @return {Promise}       A promise on tracking the event.
*/
function track(name, event) {
  var entity = seneca.make(DEFAULT_BASE, name, event);
  var save$ = Promise.promisify(entity.save$, { context: entity });
  return save$();
}

This finally calls:

module.exports = function phonecall() {
  var seneca = this;

  seneca.add('cmd:save,role:entity,name:website_visit', function(msg, respond) {
    var visit = JSON.parse(JSON.stringify(msg.ent.data$(false)));
    Organization.where('account_no', 'like', `%${visit.organization}%`)
      .fetch()
      .then(function(org) {
        return User
          .where('id_organization', org.get('id'))
          .fetch()
          .then(function(user) {
            delete visit.organization;
            var v = new WebsiteVisit(visit);
            user.websiteVisits().create(v);
            return user.save(null, {
              method: 'update'
            });
          });
      })
      .then((model) => {
        return {
          ok: true,
          model: model
        };
      })
      .catch((err) => {
        return {
          ok: false,
          why: err.message || err
        };
      }).asCallback(respond);
  });

  return {
    name: PLUGIN_NAME
  };
};

That is on a complete different application.

The main problem is that everything runs smoothly, and the model is inserted into the database. However, yield tracker.track('website_visit', visit); takes forever and doesn't behave asynchronously, which launches a timeout error for the already completed function.

The ideal behaviour for yield tracker.track('website_visit', visit); would be to operate on a different thread (asynchronously), as a normal promise should, but isn't acting as such...

Thanks in advance for any help provided.


Solution

  • The yield expression requires explicit instructions to continue execution. It's built to give you the ability to stop and start the execution of a process. In your case, this is doesn't seem to be needed. Unless I'm mistaken, it looks like all you need is to wait for the results of an asynchronous function before proceeding which is just standard use case for a Promise.

    Why not just use the same Promise architecture you're using in the phoneCall function?