Async Issue using NPM Package and Meteor.wrapAsync - javascript

When using a function client.call from an npm module (node-celery), it appears that the callback function of client.call is not executed.
Meteor.methods({
'estimates.request'(data) {
var celery = require('node-celery')
var client = celery.createClient({...})
client.on('connect', function() {
console.log('connected'); // this is executed
client.call('proj.tasks.register', [name],
function(err, result) {
console.log('result: ', result); // this is not executed
client.end();
return result;
}
);
});
}
});
Tried wrapping client.call in Meteor.wrapAsync:
callFunc = client.on('connect', function() {
console.log('connected'); // this is executed
client.call('proj.tasks.register', [name],
function(err, result) {
console.log('result: ', result); // this is not executed
client.end();
return result;
}
);
});
callFuncSync = Meteor.wrapAsync(callFunc)
callFuncSync()
but this throws an error in the Meteor server console:
err: [Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.]
err: { [Error: read ECONNRESET] code: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read' }
Question: How should we use Meteor.bindEnvironment to fix this issue?

From the docs,
Wrap a function that takes a callback function as its final parameter. The signature of the callback of the wrapped function should be function(error, result){}
Your code simply wraps the return value of the event attachment call.
You could wrap the whole thing (connection + task calls), but in your case, I would suggest a different approach:
Right now, you are connecting to Celery every time someone is calling the method. I would advise having a persistent connection to Celery, if possible.
The functions that you are trying to wrap do not adhere to the wrapAsync requirements, so you would have to wrap them in a function that does.
In the following code, both the connection and call functions are taken care of. Note that those functions take a cb argument, which will be provided to them by Meteor, and call it with an error and/or result, as appropriate.
those functions are then passed to wrapAsync.
The sync version of them throws if an error is passed to the callback and simulate a synchronous run using a fiber if they are called in a synchronous manner (i.e, no callback is passed). That's the reason for the try..catch block.
import { Meteor } from 'meteor/meteor';
const celery = require('node-celery'); // or `import` it
function createAndConnectAsync(details, cb) {
const client = celery.createClient(details);
const errHandler = function(e) {
cb(e, client);
};
client.once('connect', function() {
client.off('error', errHandler);
cb(null, client); // success
});
client.once('error', errHandler);
}
function callAsync(client, task, args, cb) {
client.call(task, args, function(result) {
cb(null, result);
});
}
const createAndConnect = Meteor.wrapAsync(createAndConnectAsync);
const call = Meteor.wrapAsync(callAsync);
let client;
try {
client = createAndConnect({...}); // returns after the client is connected
} catch(e) {
// connection error
}
Meteor.methods({
'estimates.request'(data) {
// generate `name`
const result = call(client, 'proj.tasks.register', [name]);
return result;
}
});

Combinding async libraries can be tricky, usually they have some helper methods like Meteor's bindEnvironment.
The function returned from Meteor.bindEnvironment also automatically gets run in a Fiber.
Meteor.methods({
'estimates.request'(data) {
var celery = require('node-celery')
var client = celery.createClient({...})
var otherCallback = Meteor.bindEnvironment(function(err, result) {
console.log('result: ', result); // this is not executed
client.end();
return result;
});
var connectCallback = Meteor.bindEnvironment(function() {
console.log('connected'); // this is executed
client.call('proj.tasks.register', [name], otherCallback);
});
client.on('connect', connectCallback);
}
});
FYI: didn't test this because your example isn't 100% complete :)

Related

callback of the callback function node js

So in Node we use async functions. And to do that we use callback functions as parameters. But how do I execute the final function after which I want to terminate the code? Just pass the empty function? Here's example:
fs.mkdir('stuff', function(){
fs.readFile('readMe.txt', 'utf8', function(err, data) {
fs.writeFile('./stuff/writeMe.txt', data);
});
});
mkdir has callback function - all fine
readFile has callback function - all fine
writeFile has NOT callback function because that's the last thing I want to do, but then I get an error in console:
"DeprecationWarning: Calling an asynchronous function without callback is deprecated."
Should I every time I do that, pass an empty function as a callback to avoid the error? What's the best practice for this?
Should I every time I do that, pass an empty function as a callback to avoid the error?
No.
What's the best practice for this?
Pass in a function and handle any errors it reports. You also need to handle errors from mkdir and readFile, which currently you're ignoring.
E.g.:
fs.mkdir('stuff', function(err) {
if (err) {
// Do something with the fact mkdir failed
} else {
fs.readFile('readMe.txt', 'utf8', function(err, data) {
if (err) {
// Do something with the fact readFile failed
} else {
fs.writeFile('./stuff/writeMe.txt', data, function(err) {
if (err) {
// Do something with the fact writeFile failed
}
});
}
});
}
});
...which is a fair example of callback hell, which is part of the motivation of using promises instead. You could promisify the fs API (using any of several libs, such as promisify) and do it like this:
fsp.mkdir('stuff')
.then(() => fsp.readFile('readMe.txt', 'utf8'))
.then(data => fsp.writeFile('./stuff/writeMe.txt', data))
.catch(err => {
// Do something with the fact something failed
});
...where fsp is a placeholder for the promisified fs API.
In Node 8.x+, you could use async/await to write synchronous-looking code with those promisified APIs:
// This must be inside an `async` function
try {
fsp.mkdir('stuff');
const data = await fsp.readFile('readMe.txt', 'utf8');
await fsp.writeFile('./stuff/writeMe.txt', data);
} catch (err) {
// Do something with the fact something failed
}
You can use writeFileSync instead.
fs.mkdir('stuff', function(){
fs.readFile('readMe.txt', 'utf8', function(err, data) {
fs.writeFileSync('./stuff/writeMe.txt', data);
});
});

