###Whats Meteor.wrapAsync and Meteor.bindEnvironment?
In Part 1, we saw how Meteor abstracted asynchronous code using Fibers and provided an easy way to write JS in a synchronous style. In part 2 we understand how to work with pure asynchronous libraries
Using Fibers we can modify an asynchronous function, to write synchronous like code. However, what happens when we cannot modify the function as in the case of third party libraries?
At ShareThis, our Meteor application uses Amazon’s S3 as a cache and data storage layer. From Amazon S3 documentation, the JavaScript method signature to add to an S3 bucket looks like
“`
getObject(params = {}, callback) ⇒ AWS.Request
//This is how the function would look in Meteor server code
S3.putObject({
Bucket: ‘sharethis-insights-store’,
Key : key,
Body : data
}, function(err, result){
if(result){
console.log(“Done”);
//optionally pass in callback
}
});
“`
The above code will not work as expected, as the callback to S3.putObject is not bound to the current Fiber
In this use case, we use Meteor.wrapAsync and to wrap the asynchronous function into the current Fiber
###What does Meteor.wrapAsync do?
Meteor.wrapAsync takes in the method you give it as the first parameter and runs it in the current fiber.
It also attaches a callback to it (it assumes the traditional node style of using a callback, that is the method takes a callback as its final parameter with two arguments(error and result)of which either one will be null depending on the result of the call
The callback is bound with Meteor.bindEnvironment and blocks the current Fiber until the callback is fired. As soon as the callback fires it returns the result or throws the error.
”`javascript
function synchronousFunc(){
return Meteor.wrapAsync(asyncFunction)
}
“`
**How do we access the error object using the synchronous wrapped functions returned by Meteor.wrapAsync ?**
One method is using try/catch blocks, because in case of error, it will be thrown by the sync function instead of being passed as first argument of the async function callback.
”`
try{
var result=synchronousFunc(params);
console.log(“Success :”,result);
}
catch(error){
console.log(“Error:”,error);
}
// which is equivalent to –
asyncFunction(params,function(error,result){
if(error)
console.log(“error”,error);
else
console.log(“result :”,result);
});
“`
This is great for converting asynchronous code into synchronous code since you can use the result of the method on the next line instead of using a callback and/or nesting deeper functions. It even takes care of binding the callback to the current Fiber (using Meteor.bindEnvironment) for you.
One last note, if you want the callback to the asynchronous library function to do something more (just because) you have to use Meteor.bindEnvironment explicitly or else you get an error stating that Meteor code must always run within a Fiber.
”`
var func = function(cb){
S3.putObject(params, Meteor.bindEnvironment(function(err, data) {
if (err){
throw new Meteor.Error(“Storage on S3 Failed”);
} else{
console.log(“Successfully stored data on S3”);
cb(null, data);
}
}));
};
return (Meteor.wrapAsync(func))();
“`
Few Gotchas that you need to be aware of when using Meteor.wrapAsync
It is meant to work with pure async functions that expect a callback with error and result as arguments. Also, they only work on the server-side (since you cannot use Fibers on the client-side)
Server-side methods such as Meteor.HTTP.call are already wrapped this way. If you call it without a callback, the method will block until the response is received. Otherwise, it will return immediately and then execute the callback once the network response is received.