wrapAsync with npm knox - problem

I am trying to wrapAsync an npm knox buffer put to s3.
The knox client.put function has its own listeners. eg on(‘response’)
How can i make this work synchronously in a meteor method?

I tried wrapping all the knox client functions - but that did not work eg :

var knoxS3Put = Meteor.wrapAsync(s3client.put, s3client);
var knoxS3On = Meteor.wrapAsync(s3client.on, s3client);
var knoxS3End = Meteor.wrapAsync(s3client.end, s3client);

Then I tried this :

var putToS3 = Meteor.wrapAsync(s3client.put, s3client);

var req = s3client.put('/quotes/'+newFileName, headers);
var putToS3_result = putToS3(newFileName, headers);

putToS3_result.on('response', Meteor.bindEnvironment(function(res) {
          console.log(res)
          return res;
}));

putToS3_result.on('error', Meteor.bindEnvironment(function(res) {
          console.log(res)
}));
putToS3_result.end(content)

but that also did not work - am at a loss;

I also tried this :

var req = s3client.put(newFileName, headers);

var on = Meteor.wrapAsync(req.on, req);
var on_result = on('response');
console.log(on_result);

var end = Meteor.wrapAsync(req.end, req);
var end_result = end(content)
console.log(end_result)

I just get socket hang up with this !

Also tried this :

        var syncFunc = function (fileName,headers,callback){
             var result;
             s3client.put(fileName, headers)
                 .on("response", function(res){ result = res;})
                 .end(content, callback(null,result));
        }

       var emitterFunction = Meteor.wrapAsync(syncFunc)
       var emitterFunction_Result = emitterFunction(newFileName, headers, content);
       console.log(emitterFunction_Result);
       return emitterFunction_Result

did not work - " callback is not a function "

Meteor.bindEnvironment ensures that the execution context of the callback runs in a Meteor fiber. That means that functions relying on a fiber (like MyCollection.insert()) will work. However, it does not provide a means towards sync-style programming. So, if you don’t need a fiber in your callback, there’s nothing to be gained from bindEnvironment.

Meteor.wrapAsync does let you use sync-style coding with asynchronous functions. Unfortunately for your use case, it’s written assuming npm’s de facto standard of a callback signature of (error, result). That means it won’t work as you want for these functions: your res will be treated as an error!

If you want sync-style coding here (i.e. avoiding nested callbacks) you could wrap the S3 functions in Promises and use async/await. Any fiber in the calling function’s body will be preserved should it be needed.

So: either forgo sync-style and use good old fashioned nested callbacks or wrap the functions in Promises and use async/await.

Edit: Add link to Meteor Guide: https://guide.meteor.com/using-npm-packages.html#async-callbacks

Sorry I should have been clearer - I am running this in a method - where the method is returning to the client the filepath of the uploaded file on s3.

That being the case - doesn’t that rule out nested callbacks?

Yes.

It sounds like you’ll need to wrap in Promises … or you could wrap in your own fibers/futures … or perhaps wrap the s3 functions to use an (error, result) callback and then wrap those in Meteor.wrapAsync! The possibilities are almost endless :wink:.

I’d be inclined to give Promises and async/await a shot. That’s the most ecosystem-friendly approach right now.