wrapAsync with bindEnvironment

steem.api.streamOperations((err, operations) => {

That’s your problem right there. As soon as you start running code in a callback, you lose the Fiber. Fortunately, that looks like a callback amenable to wrapAsync. Something like:

const asyncStreamOperations = Meteor.wrapAsync(steem.api.streamOperations, steem.api);

try {
  const operations = asyncStreamOperations();
  const opType = operations[0];
  const op = operations[1];
  // When an event comes in, I check to see what op type it is. I am looking for "Comment" types
  if (opType === 'comment') {
    // It is a comment so now I want to fetch teh comment using the getContent call. 
    var commentAuthor = op.author
    var commentPermlink = op.permlink
    const fetchCommentAsync = Meteor.wrapAsync(steem.api.getContent, steem.api);
    try {
      const theComment = fetchCommentAsync(commentAuthor, commentPermlink);
      // more processing here
    } catch (error) {
      // some error handling
      console.log(error)
    }
    console.log(result)
  }
} catch (err) {
  throw (new Error('Something went wrong with streamOperations method of Steem-js'));
  console.log(err);
}

I really appreciate the help. I moved the code you provided into my Meteor.startup and now I am getting this error at startup. Is Meteor not really built for this type of server backend? This seems like an awful lot of troubleshooting and coding to wrap a simple async call so just wondering if there is a better path I should be going down with something like a pure Node solution.

Exception in callback of async function: Error: Future resolved more than once

Meteor can work as well as any other node app in this scenario. However, if you want to make use of Meteor’s sync-style coding (like MyCollection.find()), then you’re automatically working with Fibers. That’s not normally an issue until you want to intermix Fibers with callback-style libraries.

However, all is not lost. You can use callback-style, with or without Promises, and use rawCollection in your Mongo functionality to bypass Fibers. If your package comes with a Promise-based API, that’s even easier. You can then get sync-style using async/await and can intermix with Fibers if needed.

I’ve published a couple of articles on how to do this:

which you may find helpful.

2 Likes

I can confirm that this code works exactly as intended:

import { Meteor } from 'meteor/meteor';
import { Results } from '/imports/both/Results';
import steem from 'steem';

const operations = steem.api.streamOperations((err, operations) => {
  const opType = operations[0];
  const op = operations[1];
  // When an event comes in, I check to see what op type it is. I am looking for "Comment" types
  if (opType === 'comment') {
    // It is a comment so now I want to fetch teh comment using the getContent call.
    const commentAuthor = op.author
    const commentPermlink = op.permlink
    steem.api.getContent(commentAuthor, commentPermlink, (err, result) => {
      result._id = `res_${result.id}`;
      Results.rawCollection().insert(result);
    })
  }
});
1 Like

You sir, are amazing. Thank you for taking the time on this. This works perfectly and i get the event and the data. I just need to convert my other collection lookups that exist in this code since that is the only thing throwing the errors now.

Thank you again so much!

1 Like

One more question on this.

Do you have more info on the rawCollection() use? I have a lot more logic that uses inserts and finds after this. This is the first time I am seeing this used and reading the docs which outline it exposes the mongojs library directly with their call backs so I would assume I need to operations below in the wrawAsync so it waits for a find and insert to come back before moving on?

Results.rawCollection().insert(result);
Results.rawCollection().find({id: 1234234})

The current MongoDB library returns Promises if no callback is used, which means you’re able to use them with async and await. That’s a really easy way to keep sync-style coding and not lose the benefits of Fibers you may have used elsewhere (Meteor’s async/await retains Fiber context).

Alternatively, if you import { Promise } from 'meteor/promise' in your modules, you can use result = Promise.await(anythingWhichReturnsAPromise);. Meteor’s Promise.await uses Fibers to allow Promise-based functions to be used without async and await. The choice, as they say, is yours.

So, my example was somewhat over-simplified to make a point! However, the changes you would need to make it more correct are fairly minor. I cover all this in my articles (listed above).

There are a couple of caveats when using the underlying MongoDB methods:

  1. The cursors returned are not the same as the cursors used in Meteor. That means that if you need Meteor cursors, you must use Meteor’s isomorphic MongoDB methods in those situations (publications, for example).
  2. They only work on the server. Your client code must continue to use Meteor’s interface.