简体   繁体   中英

How to understand asynchronous Meteor.call on the client side

I'm working on a Meteor project and want to get the return value of Meteor.call in template helpers on client side. At very first, I just set a variable in the call back function and get the variable's value outside the Meteor.call . I found out the code after Meteor.call doesn't execute at all. Then I searched a bit and use Session, it works. But I don't really understand the reason. Here's my original code and modified code. Can anyone explain a bit for me? Thanks!!

Original wrong code: html

 <div id="text-result-main">
   <h2>{{title}}</h2>
 </div>

js

Template.texts.helpers({
  title: function(){
    var index = Router.current().params.index;
    Meteor.call('getTitle', index,function(error, result){ 
       titles = result;
    });
    console.log(titles);
    return titles;
 }});

Collection text.js

Text = new Mongo.Collection("text");
Meteor.methods({
  'getTitle': function(myindex){
     return Text.findOne({index: myindex}).title;
  }});

The working code: js

Template.texts.helpers({
 title: function(){
      var index = Router.current().params.index;
      Meteor.call('getTitle', index,function(error, result){ 
        Session.set("titles",result);
      });
      console.log(Session.get("titles"));
     return Session.get("titles"); 
}});

Notice that I didn't publish Collection Text to the client at all because it's just so huge. Every time when I refresh the page when running the wrong code, I can't see the content of "title" or see it on the console. But when I set the session, it works. I don't really understand how it works here. Thanks

There is two issues Asynchronicity and Reactivity

This affectation

Meteor.call('getTitle', index,function(error, result){ 
   titles = result;
});

inside the meteor call is executed but in a asynch way. So the return of your helper is immediately called, and return a empty value.

Try it out in the console of your browser.

But then, why your template render correctly with {{title}} when you use a Session Variable ? It's because the Session is a reactive data source, witch means that every change to it trigger a re-computation of all templates involving this piece of data.

Here is a timeline:

  • Methods is called

  • Return empty value

  • Method is executed, setting variable value

  • If the Variable is a reactive data source, template is re-computed. ( in your case, the session is a reactive data source. )

To go further

I would use a reactive var in that case, it's very close from a session variable, but the scope is limited to a template.

A good read on Reactive data source: http://richsilv.github.io/meteor/meteor-reactive-data-types/

The problem is the fact that Meteor.call() is asynchronous when paired with a callback.

So when title() starts executing, it does not wait for your Meteor.call() invocation to return a result (or possibly an error). It continues execution. This is called asynchronous execution.

In short, you are trying to log the value for the key titles which doesn't exist in Session (since the state of your asynchronous Meteor call is unknown, at this point of time).

Try moving the console log statement into the callback paired with your Meteor.call() and you can see the result once it has successfully been set in Session .


A workaround to your problem is to make your Meteor.call() synchronous like this:

Template.texts.helpers({
 title: function(){
      var index = Router.current().params.index;
      var result = Meteor.call('getTitle', index); // <--- this is synchronous code now
      Session.set("titles",result);
      console.log(Session.get("titles"));
      return Session.get("titles"); 
}});

Removing the callback makes Meteor.call() behave synchronously.

If you do not pass a callback on the server, the method invocation will block until the method is complete. It will eventually return the return value of the method, or it will throw an exception if the method threw an exception.

(from http://docs.meteor.com/api/methods.html#Meteor-call )

Why not use something like this:

title: function(){
    var index = Router.current().params.index;
    var a = Text.findOne({index: myindex}).title;
    console.log(a);
    return a;

without methods

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM