I strongly suggest against using this pattern. A stub is a stub and in no way represents the actual return value of the method that’s going to be evaluated on the server. Seeking the result of a method call should be done within the callback.
Well, if you do definitely know absolutely for hundred percent sure that the client and server results are going to be the same, perhaps there is some case for it, but I still don’t feel it’s right. But again, one may than argue why you need a method for whatever you’re doing within that method if it can execute on the client just the same.
I think you should call the above method with returnStubValue: true.
Sure you can achieve the same result by generating an _id client-side (with the Random package) and pass it as a parameter to the method, but it looks overkill.
Notice also that Collection.insert is a built-in Meteor method whose stub returns a value, and nobody is shocked. To me, MDG has been all wrong hiding stub return values. This introduces special cases and makes methods more difficult to understand.
I just don’t agree with @serkandurusoy who “I strongly suggest against using this pattern”.
This is actually how meteor does latency compensation…
Eg:
// Define the method on both client and server
Meteor.methods({
test: function() {
if (this.isSimulation) {
return 1; // Client wants this to be 1
} else {
return 2; // Server wants this to be 2
}
}
});
Template.hello.events({
'click button': function () {
// Call the test method sync
var clientResult = Meteor.apply('test', [], {returnStubValue: true},
function(err, serverResult) {
if (clientResult !== serverResult) { // 1 !== 2
// If client and server disagree let server correct the client
Session.set('result', serverResult); // -> 2
}
}
);
// Expect that the stub is correct
Session.set('result', clientResult); // -> 1
}
});
@raix, this is in my opinion a very good reason to not use returnStubValue unless you know exactly what you are doing.
Per your example, the client - although for a brief moment - assumes a wrong result with which it can do unwanted things like display some secret information.
Granted, your example is a tamed one where you explicitly suggest you know what you are doing.
But @Steve’s first example suggests that the client should go on with the possibly wrong result and not care about what the server has to say.
And again @Steve’s second example assumes that (even if an id is generated on the client) the server will not throw an error, possibly due to some other criteria, like a unique key index!
Put in other terms, I think latency compensation is a great feature that Meteor has blessed us with, but it is something like the cross guards of that new light sabre from star wars 7. You can easily chop your own hands off if you don’t know how to wield it.
And I do see that inexperienced meteor developers are a tad too trigger happy with latency compensation.
For a brief moment, yes. This is what latency compensation is about. I agree this requires to be extra careful.
Thanks for pointing me to Star Wars 7 new lightsabre, didn’t know about it :- )
Meteor.methods({
test: function() {
if (this.isSimulation) {
return 1; // Client wants this to be 1
} else {
return 2; // Server wants this to be 2
}
}
});
Template.hello.events({
'click button': function () {
// Call the test method sync
var clientResult = Meteor.apply('test', [], {returnStubValue: true});
Session.set('result', clientResult);
}
});
If you do not provide the callback, does clientResult ever get overwritten by the server result?
If not - and that’s what I believe how it works - I’m correct to say that the result is permanent and you don’t know what the server will respond with.
Otherwise, yes, it will not be permanent, but then again what’s the point of a callback then?
It seems I didn’t understand what you initially meant by “permanent”, sorry for that.
Yes, if you don’t provide a callback, you have no way to know what the server will return.
To get back on track:
Collection.insert relies on returnStubValue: true. If you want to make a case against returnStubValue: true, you’ll have to explain what’s wrong with Collection.insert (which returns an _id immediately, long before the server has actually inserted anything in the collection).
On the client, insert never blocks. If you do not provide a callback and the insert fails on the server, then Meteor will log a warning to the console. If you provide a callback, Meteor will call that function with error and result arguments.
Insert is a special method which randomly creates an id, returns that id immediately, and than continues on with inserting into minimongo, followed by the actual insert on the server. But again, it also does provide some feedback in the form of a warning on the client console.
I think this should be stressed on that part of the document that the client insert is not something that should be relied upon. As far as I see it, this is implied by the warning and I find that insufficient.
Also, the client insert is required to go through an allow/deny mechanism where the platform suggests that client inserts are not to be taken lightly, nor trustworthy.
I think if one decides to use latency compensation, I think it should be considered a feature as a progressive enhancement where it provides value in terms of improved UX where the journey of the client result ends right there and is non-critical.
I have attempted to use a callback to get the return value of result but it keeps coming back undefined. No errors are occurring, so I can’t figure out why it’s not returning. Thoughts as to why?