how to implement bluebird promise in nodejs - javascript

I tried node bluebird promise, I am not able to use resolve & reject in then functions.
below is my code which calls promise
modules.modelClip.exGetAllClips(sUserData)
.then(function(finalResult) {
console.log("Final result " + finalResult)
})
.error(function(e) {
console.log("Error handler " + e)
})
.catch(function(e) {
console.log("Catch handler " + e)
});
and in exGetAllClips function am returning promise.
exports.exGetAllClips = function(pUserData) {
console.log("--- inside : clipModel : exGetAllClips -----------------------------------------------------");
console.log(pUserData);
return new modules.promise(function(resolve, reject) {
modules.dbConnection.getConnection(function(rErrorCon, connection) {
if (rErrorCon) {
reject(rErrorCon);
} else {
resolve(connection);
}
});
}).then(function(connection) {
console.log('Result 4 ')
var sClipQuery = "CALL spGetAllClips(?)";
var query = connection.query(sClipQuery, [pUserData.selfId
]);
query.on('error', function(err) {
// Handle error, an 'end' event will be emitted after this as well
//return err;
console.log(" error : spGetAllClips : ",err);
reject(err);
}).on('result', function(row) {
console.log("row : ", JSON.stringify(row));
resolve( row);
}).on('end', function() {
// all rows have been received
connection.release();
})
});
};
I want to throw error from .then if exists. But wont able to do this ,it throw error reject is undefined.
Please help, how to implement this or any other way to do.

