I want to promisify node-postgres' pg.connect method along with the inner connection.query method provided in the callback.
I can .promisify the latter, but I need to implement the first one manually (if I'm missing something here, please explain).
The thing is, I'm not sure if this code is correct or should be improved? The code is working, I just want to know if I'm using Bluebird as meant.
// aliases
var asPromise = Promise.promisify;
// save reference to original method
var connect = pg.connect.bind(pg);
// promisify method
pg.connect = function (data) {
var deferred = Promise.defer();
connect(data, function promisify(err, connection, release) {
if (err) return deferred.reject(err);
// promisify query factory
connection.query = asPromise(connection.query, connection);
// resolve promised connection
deferred.resolve([connection,release]);
});
return deferred.promise;
};
Throw all that horrible callback code away, then do this somewhere in your application initialization:
var pg = require("pg");
var Promise = require("bluebird");
Object.keys(pg).forEach(function(key) {
var Class = pg[key];
if (typeof Class === "function") {
Promise.promisifyAll(Class.prototype);
Promise.promisifyAll(Class);
}
})
Promise.promisifyAll(pg);
Later in anywhere you can use the pg module as if it was designed to use promises to begin with:
// Later
// Don't even need to require bluebird here
var pg = require("pg");
// Note how it's the pg API but with *Async suffix
pg.connectAsync(...).spread(function(connection, release) {
return connection.queryAsync("...")
.then(function(result) {
console.log("rows", result.rows);
})
.finally(function() {
// Creating a superfluous anonymous function cos I am
// unsure of your JS skill level
release();
});
});
By now there are a number of libraries which do this for you:
pg-promise - generic Promises/A+ for PG
postgres-bluebird
dbh-ph
pg-bluebird
Update for bluebird 3:
The pg.connectAsync(...).spread(function(connection, release) { ... }) call will not work anymore, because the API of bluebird has changed: http://bluebirdjs.com/docs/new-in-bluebird-3.html#promisification-api-changes .
The problem is that promisifyAll in bluebird 3 does not handle multiple arguments by default. This results in the .spread() call reporting a TypeError like the following:
TypeError: expecting an array or an iterable object but got [object Null]
To solve this, you can explicitly enable multiple arguments for connect / connectAsync. Do the following after all the promisifying stuff mentioned above:
...
pg.connectAsync = Promise.promisify(pg.connect, { multiArgs: true });
I suggest to modify Petka Antonov solution a bit
var Promise = require('bluebird');
var pg = require('pg');
Object.keys(pg).forEach(function (key) {
var Cls = null;
try {
Cls = pg[key];
if (typeof Cls === 'function') {
Promise.promisifyAll(Cls.prototype);
Promise.promisifyAll(Cls);
}
} catch (e) {
console.log(e);
}
});
Promise.promisifyAll(pg);
here 'pg[key] wrapped up in try-catch block because pg[key] can retrun error when attempt to access pg['native']
Related
I am trying to refactory my nodejs server using promises with Bluebird library, but I am stuck in a simple problem.
After to get the users from my db, I want to list all notification class associated with this user:
Bad Way (working...)
adapter.getUsers(function(users){
users.rows.forEach(function(item){
user = item.username;
adapter.getNotifications(user, function(notificationList){
console.log(notificationList);
})
});
});
Elegant Tentative Way (not working...)
var getNotifications = Promise.promisify(adapter.getNotifications);
adapter.getUsers().then(function(users) {
users.rows.forEach(function(item){
var dbUser = "sigalei/" + item.value.name;
console.log(dbUser);
return getNotifications(dbUser);
});
}).then(function(result){
console.log(result);
console.log("NOTIFICATIONLIST");
});
However when I execute this code I get this error inside my getNotification method:
Unhandled rejection TypeError: Cannot read property 'nano' of undefined
at Adapter.getNotifications (/Users/DaniloOliveira/Workspace/sigalei-api/api/tools/couchdb-adapter.js:387:30)
at tryCatcher (/Users/DaniloOliveira/Workspace/sigalei-api/node_modules/bluebird/js/main/util.js:26:23)
EDIT
After the user2864740`s precious comments, I noticed that the error is related with some scope problem. So, why after to use promisify method, the method dont getNotifications recognize the "this" env variable?
var Adapter = module.exports = function(config) {
this.nano = require('nano')({
url: url,
request_defaults: config.request_defaults
});
};
Adapter.prototype.getNotifications = function(userDb, done) {
var that = this;
console.log(that);
var userDbInstance = that.nano.use(userDb);
userDbInstance.view('_notificacao', 'lista',
{start_key: "[false]", end_key: "[false,{}]"},
function(err, body) {
if(err){ done(err); }
done(body);
});
};
This is just the very common problem of calling "unbound" methods.
You can pass the context as an option to Promise.promisify to have it bound:
var getNotifications = Promise.promisify(adapter.getNotifications, {context: adapter});
Alternatively, you'd need to .bind() the method, or call the new getNotifications function on the adapter (using .call()). You might also consider using Promise.promisifyAll(adapater) and then just calling adapter.getNotificationsAsync(…).
Notice that this still doesn't work. You cannot simply create promises in a loop - you need to await them explicitly and return a promise from the then callback, otherwise just the undefined value you returned will be passed to the next callback immediately.
adapter.getUsers().then(function(users) {
return Promise.all(users.rows.map(function(item){
var dbUser = "sigalei/" + item.value.name;
console.log(dbUser);
return getNotifications(dbUser);
}));
}).then(function(results) {
for (var i=0; i<results.length; i++)
console.log("result:", results[i]);
});
Instead of Promise.all(users.rows.map(…)), in Bluebird you can also use Promise.map(users.rows, …).
What about simply
var getNotifications = Promise.promisify(adapter.getNotifications.bind(adapter));
or possibly
var getNotifications = Promise.promisify(function () {
return adapter.getNotifications.apply(adapter, arguments);
});
?
I'm not sure I understand your problem well, but this should make sure this is bound and not undefined when you do return getNotifications(dbUser);
For a library that supports any promise library, yet wants to know when Bluebird is specified, in which way should it verify for it? -
that would be simple and reliable
that would not likely break with later versions of Bluebird
var promise = require('any-promise-library');
function isBluebird(lib) {
// do the checks
}
console.log(isBluebird(promise));
_promise0 is internal value in BlueBird library and author of that library also uses that property to check if passed object is native promise or blueBird one.
var Blue = require("bluebird");
var promise = new Blue(function() {})
function isBluebird(obj) {
try {
return {}.hasOwnProperty.call(obj, "_promise0");
} catch (e) {
return false;
}
}
console.log(isBluebird(promise))
or, if you want to test library ...
var Blue = require("bluebird");
function isBluebird(promiseLibrary) {
var promise = new promiseLibrary(function() {})
try {
return {}.hasOwnProperty.call(promise, "_promise0");
} catch (e) {
return false;
}
}
console.log(isBluebird(Blue))
For future breaking changes - please be in contact with author - as internal method isAnyBluebirdPromise is internal - it can be changed with or without any reason
If you have access to Bluebird (that is if you can require it)
This is a check that "aPromise" is the same (version of) bluebird as the one available to you:
function isBluebird(aPromise) { // aPromise is the class of Promise, not an instance
var B;
try { var B = require ('bluebird') } catch(e) {}
if (!B) return false; // bluebird must be installed
return aPromise.getNewLibraryCopy === B.getNewLibraryCopy
}
Currently this also works if aPromise is the result of getNewLibraryCopy. But that is not guaranteed.
If you want the result of getNewLibraryCopy to be seen as different, compare aPromise.resolve === B.resolve
If you want to test a promise instance, you can compare aPromise.then === B.resolve().then. But that only works for the unmodified Bluebird. Intances of a getNewLibraryCopy promise will be different.
I have an unknown number of async processes that might run from a request. There is a block of text to be modified by these processes.
UpdateScript is called with the text to be modified, it has a callback that I would like to run when everything is complete.
var promise = require('bluebird');
function updateScript(text, cb){
var funcChain = [],
re = some_Regular_Expression,
mods = {text: text};
while (m = re.exec(mods.text)) {
// The text is searched for keywords. If found a subprocess will fire
....
funcChain.push( changeTitleAsync(keyword, mods) );
}
promise.all(funcChain)
.then(function(){
// This is never called.
cb(mods.text);
});
}
function changeTitle(encryptedId, mods){
try{
// database request modifies mods.text
}catch(e){
throw e;
}
}
var changeTitleAsync = promise.promisify(changeTitle);
The changeTitle code is called but the "then" call is not
Partly the problem is incorrect use of promisify(). This is meant to convert a node-style function that takes a callback as it's last argument into one that returns a promise (see docs here).
Instead what you can do with your function above is have it return a new Promise manually like this:
function changeTitle(encryptedId, mods) {
return new Promise(function(resolve, reject){
try {
// do something then resolve promise with results
var result = ...
resolve(result)
} catch (e) {
// reject the promise with caught error
reject(e)
}
})
}
HOWEVER
There is one mistake above: I assume the db call to update the text is also asynchronous, sothe try /catch block will never catch anything because it will run through just as the db kicks off intially.
So what you would have to do is promisify the DB call itself. If you're using a node db library (like Mongoose, etc) you could run Promise.promisifyAll() against it, and use the Async versions of the functions (see promisifyAll section on above link for details).
Hope this helps!!
I'm trying to avoid using callbacks when making mongodb queries. I'm using mongoskin to make calls like so:
req.db.collection('users').find().toArray(function (err, doc) {
res.json(doc);
});
In many cases I need to make multiple queries so I want to use Node.js promise library but I'm not sure how to wrap these functions as promises. Most of the examples I see are trivial for things like readFile, I'm guessing in this case I would need to wrap toArray somehow? Can this be done or would have to be something implemented by mongoskin?
An example could be any set of callbacks, find/insert, find/find/insert, find/update:
req.db.collection('users').find().toArray(function (err, doc) {
if (doc) {
req.db.collection('users').find().toArray(function (err, doc) {
// etc...
});
}
else {
// err
}
});
You can promisify the entire module like so with bluebird:
var Promise = require("bluebird");
var mongoskin = require("mongoskin");
Object.keys(mongoskin).forEach(function(key) {
var value = mongoskin[key];
if (typeof value === "function") {
Promise.promisifyAll(value);
Promise.promisifyAll(value.prototype);
}
});
Promise.promisifyAll(mongoskin);
This only needs to be done in one place for one time in your application, not anywhere in your application code.
After that you just use methods normally except with the Async suffix and don't pass callbacks:
req.db.collection('users').find().toArrayAsync()
.then(function(doc) {
if (doc) {
return req.db.collection('users').find().toArrayAsync();
}
})
.then(function(doc) {
if (doc) {
return req.db.collection('users').find().toArrayAsync();
}
})
.then(function(doc) {
if (doc) {
return req.db.collection('users').find().toArrayAsync();
}
});
So again, if you call a function like
foo(a, b, c, function(err, result) {
if (err) return console.log(err);
//Code
});
The promise-returning version is called like:
fooAsync(a, b, c).then(...)
(Uncaught errors are automatically logged so you don't need to check for them if you are only going to log it)
Just stumbled here with the same question and didn't love "promisfying" mongoskin so did a bit more digging and found monk. It's built on top of mongoskin, tidies up the API and returns
promises for all async calls. Probably worth a peek to anyone else who lands here.
Esailija's answer may work, but its not super efficient since you have to run db.collection on every single db call. I don't know exactly how expensive that is, but looking at the code in mongoskin, its non-trivial. Not only that, but it's globally modifying prototypes, which isn't very safe.
The way I do this with fibers futures is:
wrap the collection methods for each collection
on receiving the result, for methods that return a Cursor wrap the toArray method, call it and return the resulting future (for methods that don't return a cursor, you don't need to do anything else).
use the future as normal
like this:
var Future = require("fibers/future")
// note: when i originally wrote this answer fibers/futures didn't have a good/intuitive wrapping function; but as of 2014-08-18, it does have one
function futureWrap() {
// function
if(arguments.length === 1) {
var fn = arguments[0]
var object = undefined
// object, methodName
} else {
var object = arguments[0]
var fn = object[arguments[1]]
}
return function() {
var args = Array.prototype.slice.call(arguments)
var future = new Future
args.push(future.resolver())
var me = this
if(object) me = object
fn.apply(me, args)
return future
}
}
var methodsYouWantToHave = ['findOne', 'find', 'update', 'insert', 'remove', 'findAndModify']
var methods = {}
methodsYouWantToHave.forEach(function(method) {
internalMethods[method] = futureWrap(this.collection, method)
}.bind(this))
// use them
var document = methods.findOne({_id: 'a3jf938fj98j'}, {}).wait()
var documents = futureWrap(methods.find({x: 'whatever'}, {}).wait(), 'toArray')().wait()
If you don't want to use fibers, I'd recommend using the async-future module, which has a good wrap function built in too.
I am trying to wrap the node-memcached api with deferred's promisify in order to simplify my nested callbacks.
When I try to call the promisified function I just get "TypeError: Cannot read property 'namespace' of undefined".
Memcached = require('memcached');
var memcache = new Memcached('localhost:11211');
var add = deferred.promisify(memcache.add);
add('myKey', 'myVal', 0)(function(result) {
...
});
I can't seem to find anyone else trying to wrap node-memcached, or getting my same error. Any insight into what may be going wrong? Or maybe even a push into a better direction if this is imperfect?
Thanks!
EDIT::
Just wanted to response that I found the best solution I could for now by doing some digging.
It seems that deferred.promisify calls the passed function with its own scope (this), instead of the context of the function that is passed in.
Using my own promisfy function appears to fix the issue (idea from http://howtonode.org/promises):
function promisify(fn, context) {
return function() {
var def = deferred();
var args = Array.prototype.slice.call(arguments);
args.push(function(err, val) {
if (err !== null) {
return def.reject(new Error(err));
}
return def.resolve(val);
});
fn.apply(context, args);
return def.promise;
};
}
When promisify instances members you should bind it to this instance like:
Memcached = require('memcached');
var memcache = new Memcached('localhost:11211');
var add = deferred.promisify(memcache.add.bind( memcache ) );
add('myKey', 'myVal', 0)(function(result) {
...
});