Combining synchronous and asynchronous logic inside method call

Hello everyone,

I’m having a form users can use to post comments on my webapp. What I’m doing right now is parsing the entered text, look for URLs, parse the webpage if possible and add the pages title inside the record.

Here is some pseudocode of what I’m currently doing:

Meteor.methods({
'Comment:save': function (msg) {
        var urls = extractUrls(msg);
        if(urls.length) {
            // getHTMLOfURL() will spawn a phantomjs instance and
            // request for the HTML of provided url. This can take a big amount
            // of time
            var $ = cheerio.load(getHTMLOfURL(urls[0]).value);
            var pageTitle = $('title').text();
        }
        return MessagesCollection.insert({
            username: msg.username,
            text: msg.text,
            linkedWebpage: {
                title: pageTitle||undefined
            }
        });
    }
});

The deal is that the pageTitle is not that important from a UX point of view, so I wouldn’t mind if I could update the record with the correct name at any time in the future. So what I want to do, is this method to return the ID of the new message, start it’s request and whenever this request is done, update the corresponding record. That of course assumes solution that maintains reactivity so that the template will automatically update whenever phantomjs completes it’s job.

Is what I’m trying to do possible using only server side code?

Hey @loupax, did you ever figure out how to do this?

I think the best practice for long running code is using a job queue with background workers.

This could get the job done (partially probably never had to use it) https://atmospherejs.com/vsivsi/job-collection.

Actually yes, I managed to make it work using the Futures library, which is syntactic sugar for Fibers

What you want to do is create a function that runs your asynchronous code, and use the following structure:

function async2sync(){
    var future = new Npm.require('fibers/future');
    asyncCall(function sucess(result){future.return(result)}, function fail(err){future.throw(err);});
    // Wait for the asyncCall to end before you finish the function call 
    return future.wait();
}

After that, your async2sync function will behave as a synchronous one and you’ll be able to use it in your methods.

For a more complete example, see my usage on my original problem at https://github.com/Loupax/Meteor-web-scraper/blob/master/web-scraper.js

https://github.com/meteor/promise resp https://github.com/okgrow/meteor-promise/ would have worked too, these days probably the best option…

this is how I wait in Facebook api app (coffeescript detected :smiley: )

FBQuery = (query, method, fbObject) ->
  if typeof method is 'undefined' then method = 'get'
  console.log "query is: " + query
  data = Meteor.sync((done) ->
    fbObject[method](query, (err, res) ->
      done(null, res)
    )
  )
  data.result

And yes, using it in methods

Meteor.methods(
  getUserData: ->
    fb = new Facebook(Meteor.user().services.facebook.accessToken)
    FBQuery '/me', 'get', fb
...

Wow, thank you everyone for your suggestions, these are really informative!

@davethe0nly, I can definitely see jobs being useful in many scenarios, especially where I don’t need the data right away, or where the data goes on to do something else, that’s a good technique to have in my back pocket. For my application, I need the data as soon as it’s processed.

@markusgattol promises look very promising, I think this is what I was really looking for, I especially like that it works on client and server functions.

@shock I ended up using something very similar to what you have there, which is using the Async.runSync function , which is the updated version of the now deprecated Meteor.sync, which comes with the “meteorhacks:npm” package.

How does the jobs package compare with the synced-cron package, any idea? It seems to have more features, but it’s hard for me to tell the difference
https://atmospherejs.com/percolate/synced-cron