Node js: Express js asynchronous db query execution-return results got undefiend

Just started to learn express js framework ,here is my simple database query execution part its invoked with this url localhost:3000/api/test.
db.query('SELECT * FROM user', function (error, results, fields) {
if (error) throw error;
console.log('The result is:', results[0].id);
return results;
});
Does it really asynchronous?? suppose another user request this url does he need to wait for the previous query execution??.
I've heard about async package ,but don't know how this is applicable in my case
UPDATE
I got proper result in console.log(); but when i return the result i got undefined error
Here is my model.js
module.exports = {
getUser:function () {
db.query('SELECT * FROM user', function (error, results, fields) {
if (error) throw error;
console.log('The result is: ', results[0].id);
});
}
}
From my controller.js
var model = require('../models/user.js');
module.exports = {
getData : function(req, res){
//invoke model
console.log(model.getUser());
}
}
Node is non-blocking and will serve this request as and when it's called.
If another user hits this endpoint then it will execute again regardless if the first query has completed or not (unless the SQL has locked the table, in which case all consecutive connections/queries will wait and may timeout because of it). This happens on a connection basis.
You should make sure to check your SQL server (MySQL?) configs here to make sure there are enough max_connections to be able to cope with whatever load you are expecting.
Remember that the biggest bottleneck to an application is usually the database.
Your query above will need a callback to return the data asynchronously.
db.query('SELECT * FROM user', function (error, results, fields) {
if (error) throw error;
console.log('The result is:', results[0].id);
//cb=callback function passed in to context
if (cb) cb(results);
});
Updated answer from updated question
In your model.js:
module.exports = {
getUser:function (cb) {
db.query('SELECT * FROM user', function (error, results, fields) {
if (error) throw error;
console.log('The result is: ', results[0].id);
if (cb) cb(results);
});
}
}
In your controller.js:
module.exports = {
getData : function(req, res){
//invoke model
model.getUser(function(results) {
console.log(results);
});
}
}
When you deal with callback, the safe and clean way to handle them is Promises. It's now standard in JavaScript and don't require any module.
And yes it is asynchronous. Behind, there'll be network access and dialogs with the database server. Only when they're done chatting will the callback be called.
module.exports = {
getUser: function () {
// wrap asynchronously called callback in Promise
new Promise((resolve, reject) => {
db.query("SELECT * FROM user", (error, results, fields) => {
if (error) {
reject(error); // similar to "throw"
}
else {
resolve({ results, fields }); // similar to "return"
}
});
});
}
};
How do you use it:
Vanilla notation:
// similar to "try"
model.getUser()
.then((answer) => {
console.log("answer", answer);
})
// similar to "catch"
.catch((error) => {
console.log("error", error);
});
async-await notation (only available in last versions of nodejs & browsers):
// you must be in an async environement to use "await"
async function wrapper() {
try {
var answer = await model.getUser(); // wait for Promise resolution
console.log("answer", answer);
}
catch(error) {
console.log("error", error);
}
}
// async function return automatically a Promise so you can chain them easily
wrapper();

Export a variable which is a result of an asynchronous function call

