How to return value on Meteor.call() in client?

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.

very thanks for the clear solution.

If done correctly client-side methods / stubs can be useful for latency compensation.

1 Like

if you can, please explain…

@serkandurusoy:

[…] perhaps there is some case for it

There is a common one:

Meteor.methods({
    insertDoc: function(args) {
        ...
        return MyCollection.insert(doc);
    });

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.

4 Likes

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 :- )

1 Like

Yep, it’s that brief moment that concerns me where the code might be doing some nasty things with the uncertain result.

Also, I think it is permanent if you use returnStubValue: true so that also requires extra attention. (Or please correct me if I’m wrong)

Also, I just cannot resist the urge: https://www.youtube.com/watch?v=W2kHXf7mSD8 :smile:

I think it is permanent if you use returnStubValue: true

Nope, it just makes the stub result to be returned instead of being discarded.
The the doc here and the code here.

Hm, now I’m confused.

Quoting parttially from @raix’s example:

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).

Taken from http://docs.meteor.com/#/full/insert

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.

2 Likes

Well done!
But not convincing enough to me :- )

Haha, well I do respect that :smile:

Now i find the package for this simple:reactive-method and deanius:promise.
I will try its.

Wow, I was planning to do same thing as deanius:promise! Gonna try it!

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?

And how you are returning it ?
Cause you cant use it directly as return value from helper.

I have the following

// on server
Meteor.methods({
  myMethod: function (arg1, arg2) {
    var result = getResult(arg1, arg2);
    return result;
  },
});

// on client
Meteor.call('myMethod', arg1, arg2, function(error, result) {
  if (error) {
    // handle error
  }
  else {
    console.log(result);
  }
});
2 Likes