How to get results from async function in a helper?

I have this helper which calls ImageResolver.js to resize images, but it’s not working because resolver return async. I think I need to use a reactiveVar to maintain the template instance or something, but nothing I’ve tried has worked.

  getImage: function(url) {
    resolver.resolve(url, function(result) {
      if (result) {
        console.log(result.image);
        return result.image;
      } 
    });
  }
});

https://atmospherejs.com/simple/reactive-method :wink:

My function is not a server-side call. it runs in the client. I’d have cross-site scripting issues if I tried to port it to a meteor.method

Oh, ok… so you could declare ReactiveVar in Template onCreated(onRendered) and then store the method result in it. It should work. What is the problem with this approach?

I’m not sure but I just get errors

Error: Exception in template helper:
TypeError: Cannot read property 'set' of undefined

Template.discover.onCreated(function(){
  this.imageResolverRequests = new ReactiveVar;


});
Template.discover.helpers({

  getImage: function(url) {

    console.log("page:" + url);
    url = Helpers.addHttp(url);

    var result;
    this.imageResolverRequests.set(result.image);

    resolver.resolve(url, function(result) {
      if (result) {
        console.log(result.image);

        return Template.instance().imageResolverRequests.get();
      } else {
        console.log("No image found");
        return '';
      }
    });
  },

How to get the result after I store it? should I put the whole helper function in the reactivevar?

Template.instance().imageResolverRequests.set(result.image);

In a helper, this is the data context, not the template instance

Thanks, I changed this and tried this code but I get new errors

Template.discover.helpers({

  getImage: function(url) {

    console.log("page:" + url);
    url = Helpers.addHttp(url);

    var result;
    Template.instance().imageResolverRequests.set(result);

    resolver.resolve(url, function(result) {
      if (result) {
        console.log(result.image);

        var res = Template.instance().imageResolverRequests.get();
        return res.image;
      } else {
        console.log("No image found");
        return '';
      }
    });
  },
Error: Cannot read property 'imageResolverRequests' of null
TypeError: Cannot read property 'imageResolverRequests' of null
    at helpers.js?0a6261ab33124edc5a3198af4665deef5c0f08d9:21

should be this.imageResolverRequests = new ReactiveVar();

(with parentheses - and maybe an initial value): http://docs.meteor.com/#/full/reactivevar

1 Like
Template.discover.helpers({

  getImage: function(url) {

    console.log("page:" + url);
    url = Helpers.addHttp(url);

    //var result;
    

    resolver.resolve(url, function(result) {
      if (result) {
        console.log(result.image);
       /*//// set the reactiveVar in the callback (when it arrives) */
       Template.instance().imageResolverRequests.set(result.image);
      } else {
        console.log("No image found");
        return '';
      }
    });
    // return the image into your template (when it gets updated)
    return Template.instance().imageResolverRequests.get();
},

Thanks guys, but still no cigar :frowning:

Error: Cannot read property 'imageResolverRequests' of null
TypeError: Cannot read property 'imageResolverRequests' of null

using this declaration now

Template.discover.onCreated(function() {
  this.imageResolverRequests = new ReactiveVar();

});

Do I need to use Promise to make this work?

null is returned by Template.instance() when it can’t determine the current helper (amongst other reasons), so I wonder if this might work:

Template.discover.helpers({

  getImage: function(url) {
    var instance = Template.instance();
    console.log("page:" + url);
    url = Helpers.addHttp(url);

    //var result;
    

    resolver.resolve(url, function(result) {
      if (result) {
        console.log(result.image);
       /*//// set the reactiveVar in the callback (when it arrives) */
       instance.imageResolverRequests.set(result.image);
      } else {
        console.log("No image found");
        return '';
      }
    });
    // return the image into your template (when it gets updated)
    return instance.imageResolverRequests.get();
},

as well @robfallows’ suggestion, above

it might also be worth declaring the reactive var with null value

Template.discover.onCreated(function() {
  this.imageResolverRequests = new ReactiveVar(null);
});

HI @robfallows
Thats interesting! On the positive side, it’s the first time I got results back! But on the negative side, it seems to spawn an infinite loop! It’s still running after a full 5 minutes. I’m only trying passing it 12 urls.

Ah maybe after I get the result I should destroy the reactVar? I’ll try that.

this should be in helper

The whole setting and checking should be part of something else - event handler or autorun in onCreated.

And yes - setting reactive var in async callback and still using it in same helpers as .get() is effective infinite loop

2 Likes

Good news that the image grabber works.

the infite loop is a tracker issue, everytime it changes, it calls the helper, and the helper is calling the list of urls, and the url is pulled and the helper is called and …

need to break out the logic somewhere

Correct - and that’s how I’ve done it before (in onCreated)! My brain must be elsewhere today.

@robfallows’s .set and .get are both inside the helper

  getImage: function(url) {
    var instance = Template.instance();
    //console.log("page:" + url);
    url = Helpers.addHttp(url);

    //var result;

    resolver.resolve(url, function(result) {
      if (result) {
        //console.log(result.image);
        /*//// set the reactiveVar in the callback (when it arrives) */
        instance.imageResolverRequests.set(result.image);
      } else {
        console.log("No image found");
        return '';
      }
    });
    // return the image into your template (when it gets updated)
    return instance.imageResolverRequests.get();
  },

but yeah, now how to deal with the infinite loop. I thought this might work, but when I try it, i get no images at all

    var imgUrl = instance.imageResolverRequests.get();
    instance.imageResolverRequests.set(undefined);
    return imgUrl;

@shock is correct - putting the set in the helper will cause reactive runaway ™. The setting needs to be outside of the helper - in onCreated is a good place, or in an event if you’re kicking things off from a click.

it’s the whole of this

var instance = Template.instance();
//console.log("page:" + url);
url = Helpers.addHttp(url);

//var result;

resolver.resolve(url, function(result) {
  if (result) {
    //console.log(result.image);
    /*//// set the reactiveVar in the callback (when it arrives) */
    instance.imageResolverRequests.set(result.image);
  } else {
    console.log("No image found");
    return '';
  }
});

perhaps in onRendered