0

I'm trying to wrap my head around Promises (a common theme, I know). I have the basic ideas down but I'm having trouble implementing them.

I'm trying to build an object the exposes a set of functions based on the contents of a directory. I'm using bluebird to promisify the fs library. Then read in the files of the dir, build the objects, and return the result.

    var Promise = require('bluebird'),
    fs = Promise.promisifyAll(require('fs'));

    var services = {};

    return fs.readdirAsync('./path/to/file/')
        .each(function (filename) {
            //trim off the file extension and assign the export function
            services[filename.replace(/\.[^/.]+$/, "")] = function(request) {
                request.esbOperation = filename;
                otherFunctionCall(request);
            }
        })
        .then(function() {
            return {services: services};
        })
        .catch(function(err){
             console.log(err);
        });

I've tried a variety of things but the returned object usually looks like this:

Promise {
  _bitField: 167772160,
  _fulfillmentHandler0: undefined,
  _rejectionHandler0: 
   { services: 
      { function1: [Function],
        function2: [Function],
        function3: [Function],
      },
  _promise0: undefined,
  _receiver0: undefined }

How do I get the result in the fulfillment handler? How do I get it returning the resolved object instead of the Promise object (I've tried resolve/reject in various places but I'm doing it wrong)?

GForce
  • 21
  • 8
  • You don't. You return the promise (which is what you are seeing, as it's already working). That's what they're made for. You cannot immediately return a result that will be computed in the future. – Bergi Jan 25 '17 at 21:26
  • @Bergi Why's it in the rejection handler? – GForce Jan 25 '17 at 21:29
  • @GForce I couldn't say why your object is stored in the `_rejectionHandler0` property, but you shouldn't be trying to obtain values from the promise's internal properties. You should be calling `.then()` and/or `.catch()` on it to work with it. – JLRishe Jan 25 '17 at 21:31
  • @GForce Because Bluebird is ultra-careful about memory. You should not care about its internal representation. – Bergi Jan 25 '17 at 23:36
  • seems almost a duplicate of https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call – Jaromanda X Jan 26 '17 at 03:34

1 Answers1

0

You can't return the resolved object. That's the whole point. The data you're obtaining becomes available long after your function has already returned.

So your two main options are:

  1. Accept that you have a function that returns a promise (that's not a bad thing!). Consumers that want to get at the resolution value can call .then() on it.

  2. Have your function take a callback and have the promise within your function call it. This kind of defeats the purpose of using promises in the first place, but would make your function signature more "node-like":

function myFunction(callback) {
    var Promise = require('bluebird'),
    fs = Promise.promisifyAll(require('fs'));

    var services = {};

    fs.readdirAsync('./path/to/file/')
        .each(function (filename) {
        //trim off the file extension and assign the export function
            services[filename.replace(/\.[^/.]+$/, "")] = function(request) {
                request.esbOperation = filename;
                otherFunctionCall(request);
            }
        })
        .then(function() {
            callback(null, {services: services});
        }, function(err){
            callback(err);
        });
}

As Bergi has pointed out, since you're using Bluebird an improvement on that #2 option that still achieves the same ends is to use .asCallback instead of that .then.

A few things to note here:

  • In this option you'd still need a then() to get the {services: services} -object in the pipeline so that it can be passed to the callback
  • There's a return statement on the 6th line of the function. This is because .asCallback() returns a promise and it allows callers of your function to choose whether they want to use the returned promise or use a callback.
function myFunction(callback) {
    var Promise = require('bluebird'),
    fs = Promise.promisifyAll(require('fs'));

    var services = {};

    return fs.readdirAsync('./path/to/file/')
        .each(function (filename) {
        //trim off the file extension and assign the export function
            services[filename.replace(/\.[^/.]+$/, "")] = function(request) {
                request.esbOperation = filename;
                otherFunctionCall(request);
            }
            return {services: services};
        })
        .then(function() { return {services: services} })
        .asCallback(callback);
}
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • Part of my hangup is that the result is in the rejection handler. That makes me think I'm doing something wrong. – GForce Jan 25 '17 at 21:42
  • Since OP is using Bluebird, you should use `.asCallback(callback)` instead of doing that `then().catch()` things – Bergi Jan 25 '17 at 23:37
  • @GForce As I already said, you should stop worrying about the promise's internals and try actually using the thing. As far as I can see, you're not doing anything wrong. – JLRishe Jan 26 '17 at 04:00
  • @Bergi Thanks, I was not aware of that feature. – JLRishe Jan 26 '17 at 04:04