Meteor.methods returns undefined - meteor

I'm using meteor 0.6.4.
Meteor.methods({
random: function(top){
var random = Math.floor((Math.random()*(top+1)));
return random;
}
});
It returns undefined whenever I execute
Meteor.call('random', 10);
Any ideas how I can get past this?

This is a perfectly normal behavior: server method calls in Meteor are documented to be asynchronous :
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method.
It means that when you ask for a Meteor.call method to execute remotely on the server, the local method call is non blocking and returns undefined immediately.
When the method has been called on the server it will send the result asynchronously to the client, so you should retrieve it using the callback pattern :
Meteor.call("myMethod", arguments..., function(error, result){
if(error){
console.log(error.reason);
return;
}
// do something with result
});
The anonymous callback function will be called on the client as soon as the server method result is sent back to the client.
There is another subtle feature in Meteor invalidating what I just said : latency compensation and methods stubs.
In case the server method call can be SIMULATED properly in the client and thus executed right away without a round-trip to the server, you can define what is called a method stub (or simulation).
A common use case for this behavior is inserting immediately in the local (client side replication subset) database some user content just posted (a comment under a blog article for example) : all the necessary data and logic is available and it makes sense to simulate server side insertion.
What happens next is that the user sees the webpage updated as soon as he submitted his content even if the server hasn't acknowledged these changes yet. (this is an example how latency compensation is implemented in Meteor).
Of course the server has final words on what gets ultimately inserted in the database, this means that when the server side twin method is executed, its actions will take precedence and replace what was inserted in the local database.
To define such method stub, you just have to define the same server method name on client code.
If the method declaration is defined in shared code (shipped both to client and server), you can test if the method call is actually a simulation by checking the isSimulation property :
Meteor.methods({
myMethod: function(arguments...){
if(this.isSimulation){
// called from the client
}
}
});
UPDATE 26/11/2014 : #steph643 commented on how the last part of my previous answer was actually wrong, here is a correction.
Note that on the server method calls can always be invoked using the synchronous syntax because server environment provides adequate blocking mechanism (fibers).
On the client however, if you return something from a method stub, it can be executed synchronously only if you're inside another stub and you can retrieve the result in a synchronous way, ie
Meteor.methods({
intermediateMethod: function(){
return " WORLD";
},
method: function(){
var result = "HELLO";
result += intermediateResult;
var intermediateResult = Meteor.call("intermediateMethod");
return result;
}
});
This behavior is a bit weird considering that Mongo collection operations (insert/update/delete) are implemented as Meteor methods and their client versions are implementing valid stubs (modification of minimongo replicated local database subset) that can be executed synchronously.

Related

Meteor synchronous method calls

Meteor docs about methods say:
On the server, this function can be run either synchronously or asynchronously. If the callback is omitted, it runs synchronously and the results are returned once the request completes successfully. If the request was not successful, an error is thrown. This is useful when making server-to-server HTTP API calls from within Meteor methods, as the method can succeed or fail based on the results of the synchronous HTTP call. In this case, consider using this.unblock() to allow other methods on the same connection to run in the mean time. On the client, this function must be used asynchronously by passing a callback.
But I find it pretty ambiguous and unobvious, does it just look sync but runs async using fibers, or does it really become sync?
E.g. if I make a server-to-server DDP method call to a meteor app from a meteor app:
const data = anotherMeteorApp.call(...)
Does the code above run sync, blocking the event loop and slowing things down? If it's in a method, would adding this.unblock() before that line of code make it async? If it's not in a method (e.g. if it's in a collection2 hook) would it block?
Yes! the code above runs synchronously (if no callback mentioned.), this will slow down things until operation is completed. You can yourself check the impact of adding this.unblock() by sleeping the process inside meteor call at server.
You can use below code to create a simulation of process taking too much time to execute and return from a method defined on server (Meteor.method()).
var breakTime = 20000; // 20 secs
var Future = Npm.require('fibers/future');
var fut = new Future();
var pause = new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), breakTime);
});
pause.then((1) => {
fut.return(true);
});
let waitHere = fut.wait();
return true;

Emulate RPC with Firebase

I am using Firebase to monitor machines across the building.
So architecture is multiple front-ends and multiple machines.
At a certain moment I want to be able to trigger some actions on these machines like:
take screenshot and put to ftp
encode a certain video file
analyze a large data-set
I am used to Actionscript, there are NetConnection and
Client objects to whom one could invoke remote methods.
Is there something similar in Firebase ?
How would you implement such a feature easily ?
I thought of having a message box, using an Array, where a message could be a data structure like:
{
'client_id': 'xxx-yyy-zzz',
'name': 'takeScrenshot',
'body': { 'creator': 'my-name' },
'timestamp': 1406214344
}
How it might work
a method call is a message entering this message box Array
listening with value_changed over this message box
pop item from array (this will trigger another value changed)
use the item to perform async operation
when async operation is done, using the client_id, notify the invoker about the operation
But to implement it correctly a lot of work must be done, does anyone know if there is an easy way to achieve this kind of functionality ?
Since Firebase is a powerful backend service, scalable, and has a RESTful API in addition to SDKs (not yet for Python, unfortunately), it generally makes the most sense to just use it directly, rather than fashioning API services on top of it.
One fast and effective way to do this is to utilize a queue approach. Have each client write data into an in/ path, and have the recipient of the event listen for child_added on that path. Then perform the remote invocation, and write data back to an out/ path for the requesting client.
client
// send it
var ref = new Firebase(QUEUE_URL);
var request = ref.child('in').push( requestData );
// wait for a reply and remove after processing
ref.child('out/'+request.name()).on('value', function(snap) {
if( snap.val() !== null ) {
console.log(snap.val());
request.remove();
// stop listening
snap.ref().off();
}
});
remote service
var ref = new Firebase(QUEUE_URL);
// listen for queue events
ref.child('in').on('child_added', function(snap) {
/*
... process queue event ...
*/
doneProcessing(snap, resultData);
});
function doneProcessing(snap, results) {
ref.child('out/'+snap.name()).set(results);
snap.ref().remove();
}

