5

My client is making a call to the server.

Meteor.call('someRequest', params, onAllDoneCallback);

that is processed by (server code)

Meteor.methods({
    'someRequest': function(params, cb) {
        anAsyncFunction(params, cb);
        return true;
    },
});

I'd like the onAllDoneCallback to be triggered on the client side once the anAsyncFunction has finished and triggers its own callback.

However, in Meteor it seems that the second argument of someRequest is ignored and that the onAllDoneCallback is triggered with what someRequest returns, which here is true and which is called before that anAsyncFunction has finished.

In my case I'm more concerned about the timing issue (I'm using it to tell the client that the processing is finished, and not just that the request is well received) but other will probably want to call the callback with arguments from anAsyncFunction

Guig
  • 9,891
  • 7
  • 64
  • 126

2 Answers2

4

What you are doing now is passing a function to the server. If that does work, it's very insecure. What you want to do is create a future and then use it to manage the asynchronous function. Here is an example:

let Future = Npm.require('fibers/future');
Meteor.methods({
  someRequest: function (someArg) {
    // for security reasons, make sure you check the type of arguments.
    check(someArg, String);

    // future is an async handler.
    let future = new Future();
    // this is a function for a generic node style callback [ie, function (err, res)]
    let resolver = future.resolver();

    // run your function and pass it the future resolver
    // the future will be resolved when the callback happens.
    anAsyncFunction(someArg, resolver);

    // this meteor method will not end until future has been resolved.
    return future.wait();
  }
});

Alternatively, Meteor provides a wrapAsync that provides similar functionality of wrapping async functions in futures so they can run in meteor methods. That is:

let wrappedAsyncFunction = Meteor.wrapAsync(anAsyncFunction /** second argument is `this` binding*/);
return wrappedAsyncFunction();
corvid
  • 10,733
  • 11
  • 61
  • 130
  • I had just found about it! What's your take on `future` vs `wrapAsync`? https://themeteorchef.com/snippets/synchronous-methods/#tmc-using-wrapasync seems to recommend `wrapAsync` as a good default – Guig Jan 26 '16 at 19:53
  • 1
    If you need more granular control over the "flow" of execution, it's _very_ beneficial to know how futures work. For example, if you are using an `EventEmitter` and need to unbind listeners after resolving. [This guide](https://gist.github.com/possibilities/3443021) has been a life-saver for me when using meteor's future wrappings, good to keep with you – corvid Jan 26 '16 at 19:55
  • Best example on using futures for async methods I found! – Jankapunkt Jul 18 '17 at 11:05
0

adapting this answer: Meteor: Proper use of Meteor.wrapAsync on server

You have to use Meteor's wrapAsync api, which takes a function that accepts a callback as its last argument, the callback being like function(error, result){}. So it will look like:

Meteor.methods({
    'someRequest': function(params){
        var wrap = Meteor.wrapAsync(anAsyncFunction);
        return wrap(params);
    },
});
Community
  • 1
  • 1
Guig
  • 9,891
  • 7
  • 64
  • 126