wrapAsync with bindEnvironment


#1

I have been banging my head against the wall here. I watch an event stream for events. When a certain event comes in, I call npm package function and I have it wrapped but still get the ever famous

Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.

In my startup thread, I have the function that is generating this error as:

var theComment = fetchCommentAsync(commentAuthor, commentPermlink)

Which has fetchCommentAsync wrapped as this:

var fetchCommentAsync = Meteor.wrapAsync(function(comAuthor, comPerm, callback) {
    reddit.api.getContent(comAuthor, comPerm, function(err, result) {
       if (err) throw err;
      callback(err, result)
    })
   })

For the life of me, I can’t figure this out.


#2

Hey @boxxa!

You need to use Meteor.bindEnviroment

Give it a try like this:

var fetchCommentAsync = Meteor.bindEnviroment(function(comAuthor, comPerm, callback) {
    reddit.api.getContent(comAuthor, comPerm, function(err, result) {
       if (err) throw err;
      callback(err, result)
    })
   })

This will wrap fetchCommentAsync into a Fiber.

Hopefully that helps!


#3

So the error went away but when I hit the next step, it is undefined. Is this not hitting async anymore and waiting?

var theComment = fetchCommentAsync(commentAuthor, commentPermlink)
console.log(theComment)

#4

You need to use Meteor.wrapAsync like the following

var theComment = Meteor.wrapAsync(fetchCommentAsync);
var result = theComment(commentAuthor, commentPermlink);
console.log(result);

hope that helps!


#5

I really appriciate the replies but this is now back to the error on the line calling the wrapAsync.

Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.

#6

Ok Now I get it, sorry for mixing stuff @boxxa,

You need to wrap Meteor.bindEnviroment in reddit.api.getContent callback.

var fetchCommentAsync = function(comAuthor, comPerm, callback) {
    reddit.api.getContent(comAuthor, comPerm, Meteor.bindEnviroment(function(err, result) {
       if (err) throw err;
      callback(err, result)
    }))
   }

var theComment = Meteor.wrapAsync(fetchCommentAsync);
var result = theComment(commentAuthor, commentPermlink);
console.log(result);

Let me know if this worked


#7

No worries. Seriously appreciate the quick replies. This has been driving me nuts.

Unfortunately this does not work. I am getting the same error for this line about the fibers.
var result = theComment(commentAuthor, commentPermlink);


#8

One last try, Im curious what should be the proper way, seems Im not as skilled to figure it out, I used them before but with the HTTP package only.

var fetchCommentAsync = function(comAuthor, comPerm, callback) {
    reddit.api.getContent(comAuthor, comPerm, function(err, result) {
       if (err) throw err;
      Meteor.bindEnviroment(callback(err, result));
    });
   };

   var theComment = Meteor.wrapAsync(fetchCommentAsync);
   var result = theComment(commentAuthor, commentPermlink);
   console.log(result);

#9

Same thing. I really appreciate it. You and I had the same ideas haha


#10

As long as the async functions have the (error, result) callback signature, you should be able to do:

const asyncGetContent = Meteor.wrapAsync(reddit.api.getContent, reddit.api);
const result = asyncGetContent(comAuthor, comPerm);
console.log(result);

#11

I guess the @robfallows answer should work.
Or wrap your callback with Meteor.bindEnvironment like

const onDone = Meteor.bindEnvironment((err, res) => {/* your stuff here */});
reddit.api.getContent(comAuthor, comPerm, onDone);

You should also know that if you gonna use any Fiber-await logic (like Collection.insert) in 3rd libraries callbacks you have to wrap those callbacks with Fibers (as Meteor.bindEnvironment does)


#12

After this API call happens, I get the data back and I do use some logic with insert and collection management which is why the waiting is important here and needing to get the return from the API.

It was suggested to me to use async/await on Reddit instead of messing with this stuff. Is that a better path to take than trying to wrap these callbacks?


#13

If there’s a Promise API, that’s a more “modern JavaScript” approach.

Have you tried my answer, above?


#14

I am working on it now. What is the reddit.api coming from? Here is the actual function call I am using that I need to wait for so not sure where the last wrapAsync parameter comes form.

reddit.api.getContent(comAuthor, comPerm, function(err, result) {
if (err) throw err;
return result;
})

Edit: just ran the code and get the same Fibers error on the line:

const result = asyncGetContent(commentAuthor, commentPermlink);

#15

I’d assumed you knew that! Where’s the documentation for the API you’re using?


#16

The logic of Meteor fiber-wrappers looks like:

// wrapAsync
const result = Meteor.wrapAsync(someHandler)() // -> returns result and executes in Fiber.current(). You dont need to pass callback

// bindEnvironment
someHandler(Meteor.bindEnvironment((err, res) => {})) // calls a callback ans executes in Fiber.run()

It’s better to use wrapAsync because its more like modern plain async/await. But make sure your API has a callback as a function last param anyway


#17

Are you still running that inside a callback, as your original post? There’s no need for that - in fact it makes your code more complex. The whole point behind fibers and Promises - especially async/await - is that they remove the requirement for callbacks. Refactoring my code to use your names:

const fetchCommentAsync = Meteor.wrapAsync(reddit.api.getContent, reddit.api);
const theComment = fetchCommentAsync(commentAuthor, commentPermlink);

If you want error handling, use try/catch:

const fetchCommentAsync = Meteor.wrapAsync(reddit.api.getContent, reddit.api);
try {
  const theComment = fetchCommentAsync(commentAuthor, commentPermlink);
  // more processing here
} catch (error) {
  // some error handling
}

#18

I ran this exact code block you posted in the try/catch and throws the error. Assuming my library maybe was bad, I tried another one for giggles that had a slow API call with the sandard call back and still throws the same error so maybe I am not wrapping the NodeJS library method right. Here is the second library I am trying now that has a slow call back and need to wait for the comment to come back before I move on.

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)
[Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.]

#19

There is something else going on here. You need to share more code, or point us to a repo. It should not be this hard. The code you have shown should work correctly.


#20

The library I am using is now in the linked repo. I am ditching the Reddit API and using a more documented platform. Both were running from a streaming API and when a certain operation was detected, I made my next set of API calls which working through this seems like that maybe the cause? Here is the latest full code that I have converted over to the new library linked above. Sorry if I confused this up.

// This is an active stream of events I am listening to
steem.api.streamOperations((err, operations) => {
      if (err) {
        throw(new Error('Something went wrong with streamOperations method of Steem-js'));
        console.log(err);
      }
      
      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)
      }
}