Deps.autorun says server function is undefined

I've never took the time/chance to really understand the scope of Deps.autorun... so here I am again with the same problem that had bothered me many times (previously, I always found a workaround and bypass the issue)... anyways, basically, I have a function defined on the server side:
serverFunc = function() {}
and on the client side, I do
Deps.autorun(function() { var test = serverFunc(); }
I get error message say serverFunc is not defined.
Can someone kindly help me understanding why this is happening?
Thanks so much!
Deps.autorun() always runs once, then reruns the function whenever any of the dependencies that are tracked changes. These dependencies usually need to be set up as Meteor reactive data sources. A simple function being undefined on the client and then defined on the server isn't enough to retrigger.
If you want functions defined only on the server to be called from the client, you have to do two things:
On the server, put the function in Meteor.methods
On the client, use Meteor.call
Otherwise, a function defined only on the server does not exist on the client, and calling it on the client will throw an error as calling an undefined function.

How to check if Meteor.call() fails when server connection is down?

When the Meteor server connection is lost, how can I verify that Meteor.call() failed? Meteor.call() doesn't return any value. Basically Ctrl+Z in the Meteor shell when your app is running, then do something in the app that triggers a Meteor.call i.e. adding a new blog post:
Meteor.call('createPhrase', phrase, function(error) {
console.log("This NEVER gets called if server is down.");
if (error) {
throwError(error.reason);
}
});
I tried using Session vars, but the reactivity screws it up, i.e. the code below will trigger an error in my template handler (that get's flashed to the browser quickly) and as soon as isMyError is set to true, then when the Meteor.call is successful the error goes away as per isMyError = false, but this looks really sloppy.
Session.set("isMyError", true);
Meteor.call('createPhrase', phrase, function(error) {
console.log("This NEVER gets called if server is down.");
Session.set("isMyError", false);
if (error) {
throwError(error.reason);
}
});
Template.index.isMeteorStatus = function () {
myClientStatus = Meteor.status();
if ( (myClientStatus.connected === false) || (Session.get("isMyError") === true) ) {
return false;
} else {
return true;
}
};
Meteor's calls are generally entered into a queue that are sent to the server in the order that they are called. If there is no connection they stay in the queue until the server is connected once more.
This is the reason nothing is returned because Meteor hopes that it can reconnect then send the call and when it does it does eventually return a result then.
If you want to validate whether the server is connected at the point of the call it's best to check Meteor.status().connected (which is reactive) and only run Meteor.call if it is else throw an error
if(Meteor.status().connected)
Meteor.call(....
else throwError("Error - not connected");
You could also use navigator.onLine to check whether the network is connected.
The reason you would experience a 60 second delay with Meteor.status().connected on the true status of whether meteor is connected or not is there isn't really a way for a browser to check if its connected or not.
Meteor sends a periodic heartbeat, a 'h' on the websocket/long polling wire to check it is connected. Once it realizes it didn't get a heartbeat on the other end it marks the connection disconnected.
However, it also marks it as disconnected if a Meteor.call or some data is sent through and the socket isn't able to send any data. If you use a Meteor.call beforehand to Meteor.status().connected it would realize much sooner that it is disconnected. I'm not sure it would realize it immediately that you can use them one line after the next, but you could use a Meteor.setTimeout after a second or two to fire the call.
Attempt to succeed:
Meteor is designed very well to attempt to succeed. Instead of 'attempting to fail' with an error stating the network is not available its better to try and queue everything up until the connection is back.
The best thing to do would be to avoid telling the user the network is down because usually they would know this. The queued tasks ensure the userflow would be unchanged as soon as the connection is back.
So it would be better to work with the queues that are built into the reconnection process rather than to avoid them.

In meteor, are successive operations on the server synchronous?

If, as part of a single Meteor.call, I make two calls to the database on the server, will these happen synchronously or do I need to use a callback?
Meteor.methods({
reset: function(id) {
Players.remove(_id:id);
// Will the remove definitely have finished before the find?
Players.find();
...
}
From the docs:
In Meteor, your server code runs in a single thread per request, not in the asynchronous callback style typical of Node. We find the linear execution model a better fit for the typical server code in a Meteor application.
If you read the docs on docs.meteor.com/#remove
you can find this :
Blockquote
On the server, if you don't provide a callback, then remove blocks until the database acknowledges the write and then returns the number of removed documents, or throws an exception if something went wrong. If you do provide a callback, remove returns immediately. Once the remove completes, the callback is called with a single error argument in the case of failure, or a second argument indicating the number of removed documents if the remove was successful.
Blockquote
On the client, remove never blocks. If you do not provide a callback and the remove fails on the server, then Meteor will log a warning to the console. If you provide a callback, Meteor will call that function with an error argument if there was an error, or a second argument indicating the number of removed documents if the remove was successful.
So on server side you choose if you want it to run in a sync or async way, it depends if you send a callback or not.

Resources