Reputation: 63619
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?
Upvotes: 0
Views: 94
Reputation: 16478
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;
}
});
Upvotes: 3
Reputation: 2013
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 :)
Upvotes: 2