1

I have a Meteor method that tries to manipulate the database, and if successful, calls an async method. I would like to be able to call this method and return the result of the async call or the error from the database manipulation.

This is (roughly) my code on the server:

Meteor.methods({
    'data.update'(id, data) {
        Collection.update({id_: id}, {$set: {data: data}}, error => {
            if (error) {
                // Have method return Meteor error for DB-failure
            } else {
                callAsync(id, (error, res) => {
                    if (error) {
                        // Have method return Meteor error for async call failure
                    } else {
                        // Have method return success(res)
                    }
                })
            }
        })
    }
});

I have read about Futures and Promises, but I'm new to the concepts and I'm not sure when to use what. Preferably I'm looking for a solution that does not rely on any third party libraries outside of Meteor/ES6. Bonus (related) question: what is normally returned after a database manipulation that let's me attach a callback to a method?

Reason
  • 1,410
  • 12
  • 33
  • There's no difference between futures and promises (besides terminology)? Though in JS we call them "promises", and yes you should return one. – Bergi Sep 13 '16 at 12:11
  • As I said, I'm new to the concepts. I know that in Scala they are different, but related, concepts and Wikipedia says "Futures and promises originated in functional programming and related paradigms (such as logic programming) to decouple a value (a future) from how it was computed (a promise)". One big difference between futures and promises in JS is that you can only do meaningful google searches for the latter ;). – Reason Sep 13 '16 at 12:20
  • Yeah, [terminology](http://stackoverflow.com/q/29268569/1048572) is a bit weird in JS. We talk of deferreds and promises instead of promises and futures, and deferreds are being phased out in favour of pure callbacks anyway. – Bergi Sep 13 '16 at 12:57
  • I see :). Anyway, if you have a good answer on how I should use promises in this situation, feel free to write an answer and I will accept it. – Reason Sep 13 '16 at 13:15
  • I don't know meteor well enough so I'm afraid I cannot help you here. – Bergi Sep 13 '16 at 13:21

2 Answers2

3

According to docs

On the server, if you don’t provide a callback, then update blocks until the database acknowledges the write, or throws an exception if something went wrong. If you do provide a callback, update returns immediately. Once the update completes, the callback is called with a single error argument in the case of failure, or a second argument indicating the number of affected documents if the update was successful.

So, if the update is successful, the number of documents affected is returned. In case of insert , the _id of the inserted document is returned.

You can simply pass a third argument to the update function as mentioned.

For promise implementation, you can use Meteor.wrapAsync method. You may also want to look at Meteor.bindEnvironment to achieve that, if you need to pass the state of instance variables too.

Ankit
  • 1,118
  • 13
  • 21
1

You can look into using Promises, but there is another fairly standard way of handling something like this within the Meteor ecosystem. You can use Meteor.wrapAsync to convert your asynchronous callback based function into a Fibers based version. This will then let you leverage return values and exceptions. Here's a quick example:

1) Let's say we have an internal function somewhere called increaseLifetimeWidgetCount that increases our lifetime widget count in the database somewhere, then calls a callback with either an error or the newly updated lifetime count:

function increaseLifetimeWidgetCount(callback) {
  // ...
  // increase the lifetime widget count in the DB somewhere, and 
  // get back the updated widget count, or an error.
  // ...
  const fakeError = null;
  const fakeLifetimeWidgetCount = 1000;
  return callback(fakeError, fakeLifetimeWidgetCount);
}

2) Let's say we then have a simple Method defined that will create a new Widget in our database, call our internal increaseLifetimeWidgetCount function, then return the newly updated lifetime widget count. Since we want to return the updated lifetime widget count, we'll wrap our callback based increaseLifetimeWidgetCount function in a Meteor.wrapAsync call, and return the result:

Meteor.methods({
  newWidget(data) {
    check(data, Object);
    Widgets.insert(data);
    const increaseLifetimeWidgetCountFiber =
      Meteor.wrapAsync(increaseLifetimeWidgetCount);
    const lifetimeWidgetCount = increaseLifetimeWidgetCountFiber();
    return lifetimeWidgetCount;
  }
});

3) We can then call the newWidget Method from our client, with an asynchronous callback, and handle either the returned error or returned lifetime widget count:

Meteor.call('newWidget', { 
  name: 'Test Widget 1' 
}, (error, result) => { 
  // Do something with the error or lifetime widget count result ...
  console.log(error, result);
});
hwillson
  • 1,399
  • 9
  • 7