How can invoke a stub inside a stub to be executed synchronously?


#1

In document

> Finally, if you are inside a stub on the client and call another method, the other method is not executed (no RPC is generated, nothing “real” happens). If that other method has a stub, that stub stands in for the method and is executed. The method call’s return value is the return value of the stub function. The client has no problem executing a stub synchronously, and that is why it’s okay for the client to use the synchronous Meteor.call form from inside a method body, as described earlier.

I don’t understand how a stub can be executed synchronously.

Can you please describe it with an example ?


#2

I had the same trouble understanding stub methods at first, so I created this little demo


#3

@jamgold Thank you for the demo! But, I still have 2 questions.
How can I get the return value of stub?
And I don’t understand the meaning of “The client has no problem executing a stub synchronously” in document. Can you please suggest an example or description of that?


#4

That’s because it’s not doing a RPC. It’s just running client side code, which can be run syncronously.


#5

@babrahams So, do you mean that a stub is always executing synchronously(because they are just simulating the result of the real server method), even if the real server side method is executing asynchronously ?


#6

Yes, I’m pretty sure that’s what’s happening. Not sure how you get the return value from a stub though, as the result from the callback (function(error, result) { ... }) is the return value from the real server side execution.


#7

Oh Thank you so much @babrahams.
Now I understand almost! Can I ask you one more question?

If I assume that I invoke the real server side method named B with callback in another real server side method named A with callback, are both A and B executed asynchronously ?? and are the stub of A and stub of B executed synchronously ??


#8

That’s my understanding, based on what is found in the docs and my own experiments. The stub of B will be executed syncronously within the context of the callback, which is fired after a server round-trip. So stub of B is not entirely syncronous.

client.js

console.log('start');
Meteor.call('a', function(err,res) {
  console.log('Result of a:', res);
  Meteor.call('b', function(e,r) {
    console.log('Result of b:',r); 
  });
  console.log('After b is called. Have we seen the result of b yet?');
});
console.log('finish');

common.js

Meteor.methods({
  a: function() {
    return 'A'; 
  },
  b: function() {
    return 'B'; 
  }
});

The above code produces this in the client console:

"start" app.js:1:12
"finish" app.js:9:0
"Result of a:" "A" app.js:3:2
"After b is called. Have we seen the result of b yet?" app.js:7:2
"Result of b:" "B"

Meteorpad example is here if you want to play around with it.


#9
Thank you @babrahams. I tested your example and changed a little bit for attempting to see the behavior of stub. In the following code, "console.log('isSimulation - A : ' + x)" is not logging on console, although "console.log('isSimulation - B : ' + x)" is working. And, when you ensure the following result, you can see the log "isSimulation - B : 10". Can you please explain about this behavior ? I don't understand why it's working as this. I'm very confused..

client.js
console.log(‘start’);
for (i = 0; i < 10; i++) {
    Meteor.call(‘a’, i, function (err, res) {
        console.log(‘Result of a:’, res);
        Meteor.call(‘b’, i, function (e, r) {
            console.log(‘Result of b:’, r);
        });
        console.log(‘After b is called. Have we seen the result of b yet?’);
    });
}
console.log(‘finish’);



public.js
Meteor.methods({
    a: function (x) {
        // stub
        if(this.isSimulation) {
            console.log('isSimulation - A : ’ + x);
            return 'A-stub : ’ + x;
        // Server side method
        } else {
            // When it is server, delay a while.
            for (i=0; i < 10000000; i++){}
            return 'A : ’ + x;
        }
    } ,
    b: function (x) {
        // stub
        if(this.isSimulation) {
            console.log('isSimulation - B : ’ + x);
            return 'B-stub : ’ + x;
        // Server side method
        } else {
            // When it is server, delay a while.
            for (i=0; i < 10000000; i++){}
            return 'B : ’ + x;
        }
    }
});



result for browser console
start
finish
Result of a: A : 0
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 1
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 2
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 3
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 4
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 5
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 6
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 7
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 8
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 9
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of b: B : 10


#10

Interesting test. That last line:

… was that, by any chance, repeated 10 times in the console? If so, it all makes a bit more sense.

The result of

is expected because by the time the first callback of method a is run on the client (i.e. after a server round trip), the value of i is already 10 and method b is only called from within callbacks of method a.

I have to admit, I’m not sure why isSimulation isn’t set to true on the client side run of method a. In fact, it seems that method a isn’t even being run on the client. I expected it would for latency compensation purposes. I’d be interested to hear if anyone knows why this is the case.


#11
Yes it was repeated 10 times in the console. Oh Thank you so much @babrahams :) ! now I understood the behavior of stubs !

Also I found out that it is working well(logging “isSimulation - A” on console) when Meteor.methods is divided into server block and client block, even though “isSimulation - A” is not working when used with “this.isSimulation” or “Meteor.isClient” on public block(when write once for both server and client).
(Actually, I’m seriously curious of why this is working, and the others are not… :sob: )
You can check the following code working well.

Code
if (Meteor.isClient) {
    Meteor.methods({
        a: function (x) {
            // stub
            console.log('isSimulation - A : ' + x);
            return 'A-stub : ' + x;
        },
        b: function (x) {
            // stub
            console.log('isSimulation - B : ' + x);
            return 'B-stub : ' + x;
        }
    });

    console.log('start');
    for (i = 0; i < 10; i++) {
        Meteor.call('a', i, function (err, res) {
            console.log('Result of a:', res);
            Meteor.call('b', i, function (e, r) {
                console.log('Result of b:', r);
            });
            console.log('After b is called. Have we seen the result of b yet?');
        });
    }
    console.log('finish');
}


if (Meteor.isServer) {
    Meteor.methods({
        a: function (x) {
            // When it is server, delay a while.
            for (i = 0; i < 10000000; i++) {}
            return 'A : ' + x;
        },
        b: function (x) {
            // When it is server, delay a while.
            for (i = 0; i < 10000000; i++) {}
            return 'B : ' + x;
        }
    });
}


Result
start
isSimulation - A : 0
isSimulation - A : 1
isSimulation - A : 2
isSimulation - A : 3
isSimulation - A : 4
isSimulation - A : 5
isSimulation - A : 6
isSimulation - A : 7
isSimulation - A : 8
isSimulation - A : 9
finish
Result of a: A : 0
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 1
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 2
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 3
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 4
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 5
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 6
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 7
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 8
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of a: A : 9
isSimulation - B : 10
After b is called. Have we seen the result of b yet?
Result of b: B : 10
Result of b: B : 10
Result of b: B : 10
Result of b: B : 10
Result of b: B : 10
Result of b: B : 10
Result of b: B : 10
Result of b: B : 10
Result of b: B : 10
Result of b: B : 10
</blockquote>

#12

See also here, for how to retrieve the stub return value.


#13

In the client, the function inside Method ‘a’ wasn’t exist at the moment you call it, therefore, your last example works, but the previous one doesn’t, because you swapped their sequence. I’ve modified your code and fixed the scope issue in [here][1]. (PS: there is something weird happened in passing a scope variable to the second call ‘b’, and it requires to declare and assign a local var to fix that :dizzy_face: )
[1]: http://meteorpad.com/pad/i4PpiqenqTbJm94zK/Copy%20of%20Methods%20syncronicity%20test