Transcript
  • Meteor
  • Tutorial

Meteor._wrapAsync

In this video, we're going to talk about a Meteor function called wrap async. This lets us wrap an asynchronous function so that we can call it synchronously. And it works with Fiber's and Meteor's bind environment.

Before we get into the code, let's review what it means to be asynchronous and what an asynchronous function looks like. Up top I've drawn a depiction of an event loop. And what you see is that this event loop kind of looks like a bunch of arrays. And it's moving from left to right in this diagram.

And on the far right, the function F1 is what's currently executing. And when F1 is done, we'll execute F2. And when that's done, we'll execute F3 and then F4 and so on. And when this tick of the event loop is done, we move to this one and start executing F5, F6, and F7.

Now, in F1 we might call an asynchronous function. This means that we're going to put some code back onto the event loop at a later time. So for example, you might call nodes read file function. The signature here is a little off, but I hope it illustrates the point. Oftentimes when you call these asynchronous functions, you pass a callback function as a parameter. And that callback function typically takes an error as its first parameter and a result as its second, just by convention.

Now, the benefit of executing this function later or putting it back onto the event loop later is that sometimes the operating system can do some work for you in parallel. So for example, when we're reading this file, the operating system will read the file and our event loop can continue to move forward. And when the operating system is done reading the file, we get notified and then take this callback function and put it back onto the event loop. And so this becomes F8. And F8 will execute when it makes its way to the front of the line.

So we call this function asynchronous because it puts something back on the event loop at a later time. So we can use Meteor async to wrap this asynchronous function so that we can call it in a synchronous style without losing the performance benefits down here. Let's look at some code.

In my code file, we're just going to be working on the server. And I've created a function called call me later async, which takes a name and a callback as parameters. And just calls set timeout. Even though we're just using set timeout and not a fancy function like read file, it's still asynchronous because we're putting something back onto the event loop at a later time.

And all this function is going to do is in about two seconds, if there's a call back that was passed in, then we'll call it with a null error and the result, which is just a message that says, hey there to the name. I've created a Meteor method so that we can call this function from the client.

And the way that I would normally call this asynchronous function is I would pass any parameters to it like the name and then as the final parameter I'd pass a callback function that would take an error as a first parameter and the result as the second. And then I might do something like print out the result to the console. In the browser debugger, I'll called meteor dot call and pass the name of the method. And you'll see in about two seconds that the servers prints out "Hey there Chris."

So what I'd like to do is to be able to wrap this function so that I can call it in a synchronous style. That means I'd like to create a variable called result and just assign it equal to the result of calling the function call me later. And I don't want to have to pass this callback function. And I might just return the result and I'll print it to the console so we can see it.

So to do that, I can wrap a call me later async function. I'm going to call this function call me later sync. And assign it to the result of calling meteor dot underscore wrap async. The wrap async function is prefixed with an underscore because it's still a private function. So that means it might change. But that's OK, we're going to play with it anyway. So I passed the asynchronous function as a parameter to wrap async.

And now instead of calling call me later async, I can call the wrapped function. And instead of passing a callback function as the last parameter, I can just pass the name parameter. And when I get a result, I'll print it to the log and then return it. Let's make sure this still works. If I type meteor dot call and provide the call me later method name and then Chris as a parameter and hit Enter, in two seconds on the server I see the message printed to the console with the result "Hey there Chris."

Now the important thing to note is that even though we're wrapping this asynchronous function so that we can call it in a synchronous style, we're not blocking the event loop. And that's because we're using Fiber's and Meteor's bind environment behind the scenes. But it does allow me to call the function in a synchronous style. I can just wait on the results and then use it in the rest of this function.

In this example, we've used meteor dot underscore wrap async to wrap a sort of silly function called call me later async. Just to demonstrate the concept. But of course, you can use wrap async on any asynchronous function that takes a call back as a last parameter where the call back expects an error as the first parameter and a result as the second. So this can be useful when wrapping third party APIs, for example.

There's also a number of node modules that have asynchronous functions, like the read file function of the FS module. And you can see that the function, the call back function signature, has an error as the first parameter, and in this case data or results as the second. And so we could use media wrap async if we wanted to wrap this function. And Meteor has actually already wrapped a few methods for us, like set timeout and set interval and all of the HTTP module from node.