1

I'm using Node.js and the Bluebird promises library.

This code works exactly the way I want:

/*
 * Try using Bluebird promisify():
 * - "Good" case: this works perfectly.  
 *   ... but it DOESN'T use "promisify()"; it creates a new promise for each function.
 * - SAMPLE OUTPUT:
 *     callABC()...
 *     a():  [ 'a' ]
 *     b():  [ 'a', 'b' ]
 *     c():  [ 'a', 'b', 'c' ]
 *     Done: results: [ 'a', 'b', 'c' ] 
 */
var Promise = require('bluebird');

var a = function (results) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      results.push("a");
      console.log("a(): ", results);
      resolve(results);
    }, 15);
  });
}

var b = function (results) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      results.push("b");
      console.log("b(): ", results);
      resolve(results);
    }, 5);
  });
}

var c = function (results) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      results.push("c");
      console.log("c(): ", results);
      resolve(results);
    }, 10);
  });
}


var callABC = function (results) {
  console.log("callABC()...");
  a(results)
  .then(b)
  .then(c)
  .then(function (results) {
    console.log("Done: results:", results);
  })
  .catch(function (err) {
    console.log("Error:", err);
  });
}

callABC([]);

I understand that manually instantiating your own promises like this can be considered "bad":

Q: How can I "promisify" the above snippet?

I've tried many things; none of them have worked. For example:

/*
 * Try using Bluebird promisify():
 * - Fails: never calls b() or c() 
 */
var Promise = require('bluebird');

var a = Promise.promisify(function (results) {
  setTimeout(function() {
    results.push("a");
    console.log("a(): ", results);
  }, 15);
});

var b = Promise.promisify(function (results) {
  setTimeout(function() {
    results.push("b");
    console.log("b(): ", results);
  }, 5);
});

var c = Promise.promisify(function (results) {
  setTimeout(function() {
    results.push("c");
    console.log("c(): ", results);
  }, 10);
});

var callABC = function (results) {
  console.log("callABC()...");
  a(results)
  .then(b)
  .then(c)
  .then(function (results) {
    console.log("Done: results:", results);
  })
  .catch(function (err) {
    console.log("Error:", err);
  });
}

callABC([]);

Q: What's the correct way to "promisify" the first example?

Q: In particular, how do I "resolve()" or "reject()" my callbacks if I substitute the automated Promise.promisify() or Promise.promisifyAll() for the manual new Promise()? I imagine "throw" invokes .catch(), but is there another (better?) mechanism?

Q: Are there any restrictions? For example, does the function need to have a callback parameter in order to be "promisified"?

Community
  • 1
  • 1
paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • 2
    `promisify` does not do magic. It only works for functions that take callbacks. See [How do I convert an existing callback API to promises?](http://stackoverflow.com/q/22519784/1048572) for good use cases. – Bergi May 03 '16 at 20:32
  • 1
    The correct way to solve this particular problem is to use `Promise.delay`, which is the promisified version of `setTimeout`. Try it. – Bergi May 03 '16 at 20:33
  • @Bergi: 1) This is the answer I was looking for "promisify does not do magic. It only works for functions that take callbacks". 2) Thank you for the link. 3) "setTimeout()" was just for test purposes; it has nothing to do with the actual question. 4) If you'd like to put your comment in an "answer", I'd be happy to "accept" it. – paulsm4 May 03 '16 at 23:17

1 Answers1

0

The answer is: "not all JS functions can be promisified". Specifically:

http://bluebirdjs.com/docs/api/promise.promisify.html

Promise.promisify)... returns a function that will wrap the given nodeFunction. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument.

So in the example above, "new Promise" is the correct approach: "Promise.promisify()" will not work.

paulsm4
  • 114,292
  • 17
  • 138
  • 190