Async Meteor Method calls
Tags: javascript, meteor, async
Published: 2020-09-17 11:24:00

Meteor is mostly backward compatible to even < 1.0 projects. Therefore lots of code is still callback based. So is the [`Meteor.call`](https://docs.meteor.com/api/methods.html#Meteor-call) method. You can easily wrap it into a Promise-based approach:

```javascript
export const callMethod = ({ name, args }) => 
  new Promise((resolve, reject) => {
    Meteor.call(name, args, (error, result) => {
      if (error) {
        return reject(error)
      }

      return resolve(result)
    })
})
```

Although `Meteor.call` supports multiple arguments I rather prefer to pass a single object with named arguments to keep the code more expressive.

You can then use `callMethod` natively in an async environment:

```javascript
Template.myTemplate.events({
  'click .some-button': async function (event, templateInstance) {
    const age = await callMethod({
      name: 'getAge', 
      args: { name: 'John Doe' }
    })
    console.log(age) // whatever the method returned
  }
})
```

Additionally you can "hook" into these calls and thus mix callbacks with promises and create a decent UX experience when method calls are part of user interactions:

```javascript
export const callMethod = ({ name, args, prepare, receive, success, failure }) => {
  // before call
  if (typeof prepare === 'function') {
    prepare()
  }

  // create the promise
  const promise = new Promise((resolve, reject) => {
    Meteor.call(name, args, (error, result) => {
      // on received
      if (typeof receive === 'function') {
        receive()
      }

      if (error) {
        return reject(error)
      }

      return resolve(result)
    })
  })

  // on success
  if (typeof success === 'function') {
    promise.then(success)
  }

  // on error
  if (typeof failure === 'function') {
    promise.catch(failure)
  }

  return promise
}
```

The code can then be used for example to display a "waiting" indicator:

```javascript
Template.myTemplate.events({
  'click .update-button': async function (event, templateInstance) {
   const updatedDoc = await callMethod({
     name: 'updateUser', 
     args: { name: 'John Doe', age: 42 },
     prepare: () => templateInstance.state.set('updating', true),
     receive: () => templateInstance.state.set('updating', false),
     failure: er => alert(er),
     success: () => alert('updated')
   })
   // process updatedDoc if desired
})
```

<hr>
I regularly publish articles here on dev.to about **Meteor** and **JavaScript**. If you like what you are reading and want to support me, you can [send me a tip via PayPal](https://paypal.me/kuesterjan).

You can also find (and contact) me on [GitHub](https://github.com/jankapunkt/), [Twitter](https://twitter.com/kuester_jan) and [LinkedIn](https://www.linkedin.com/in/jan-kuester/).

Keep up with the latest development on Meteor by visiting [their blog](https://blog.meteor.com/?utm_source=jkuester&utm_medium=online&utm_campaign=Q2-2022-Ambassadors) and if you are the same into Meteor like I am and want to show it to the world, you should check out the [Meteor merch store](https://shop.meteor.com/?utm_source=jkuester&utm_medium=online&utm_campaign=Q2-2022-Ambassadors).