I'm creating an app that will use the https://github.com/vpulim/node-soap to communicate with a soap server.
I would like to create a client component which I will forward the necessary methods to the soap-client created with this module.
I'm having trouble to return an object that will use this client, since the client is created asynchronously.
var soap = require('soap');
var url = 'http://someurl?wsdl';
soap.createClient(url, function(err, client) {
if (err) {
console.log(err);
return;
}
console.log(client.describe());
// I need to publish this client so that other functions in this file will be able to use it
});
module.exports = {
doSomething: function() {
//how can I access the client here?
}
};
How would I go about doing this?
One solution to this problem is to use promises:
var soap = require('soap');
var url = 'http://someurl?wsdl';
var clientPromise = new Promise(function(resolve, reject) {
soap.createClient(url, function(err, client) {
if (err) {
// reject the promise when an error occurs
reject(err);
return;
}
// resolve the promise with the client when it's ready
resolve(client);
});
});
module.exports = {
doSomething: function() {
// promise will wait asynchronously until client is ready
// then call the .then() callback with the resolved value (client)
return clientPromise.then(function(client) {
// do something with client here
}).catch(function(err) {
// handle errors here
console.error(err);
});
}
};
A few advantages to this:
Promises are native JavaScript objects (as of Node 4.0.0, with packages such as bluebird providing support for prior versions)
Promises can be "reused": if clientPromise has already resolved once, it will immediately resolve when doSomething is later called.
Some disadvantages:
doSomething and all other exported functions are inherently asynchronous.
Is not directly compatible with Node-type callbacks.
Not sure if my response helps you, but this is how I do. I create createClient every time and then within the client, I call the actual SOAP method (here GetAccumulators). May be not a great way, but this works for me. Here is my code sample
soap.createClient(url, function (err, client) {
if (err) {
logger.error(err, 'Error creating SOAP client for %s', tranId);
reject('Could not create SOAP client');
}
client.addSoapHeader(headers);
// envelope stuff
client.wsdl.definitions.xmlns.soapenv = 'http://schemas.xmlsoap.org/soap/envelope/';
client.wsdl.definitions.xmlns.acc = 'http://exampleurl/';
client.wsdl.xmlnsInEnvelope = client.wsdl._xmlnsMap();
client.GetAccumulators(args, function (err, result) {
if (err) {
if (isNotFoundError(err)) {
logger.debug('Member not found for tranId %s', tranId);
reject({status:404, description:'No match found'});
}
reject({status:500, description:'GetAccumulators error'});
}
return resolve({data: result, tranId: tranId});
});

Node promise-circuitbreaker - Returning a value from called function

I'm trying to use the promise-circuitbreaker node module.
I am able to pass a parameter to the called function, however, I am unable to have the called function return a value back.
Furthermore, I keep getting a timeout which I don't understand. I am obviously missing something, however I can't find any solution in the docs (http://pablolb.github.io/promise-circuitbreaker/)
I've made a very simple sample app to show my difficulties:
var CircuitBreaker = require('promise-circuitbreaker');
var TimeoutError = CircuitBreaker.TimeoutError;
var OpenCircuitError = CircuitBreaker.OpenCircuitError;
function testFcn(input, err) {
console.log('Received param: ', input);
//err('This is an error callback');
return 'Value to return';
}
var cb = new CircuitBreaker(testFcn);
var circuitBreakerPromise = cb.exec('This param is passed to the function');
circuitBreakerPromise.then(function (response) {
console.log('Never reach here:', response);
})
.catch(TimeoutError, function (error) {
console.log('Handle timeout here: ', error);
})
.catch(OpenCircuitError, function (error) {
console.log('Handle open circuit error here');
})
.catch(function (error) {
console.log('Handle any error here:', error);
})
.finally(function () {
console.log('Finally always called');
cb.stopEvents();
});
The output I get from this is:
Received param: This param is passed to the function
Handle timeout here: { [TimeoutError: Timed out after 3000 ms] name: 'TimeoutError', message: 'Timed out after 3000 ms' }
Finally always called
In my case, I want a simple string to be returned. I don't want a timeout error.
If I uncomment the line //err('This is an error callback') in testFcn(), I get the following output:
Received param: This param is passed to the function
Handle any error here: This is an error callback
Finally always called
So it appears that the 2nd parameter in the called function is for error handling.
Any help would be greatly appreciated.

How can I avoid my Q promise catch block being called twice in a NodeJS callback-style API when running mocha unit tests?

We're using the Q promise library in our node API, but allow functions to be called via callbacks.
For example:
function foo(cb) {
Q.fcall(function () {
return true;
}).then(function (result) {
return cb(null, result);
}).catch(function (err) {
return cb(err, null);
});
}
When I run my mocha unit tests, if there is an exception in the callback, it results in the the callback being called twice.
For example:
var called = 0;
foo(function (err, res) {
called++;
console.log('err: ' + err);
console.log('res: ' + res);
console.log('called: ' + called);
throw Error(throw Error from foo!');
});
This gives the following result:
err: null
res: true
called: 1
err: Error: throw Error from foo!
res: null
called: 2
One approach we found was to do the following:
function foo2(cb) {
var _result, _err;
Q.fcall(function () {
return true;
}).then(function (result) {
_result = result;
}).catch(function (err) {
_err = err;
}).finally(function () {
_.defer(cb, _err, _result);
});
}
The idea was to have one place where the callback would be called and try to prevent developer errors by enforcing the defer to clear the call stack.
With this approach, our callback is called once, and any errors (or in our case, asserts) get handled directly by the caller.
Is there a better technique we can use? I feel like this is a pretty common scenario, so I'd imagine there exists a cleaner solution...
Modify your foo function to handle both the fulfillment and the rejection in the same then call using 2 separate handlers:
function foo(cb) {
Q.fcall(function () {
return true;
}).then(function (result) {
return cb(null, result);
}, function (err) {
return cb(err, null);
});
}

Categories