SOLVED: Having trouble with promises in Meteor methods

@robfallows might have a solution to this one.

I’ve got a method that’s called from the client side, to take a code and generate an Asana OAuth token from that, and then store the information. It looks like this:

export const setAsanaToken = new ValidatedMethod({
  name: 'setAsanaToken',

  validate(args) {
    check(args, {
      code: String,
    });
  },

  async run({ code }) {
    if (!Meteor.isServer) return;

    const client = createAsanaClient();
    const credentials = await client.app.accessTokenFromCode(code);
    Meteor.users.update(this.userId, {
      $set: {
        asana: {
          accessToken: credentials.access_token,
          asanaUserId: credentials.data.id,
          expires: credentials.expires_in,
          refreshToken: credentials.refresh_token,
        },
      },
    });
  },
});

I get the usual “must run within a fiber” error. But I’ve seen people successfully use async/await with Meteor methods, so what am I doing wrong here?

EDIT: Ah, got it. Had to change the user update line to Meteor.users.rawCollection().update(...).

1 Like

I think Promise.await might be a less invasive solution compared to async/await/rawCollection

Hmm. Looking at this more closely now (i.e. not on my phone), I am confused. The code you posted should have worked. Meteor’s async/await should work as you’d expect with Meteor’s normal, minimongo-style methods (it always has for me). There should be no need to use rawCollection() or Promise.await().

I need to try this for myself.

Yeah, I’m not really clear why I got the typical fibers error until I changed it to rawCollection(). Strange.

I’m not familiar with this. What’s the difference between this and just using await? I did a search but only ES7 async/await came up. Do you have a link to a reference?

I really have not seen any documentation on Promise.await, but basically, meteor ships its own promise library and Promise.await is a fiber-aware utility that you can use.

Oh and on a side note, sometimes some promise libraries and meteor’s promise clash and you may need to explicitly import { Promise } from 'meteor/promise'

2 Likes

This is exactly what we had to do when we had some issues with NPM packages in the past.

I do hope the system gets improved… somehow. We have had to do hacky work-arounds with nesting many Future/Promise.await to get some packages running. Makes the code needlessly complicated/confusing to read. That’s my 1 major issue we’ve had with Meteor’s NPM support.

@serkandurusoy A thousand thanks!! Promise.await() totally solved my issue. Regular ES7 async/await is not compatible with Meteor and Fibers, as you said.

1 Like

Clearly, no-one has read my article on async/await :cry:

I show an example of Promise.await() and explain the difference and usage:

One limitation of await is that it may only be used inside an async function. However, Meteor’s Promise library has a utility method Promise.await, which lets us await inside a normal, synchronous function — or a normal method if we wish…

Yes it is - as long as you’re using the ecmascript package and haven’t overriden/changed Meteor’s babel transpiling with some custom babel.

The transparent way Meteor’s inbuilt Promises work with other Promises and its own classic Fibers based code means we get the benefit of both.

And I actually show this working by mixing Bluebird Promises into Meteor’s async/await and Fiber methods (Collection.insert).

4 Likes

I would like to openly apologize for missing the information you’d already shared in your blog post, which by the way, was one of the best posts I’ve recently read.

Although, in retrospect, I was kind of referring to a more or less official and topical documentation on meteor’s promise library. Meteor indeed has lots of “hidden, undocumented” gems like promise, webappinternals, httpinternals, mongointernals, mailinternals as well as some “private” yet exposed and very useful _methods on accounts, collections, subscriptions, meteor etc.

2 Likes

No worries - my concern was that I’d failed to pitch the tone / content in an easy-to-consume way. As this was essentially my first very visible article I wanted to make sure it was suitably bite-sized, but with enough detail to make it useful and informative.

So, any feedback to make the next one more likely to be memorable (?) would be great.

And, yes, there’s lots of hidden gold if you’re prepared to dig into the source - which really shouldn’t be necessary. As far as Promise itself (the Meteor version), it’s pretty basic - except for Promise.await(), which is really useful. I guess the guide could do with something in this space.

1 Like

As far as constructive critisism goes, an article should not convey more than 2 or 3 topics and each should receive comparable emphasis. On this regard, I think the mention of meteor promises was perhaps relatively too burried.

Otherwise, an avid reader should be able to recognize all those topics regardless of emphasis.

1 Like

That’s fair - I nearly didn’t include it at all, on the grounds it wasn’t strictly necessary.

1 Like

Sorry Rob! To be honest, I skimmed it (I was juggling two things at once and had to get a feature finished ASAP). I thought I’d give regular async/await a shot since it was supposed to work, but as you pointed out, if babel takes things over, it overrides the Meteor ecmascript's version and it fails. And in my case, I do have this in my .babelrc:

{
  "plugins": [
    "babel-root-slash-import",
    "transform-decorators-legacy"
  ],
  "presets": ["es2015", "react", "stage-2"]
}

I’m assuming the stage-2 inclusion (which includes stage-3 of course) is bringing in babel’s async/await.

Sam, I’m trying to do something similar - my Meteor app uses username/password for logging in, then user can add Asana service. Not to let them login to app, but to access Asana features. I’m using accounts-base and the oauth packages, and had overridden OnCreateUser, but can’t access Meteor.user and have to find a matching email address to store Asana service.

Your approach seems much better. Are you using Meteor oauth package for the login and flow? What did you use to get the code that you pass to this server method (not sure how to “Meteorfy” the webserver example from node-asana)?

Thanks for the inspiration on this!

1 Like

Hey Tim!

I’m manually doing all my Asana OAuth, and using their refresh token to automatically renew expired tokens as well. Private message me if you’d like to see any code. My set of Meteor methods includes ones for Asana task creation, authentication, fetching projects and users, etc.