17

I need a way for a meteor call to be synchronous so that when a call is run, the code waits for a result to be done so it can continue to the next line of code on the client.

for example:

clientFunction = function(){

    Meteor.call('serverFunction', function(err,result){})//<--so when this gets a result and    
                                                    //is stored in a session variable
    var doSomeThing = Session.get('whatever') <-- so that your able to use it here
}

I've tried using a while loop to prevent anything from happening until a value is returned, but it seems like it runs after the clientFunction thus throwing it to its demise

any help would be appreciated

LJones
  • 35
  • 1
  • 8
cronicryo
  • 437
  • 1
  • 4
  • 15
  • Why don't you just set/doSomething it in the callback? – Firo Mar 26 '14 at 17:47
  • i would do that and it doesnt happen till after the client code is done running that it would set the value – cronicryo Mar 26 '14 at 17:51
  • If you explain a bit more what you are trying to do that may be helpful. With your current code you could just move your `var doSomething` code into the callback. Based on what you have, there is no reason for this not to work. It is not a good idea to block the client, that's what the asynch callback is designed to prevent. – Firo Mar 26 '14 at 17:59
  • i have 7 collection with specified separated classes to deal with the classes on an individual level so for instance this.add = function (name, number,status,type, brand, agency){ return Projects.insert({ name:name, number:number, status:status, visibility:true, dateCreated: new Date(), dateModified:new Date(), type: type, brand:brand, agency:agency, spots:[], archive:false }); }; but i cant put a call in a function to retrun the value – cronicryo Mar 26 '14 at 18:19

3 Answers3

12

This is a very common question, being asked in various shapes and forms. Most people don't realize when they are making asynchronous calls. The solution, however, is always the same: wrap your method code on the server into a fiber or use a future.

The best practice, I think, is to use the currently available Meteor._wrapAsync function as described, e.g., here: Meteor: Calling an asynchronous function inside a Meteor.method and returning the result

Some other options are described here: https://gist.github.com/possibilities/3443021


Update: The method is now called Meteor.wrapAsync.

Community
  • 1
  • 1
Christian Fritz
  • 20,641
  • 3
  • 42
  • 71
  • i actually did try this and the console kept spitting out that "Object has no method _wrapAsync" Object Meaning Meteor – cronicryo Mar 26 '14 at 19:13
  • Are you perhaps calling on the client? I've just confirmed that in the last two version of meteor this function still exists: on server: `console.log(Meteor._wrapAsync)` prints `[Function]`, i.e., it exists. – Christian Fritz Mar 26 '14 at 22:37
2

Put the code that you want to run after the method completes into the method callback. This is standard for any asynchronous javascript.

clientFunction = function(){
  Meteor.call('serverFunction', function(err, result){
    if (err) {
      alert(err);
    } else {
      Session.set('whatever', result.whatever);
    }
  });
};

That sets the value of the session variable once the method call returns. Now you use Meteor's reactivity to use that variable:

Template.hello.helpers({
  myWhatever: function () {
    var whatever = Session.get('whatever');

    if (whatever) return whatever;

    return 'Loading whatever...';
  }
});
alanning
  • 5,198
  • 2
  • 34
  • 33
  • this dont work in a case you are waiting the value of the `Meteor.call` for do some operation in your next line of code... – ncubica Feb 08 '15 at 00:56
  • 2
    The `Meteor.call` is asynchronous which means by definition that the next line of code after the `Meteor.call` will not wait on its completion. This is how any asynchronous call works. That is why asynchronous calls have callbacks, to allow you to execute code only after the result has come back. If you need to wait on a return value you need to put "your next line of code" into the callback. – alanning Feb 08 '15 at 01:26
  • For completeness, Meteor offers an alternative to putting the code that depends on the return value in the callback. Instead, have your code that depends on the return value use a reactive variable (like `Session`) and then in the callback store the return value in the reactive variable. – alanning Feb 08 '15 at 01:31
  • I totally understand async calls, what Im saying your solution only works for template because the reactive property but not is a general solution, the `_wrapAsync` look like a better approach... – ncubica Feb 08 '15 at 01:41
  • 1
    Putting code that depends on the result of a remote call in a callback is a general solution and is the standard way javascript async code is written. Meteor makes use of [Fibers](https://github.com/laverdet/node-fibers) on the server-side to avoid this. The context for this question is code running on the client (we can tell this because the original question includes the use of `Session` which is client-only). `wrapAsync` (which uses Fibers) is a fine solution for server-side code. On the client, you have to use the callback method as I showed above. – alanning Feb 08 '15 at 03:56
  • This answer is not a synchronous client-side method call. It's asynchronous. – Lawrence Weru Jan 18 '16 at 16:45
  • @LarryW., there's no such thing as synchronous Meteor method calls on the client. The Meteor [docs](http://docs.meteor.com/#/full/meteor_call) point this out as well due to the client not having Fibers. So the way to achieve what the user is after, putting the result of a Meteor method call into a Session variable, is to use the provided callback mechanism. – alanning Jan 19 '16 at 04:36
1

I followed this tutorial and do something like below

This is a meteor server side method

productIdResult:function(searchstring)
{
   {
      var skimlinks_query = Async.wrap(skimlinks.query);
      var title_val="title:\"electric bicycle\"  AND merchantCategory:*bikes*";                     
      var result = skimlinks_query({
                        searchFor: searchstring,
                        start:start,
                        rows:rows,
                        fq: "country:US"
                    });
      return result;
}

And I call it from client like this

Meteor.call('productIdResult',
            searchstring,
            function(error,resul)
            {
                arr[0]=resul.skimlinksProductAPI.products[0].title;
                $( "#op1").autocomplete({source:arr});

            }
);

Note that it is not synchronous, but you get the return value in the callback

Soren
  • 14,402
  • 4
  • 41
  • 67
Sasikanth
  • 3,045
  • 1
  • 22
  • 45