How to understand asynchronous Meteor.call on the client side

Hey guys! 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!!

html

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

Collection text.js

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

Wrong 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;
 }});

The working 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

It works because the helper runs multiple times. The first time it doesn’t return anything. Just as with your first attempt at this. The second it does however since the session var has been set.

I assume you’ll see this in the console logs too. Btw, calling a method in a helper seems like a massive anti pattern to me.

Could you suggest how to deal with the problem without Meteor.call on the client. I can’t publish the collection to the client because it’s just so huge. But each time, there’s a search in the whole collection to return a random element for display on the client.

Maybe you could run it before the template even gets rendered

Template.texts.onCreated(function(){
 var self = this;
 self.titles = new ReactiveVar([]);
 var index = Router.current().params.index;
 Meteor.call('getTitle', index,function(error, result){ 
  if(error) console.error(error);
  else self.titles.set(result);
 });
});
Template.texts.helpers({
 titles: function() {
  var instance = Template.instance();
  return instance.titles.get();
 }
});
2 Likes

Yeah… as this code. Although it looks like there’s a small error.

self.titles = results;

Should be:

self.titles.set(result);

(Unless I’m missing something).

1 Like

How embarrassing. I fixed it

1 Like

Thanks a lot!! :slight_smile:

Hi jamgold, I tried this and it works! Thanks a lot. Just a teeny-tiny here. I only get this piece work after removing the var before var self.titles = new ReactiveVar([]);. I’m not so sure if it’s because we defined the self here already. Anyway, this a great idea. Thanks again.

You correct, that ‘var’ should not have been there

i have a question.
i am calling get api from server and it returns a 10 character string.
i need that string on client side like

let xxx = ""
Meteor.call('methodInWhichAPICalled', function(er, rr){
  xxx = rr
})
console.log(xxx);

is there any way i can use asyncmethod for Meteor.call without callback… ??

Sure! You can use ES7 async and await. Check this article: