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.