• Node.js
  • Tutorial

Using Futures

In the last episode, I introduced the fibers library, which lets us write synchronous code without blocking the event loop. In this episode, I'm going to show you how to use the Future's library, which is a nice wrapper around the fiber API.

So let's look at the code that I have from last time quickly. Down at the bottom I have handle request three times. And in handle request I run that code inside of a fiber. And you can see the handling request method gets printed. And then we do some asynchronous work with the fiber. And we wait for the results.

But in the meantime, we yield to the next fiber. And so handle request two gets called. And then similarly, handle request three gets called.

And so over on the right, the handling request line gets printed out almost at the same time the three requests. And then inside to doAsync work with fiber, unlike the regular doAsync work where we just set a timeout for about three seconds and then call a callback, inside the doAsync work with fiber we grab the current fiber. And then we yield until the Async work is done.

And when it's done, we call the runMethod on the fiber which then returns the result to results. And then we return this results to the caller. And so about three seconds later over on the right we get the results of the Async work all at once.

And just to recap, Async work just means that code is run on the event loop at a later time. It doesn't necessarily mean that the code is run in parallel. But if you're doing I/O work, the operating system will do its best to parallelize that work. In this case, the setTimeout just put some code back onto the event loop about three seconds later.

So now let's use the Future's library to make this code a little bit nicer. And I could show you some patterns that you can use. OK. Over in the left up top underneath the fiber module, I'm going to paste in the Future module. And so I can include the Future module by requiring fibers forward slash Future.

And the first thing I'm going to do with the Future library is change the doAsync with fiber. I'm going to create a new method called doAsync work with Future. And just show you how the API is a little bit different.

So of course, this assumes that we have control over the code that we're writing. And in a little bit, I'll show you how to wrap an existing library. So this time, what I'm going to do is I'm going to create a new Future. And then what I can do is I'll do my normal setTimeout to simulate the Async work.

But inside of the setTimeout, instead of doing anything with a fiber, what I can do is call the returnMethod of my Future. And I can pass parameters to the return value. So in this case, I'll pass the results of work. And we'll execute this about three seconds later, just like above. And then what I'll do is just return Future.

Now down on 42, instead of calling the doAsync work with fiber, I'll replace that with the doAsync work with Future. And then I'll go over and run the application again.

Now you see the results that are printed are different than what we had before. It doesn't look like doAsync work with Future is yielding. We're not waiting for that before we print out the after doAsync work call. And the reason for that is what we need to do is call the wait method on the Future. This method is returning immediately. And it's returning a Future object, which has a method on it called wait.

So if we go back and run this code again, now it seems to work properly. So one thing we might want to do is if we don't want our caller to have to remember to call the wait method, we can just go ahead and do that here. So instead of returning the Future, we'll just return the result of calling wait. And we'll go back and make sure that it still works. Great. It does.

OK. So this required me to actually rewrite my initial method. Let me just jump up. It required me to rewrite the doAsync work method. But what if we don't actually want to rewrite it? We just want to wrap it. For example, if we're using another library that we don't have control over.

So what I can do is I'll create a function called wrapAsync work. And in this case, what we want to do is actually call the doAsync work method. But we want to make it work with Futures.

And so similar to below, I'll just create a new Future. And then this time I'll call doAsync work. But as a parameter I'm going to call Future.resolver, which is going to return a function in the style of the error comma result callback. I'll explain that in more detail in a second.

And then similarly to below, I'll just return a Future.wait. So let's jump down to our handle request method and make sure that this works. So this time I'll call the and then just jump over to the console. And I'll run this again.

Great. So I'm still getting the same result. Let me pull out my pen and show you what this is doing in case it isn't clear. So this time what we did was we wanted to use the original method, doAsync work, that takes a callback as a parameter. And as the parameter, what we're doing in wrapAsync work with Future is we're passing the result of calling this resolver method.

And so what this resolver method is going to do is it's going to return a function that looks like this. Function with an error as the first parameter and the result as the second. So it's going to return a callback function. But it has all the plumbing embedded in it to be able to work with Futures.

And so what it'll do is it will actually resume or return the Future once the Async work is done. And so then what we do is we return Future.wait which yields to the next fiber.

And so this is already looking like a nicer interface around fibers. But actually we can do something even easier. Let me rewrite this function, and I'm going to create as a variable this time. And what I can do is I can use the RAF method of Future. And as a parameter just pass the method that I want to wrap.

And then I'll just delete this. Or we'll comment it out. I'll delete it.

And so the Future.wrap method does exactly what we just did. It just wraps the doAsync work method, passing in a callback that works specifically with Futures. And so if I come over here and I type node main js again, let's see what the error is. doAsync work is not defined.

And so let's jump up here and see. Because I misspelled it. Let's try it one more time. OK. So this time it's not waiting because Future.wrap doesn't actually call the wait method.

And so what we need to do is go down to where we actually use this method and call wait. And we'll try this again. OK. Great. So it works. And so by calling the wrap method of Future passing in our asynchronous function, as long as our asynchronous function works with a callback of the form error result, we can use this wrap method.

But then one limitation of using the wrapMethod is we do have to call the waitMethod if we want to wait on the results. So if you don't want to have to do that, you may prefer just to write your own wrapper around the asynchronous code like I've done here.

OK. So there's one more thing we can do with the Futures library to make our handle request code a little bit nicer. I can actually get rid of putting this in a fiber altogether. And instead, call the Future method on this function. And I'll explain this. Let's just make sure that it works first. Great. It looks like it does.

So the Future library provides an extension to the function prototype called Future. And so what we're doing here with this Future method is we're calling it on this function. And so the effect of that is it's actually returning a new function and assigning it to this handle request variable. And that new function takes care of wrapping all this into a fiber for us.

And so hopefully this episode showed you how the Future's library can make working with fibers a little bit easier and allows us to nicely wrap existing Async libraries. So I've only shown you a few of the methods available on Futures, but there's a couple of more. And so if you go to the GitHub page for node fibers and you check out the Futures section, you can learn a little bit more about the methods available on them.

The other thing that I recommend is that you just go and look at the source. And so if we click on the Future.js, or we clone this repository, you can see the source like the Future method added to the function prototype and how Future.wrap is implemented. And if we scroll down a bit, we can see all the methods that are available on the Future prototype.