I would it like this, first there are two callbacks( one is actually an eventHandler, I am not exactly comfortable using Promises to handle that), so split them into two promises:
use getConnectionAsync instead of getConnection by promisifying the whole module.
follow bluebird docs for handling one time events( mind you, I am not maintaining a flag to check if promise is already resolved), the code could be like:
modules.dbConnection = modules.promise.promisifyAll(modules.dbConnection);
...
exports.exGetAllClips = function(pUserData) {
console.log("--- inside : clipModel : exGetAllClips -----------------------------------------------------");
console.log(pUserData);
return modules.dbConnection.getConnectionAsync()
.then(function(connection) {
console.log('Result 4 ')
var sClipQuery = "CALL spGetAllClips(?)";
return new modules.promise(function(resolve, reject){
var query = connection.query(sClipQuery, [pUserData.selfId]);
query.on('error', function(err) {
// Handle error, an 'end' event will be emitted after this as well
//return err;
console.log(" error : spGetAllClips : ",err);
reject(err);
}).on('result', function(row) {
console.log("row : ", JSON.stringify(row));
resolve( row);
}).on('end', function() {
// all rows have been received
connection.release();
});
});
};

Related

Function returned undefined, expected Promise or value async method

I have an async method named generateSession. And I want the promises system to wait till the call is done. I wait till the data is there and than delete the row at the database. For now, that doesn't make any sense.
But I get an error at this state. Function returned undefined, expected Promise or value It looks like this comes from calling the generateSession. But I don't know how to fix it.
exports.pending = functions.database
.ref('/groups/{groupId}/status/pending/{deviceId}/')
.onCreate(event => {
generateSession().then(function(data) {
console.log("generated session:" + data.sessionId);
return event.data.ref.set({})
}).catch(function(err) {
console.log("error:", err);
});
});
var generateSession = function(){
// *** Return a promise
return new Promise(function(resolve, reject) {
opentok.createSession({mediaMode:"relayed"}, function(err, session){
if (err) {
reject(err);
} else {
resolve(session);
}
});
});
};
The onCreate of Firebase Functions itself needs to return a Promise:
exports.pending = functions.database
.ref('/groups/{groupId}/status/pending/{deviceId}/')
.onCreate(event => {
// You should return the result of generateSession() here
return generateSession().then(function(data) {
console.log("generated session:" + data.sessionId);
return event.data.ref.set({})
}).catch(function(err) {
console.log("error:", err);
// You probably don't want to catch here, let the error
// go through so that Cloud Functions can pick it up
})
});

Handle Success/Error Responses from ssh2 using Promises

I'm building a node.js app which in production will act as a SSH client to many servers, some of which may be inaccessible at any given time. I'm trying to write a function which attempts to run a SSH command with each client in its config upon startup, and I'm not able to handle both successful sessions and those which end in error. I wrapped a ssh2 client in a promise. If I remove the third (trash) server and only successes result, this works fine! See the output:
STDOUT: Hello World
STDOUT: Hello World
Session closed
Session closed
Successful session: Hello World,Hello World
But if one of the connections times out, even though I handle the error, I don't get to keep any of my data. It looks like the error message overwrites all of the resolved promises
Successful session: Error: Timed out while waiting for handshake,Error:
Timed out while waiting for handshake,Error: Timed out while waiting
for handshake
Here's my code, forgive me if this is a bit scattered, as I've combined a few of my modules for the sake of this question. My goal is to keep the data from the successful session and gracefully handle the failure.
var Client = require('ssh2').Client;
const labs = {
"ny1": "192.168.1.2",
"ny2": "192.168.1.3",
"ny3": "1.1.1.1"
};
function checkLabs() {
let numLabs = Object.keys(labs).length;
let promises = [];
for(i=0;i<numLabs;i++){
let labName = Object.keys(labs)[i];
promises.push(asyncSSH("echo 'Hello World'", labs[labName]));
}
Promise.all(promises.map(p => p.catch(e => e)))
.then(results => console.log("Successful session: " + results))
.catch(e => console.log("Error! " + e));
}
var sendSSH = function (command, dest, callback) {
var conn = new Client();
conn.on('ready', function() {
return conn.exec(command, function(err, stream) {
if (err) throw err;
stream.on('data', function(data) {
callback(null, data);
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data){
callback(err);
console.log('STDERR: ' + data);
}).on('close', function(err) {
if(err) {
console.log('Session closed due to error');
} else {
console.log('Session closed');
}
});
stream.end('ls -l\nexit\n');
});
}).on('error', function(err){
callback(err);
}).connect({
host: dest,
port: 22,
username: 'root',
readyTimeout: 10000,
privateKey: require('fs').readFileSync('link-to-my-key')
});
};
function asyncSSH(command, dest) {
return new Promise(function(resolve, reject){
sendSSH(command, dest, function(err,data) {
if (!err) {
resolve(data);
} else {
reject(err);
}
});
});
}
checklabs();
How can I better use this promise wrapper to handle whatever errors come from the ssh2 client? Any tips are appreciated.
To get best use from each connection, you can (and arguably should) promisify separately :
the instatiation of each Client() instance
each instance's conn.exec() method (and any other asynchronous methods as required)
This will allow each instance of Client() to be reused with different commands (though not required in this example).
You should also be sure to disconnect each socket when its job is done, by calling client_.end(). For this, a "disposer pattern" is recommended.
With those points in mind and with a few assumptions, here's what I ended up with :
var Client = require('ssh2').Client;
const labs = {
"ny1": "192.168.1.2",
"ny2": "192.168.1.3",
"ny3": "1.1.1.1"
};
function checkLabs() {
let promises = Object.keys(labs).map((key) => {
return withConn(labs[key], (conn) => {
return conn.execAsync("echo 'Hello World'")
.catch((e) => "Error: " + e.message); // catch in order to immunise the whole process against any single failure.
// and inject an error message into the success path.
});
});
Promise.all(promises)
.then(results => console.log("Successful session: " + results))
.catch(e => console.log("Error! " + e.message)); // with individual errors caught above, you should not end up here.
}
// disposer pattern, based on https://stackoverflow.com/a/28915678/3478010
function withConn(dest, work) {
var conn_;
return getConnection(dest).then((conn) => {
conn_ = conn;
return work(conn);
}).then(() => {
if(conn_) {
conn_.end(); // on success, disconnect the socket (ie dispose of conn_).
}
}, () => {
if(conn_) {
conn_.end(); // on error, disconnect the socket (ie dispose of conn_).
}
});
// Note: with Bluebird promises, simplify .then(fn,fn) to .finally(fn).
}
function getConnection(dest) {
return new Promise((resolve, reject) => {
let conn = promisifyConnection(new Client());
conn.on('ready', () => {
resolve(conn);
})
.on('error', reject)
.connect({
host: dest,
port: 22,
username: 'root',
readyTimeout: 10000,
privateKey: require('fs').readFileSync('link-to-my-key')
});
});
}
function promisifyConnection(conn) {
conn.execAsync = (command) => { // promisify conn.exec()
return new Promise((resolve, reject) => {
conn.exec(command, (err, stream) => {
if(err) {
reject(err);
} else {
let streamSegments = []; // array in which to accumulate streamed data
stream.on('close', (err) => {
if(err) {
reject(err);
} else {
resolve(streamSegments.join('')); // or whatever is necessary to combine the accumulated stream segments
}
}).on('data', (data) => {
streamSegments.push(data);
}).stderr.on('data', function(data) {
reject(new Error(data)); // assuming `data` to be String
});
stream.end('ls -l\nexit\n'); // not sure what this does?
}
});
});
};
// ... promisify any further Client methods here ...
return conn;
}
NOTES:
the promisification of conn.exec() includes an assumption that data may be received in a series of segments (eg packets). If this assumption is not valid, then the need for the streamSegments array disappears.
getConnection() and promisifyConnection() could be written as one function but with separate function it's easier to see what's going on.
getConnection() and promisifyConnection() keep all the messy stuff well away from the application code.

Error while using promisefiy all

I use promisifyAll to the following module since I want to use it with promises and I got error "TypeError: Cannot read property 'then' of undefined"
const DBWrapper = Promise.promisifyAll(require("node-dbi").DBWrapper);
var dbWrapper = new DBWrapper('pg', dbConnectionConfig);
dbWrapper.connect();
dbWrapper.insert('USERS', data, function (err, data) {
if (err) {
console.log("error to insert data: " + err);
} else {
console.log("test" + data);
}
}).then(() => {
//read data
dbWrapper.fetchAll("SELECT * FROM USERS", null, function (err, result) {
if (!err) {
console.log("Data came back from the DB.", result);
} else {
console.log("DB returned an error: %s", err);
}
dbWrapper.close(function (close_err) {
if (close_err) {
console.log("Error while disconnecting: %s", close_err);
}
});
});
})
You have two things going on here that are incorrect from what I can tell.
You're not properly implementing the promises in the above code. Error first callbacks aren't passed in when invoking Promise returning methods, instead they pass the result value to the nearest .then(). If an error occurs they will pass that to the nearest .catch(). If there is no .catch() and an error occurs, an unhandledRejection error will be thrown.
By default, promisifyAll() appends the suffix Async to any promisified methods, so you will need to modify any method calls on dbWrapper accordingly.
Assuming node-dbi can be promisifed and that your DB calls are correct the following code should work as you were initially anticipating
const Promise = require('bluebird');
const DBWrapper = require("node-dbi").DBWrapper;
const dbWrapper = Promise.promisifyAll(new DBWrapper('pg', dbConnectionConfig));
return dbWrapper.insertAsync('USERS', data)
.then((data) => {
console.log("test" + data);
//read data
return dbWrapper.fetchAllAsync("SELECT * FROM USERS", null)
})
.then((result) => {
console.log('Data came back from DB.', result);
return dbWrapper.closeAsync();
})
.catch((err) => {
console.log('An error occurred:', err);
});

Multiple exist points for a nodejs controller with promises

The following snippet of code is a nodeJS controller method calling a service that returns a deferred promise. Im trying to understand the best way to handle multiple exist points.
For example, if the service returns an empty object I want the promise chain to exist and return the response 'Nothing found here' to the user. If it does find something, it moves from step 1 onto the next item in the promise chain i.e. step 2.
From my testing, it seems to be returning the json response and then dropping in to the next logic step i.e. step 2. This logic cant be handled in the service right now i.e. if no item is found to return an error.
module.exports.show = function (req, res) {
service.getItem(itemId)
.then(function (item) {
if (!item) {
return res.json('Nothing found here');
}
// Step 1
// do some work on the item
return item;
})
.then(function (foundItem) {
// Step 2
// do some more work on the item
return res.json('Item has changed ' + foundItem);
})
.catch(function (err) {
return res.json(err);
});
};
Try the following code. The Error is caught by the catch handler. I also changed the if-clause (I assumed that's what you actually meant):
module.exports.show = function (req, res) {
service.getItem(itemId)
.then(function (item) {
if (!item) {
throw new Error('Nothing found here');
}
// Step 1
// do some work on the item
return item;
})
.then(function (foundItem) {
// Step 2
// do some more work on the item
return res.json('Item has changed ' + foundItem);
})
.catch(function (err) {
return res.json(err);
});
};
Remember that then always returns a promise. If you don't return a promise yourself, a resolved promise is automatically created with the return value.
So, return res.json('Nothing found here') is the same as return Promise.resolve(res.json('Nothing found here'));, which means that next then will, obvisouly, be called.
If you don't want to execute the next then, you just have to reject the promise :
throw new Error('Nothing found here'));
// ...
.catch(function (err) {
return res.json(err.message);
});
By the way you probably meant if (!item), not if (item) :
if (!item) {
throw new Error('Nothing found here');
}
If Steps 1 and 2 are synchronous, then :
module.exports.show = function (req, res) {
service.getItem(itemId).then(function (item) {
if (!item) {
throw new Error('Nothing found here'); // this will be caught below
}
item = doWork_2(doWork_1(item)); // Steps 1 and 2:
return res.json('Item has changed ' + item);
}).catch(function (err) {
return res.json(err.message);
});
};
where doWork_1() and doWork_2() both return the processed item.
If Steps 1 and 2 are asynchronous, then :
module.exports.show = function (req, res) {
service.getItem(itemId).then(function (item) {
if (!item) {
throw new Error('Nothing found here'); // this will be caught below
} else {
return doWork_1(item).then(doWork_2); // Step 1, then Step 2
}
})
.then(function (item) {
return res.json('Item has changed ' + item);
}).catch(function (err) {
return res.json(err.message);
});
};
where doWork_1() and doWork_2() return a promise resolved with the processed item.
If it is uncertain whether doWork_1() and .doWork_2() are synchronous or asynchronuus, then use the following pattern, which will handle both eventualities :
module.exports.show = function (req, res) {
service.getItem(itemId).then(function (item) {
if (!item) {
throw new Error('Nothing found here'); // this will be caught below
} else {
return item;
}
})
.then(doWork_1) // Step 1
.then(doWork_2) // Step 2
.then(function (item) {
return res.json('Item has changed ' + item);
}).catch(function (err) {
return res.json(err.message);
});
};

Using Promise.all() for multiple http/oauth queries

I'm trying to wait for the output of two OAuth calls to an API, and I'm having trouble retrieving the data from those calls. If I use Promise.all(call1,call2).then() I am getting information about the request object.
First, here's the setup for the fitbit_oauth object:
var fitbit_oauth = new OAuth.OAuth(
'https://api.fitbit.com/oauth/request_token',
'https://api.fitbit.com/oauth/access_token',
config.fitbitClientKey,
config.fitbitClientSecret,
'1.0',
null,
'HMAC-SHA1'
);
foodpath = 'https://api.fitbit.com/1/user/-/foods/log/date/' + moment().utc().add('ms', user.timezoneOffset).format('YYYY-MM-DD') + '.json';
activitypath = 'https://api.fitbit.com/1/user/-/activities/date/' + moment().utc().add('ms', user.timezoneOffset).format('YYYY-MM-DD') + '.json';
Promise.all([fitbit_oauth.get(foodpath, user.accessToken, user.accessSecret),
fitbit_oauth.get(activitypath, user.accessToken,
user.accessSecret)])
.then(function(arrayOfResults) {
console.log(arrayOfResults);
}
I want arrayOfResults to give me the data from the calls, not information about the requests. What am I doing wrong here? I'm new to promises so I'm sure this is easy for someone who isn't.
The callback for a single fitbit_oauth call is as follows:
fitbit_oauth.get(
'https://api.fitbit.com/1/user/-/activities/date/' + moment().utc().add('ms', user.timezoneOffset).format('YYYY-MM-DD') + '.json',
user.accessToken,
user.accessSecret,
function (err, data, res) {
if (err) {
console.error("Error fetching activity data. ", err);
callback(err);
return;
}
data = JSON.parse(data);
console.log("Fitbit Get Activities", data);
// Update (and return) the user
User.findOneAndUpdate(
{
encodedId: user.encodedId
},
{
stepsToday: data.summary.steps,
stepsGoal: data.goals.steps
},
null,
function(err, user) {
if (err) {
console.error("Error updating user activity.", err);
}
callback(err, user);
}
);
}
);
Thanks to jfriend00 I got this working, here's the new code:
function fitbit_oauth_getP(path, accessToken, accessSecret) {
return new Promise (function(resolve, reject) {
fitbit_oauth.get(path, accessToken, accessSecret, function(err, data, res) {
if (err) {
reject(err);
} else {
resolve(data);
}
}
)
})};
Promise.all([fitbit_oauth_getP(foodpath, user.accessToken, user.accessSecret),
fitbit_oauth_getP(activitypath, user.accessToken, user.accessSecret)])
.then(function(arrayOfResults) {
console.log(arrayOfResults);
});
Promise.all() only works properly with asynchronous functions when those functions return a promise and when the result of that async operation becomes the resolved (or rejected) value of the promise.
There is no magic in Promise.all() that could somehow know when the fitbit functions are done if they don't return a promise.
You can still use Promise.all(), but you need to "promisify" the fitbit functions which is a small wrapper around them that turns their normal callback approach into returning a promise that was then resolved or rejected based on the callback result.
Some references on creating a promisified wrapper:
Wrapping Callback Funtions
How to promisify?
If you have an async function that accepts a callback to provide the async result such as fs.rename(oldPath, newPath, callback), then you can "promisify" it like this:
function renameP(oldPath, newPath) {
return new Promise(function(resolve, reject) {
fs.rename(oldPath, newPath, function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
};
renameP("orig.txt", "backup.txt").then(function() {
// successful here
}, function(err) {
// error here
});
Some promise libraries such as Bluebird have a built in .promisify() method that will do this for you (it will return a function stub that can be called on any function that follows the node.js async calling convention).

Categories

Resources