This course has been updated! We now recommend you take the Complete Intro to Real-Time course.
Transcript from the "Asynquence" Lesson
>> Kyle: Where we left off before is we had just started to dive into coding in Node.js and we built our first one .js program. Which if you recall, it was sort of a command line program in that we could pass in a filename and it would read that filename.
[00:00:23] Get the contents back, send them back to us and it would even simulate the idea of multiple levels of asynchronicity. Now, let's assume that there's more complexity to real world programs, obviously there will be. So the first thing that we might observe is we're already starting to get into that nested callback syndrome.
[00:00:47] And we spent quite a bit of time yesterday talking and even this morning talking about why nested callbacks, just in general. Not this specific example, but in general, they tend to lead us down a path that's less flexible, less reliable, loss of implicit trust, inversion of control. So wherever we can attempt to address that, I think it's usually better to try to address it.
[00:01:09] So, what we're gonna do is take our helloworld file and save it out. Call it a different name, so save it out and call it helloworld2.js. And we'll do the same thing with our 1.js, we're now actually ready, I was pretty much sure on the other one. We're now ready to call that one 2.js [COUGH].
[00:01:38] And we'll make sure to load in, in 2.js make sure to update your reference to your loading in helloworld2.js. Update your little copyright statement if you will. So, the next task that we wanna do is look at dealing a little bit more cleanly with the asynchronicity. Now I'm gonna show you and ask you to play along with asynquence.
[00:02:04] Not because I wanna shove it down your throat, if you don't like it, if you've got another library you wanna use, please feel free. Everybody has their own taste, but I made it so that it was easy to understand, that's the whole goal of the design of that API.
[00:02:18] And hopefully that aligns with our goals here, we wanna easily understand what we're doing. So, the first thing that we're going to do is need to make sure asynquence is installed. And we're also gonna wanna make sure there's a separate repository that includes some plugins. I've written all of them, but theoretically they would include plugins from others.
[00:02:43] Because one of the other things that asynquence provides is a simple mechanism for you to extend the API with your own functionality. There's a simple little plug in mechanism. So about half of the functionality that's already out there for asynquence is provided as separate plugins. Which means you can choose to include them or not or include bits and pieces of them.
[00:03:04] So in our directory, we would want to do npm install. The word asyquence, it's spelled a-s-y-n-q-u-e-n-c-e. It's like the word async plus the word sequence and smash together. So you want to install asynquence and you also wanna install asyquence-contrib. Which is gonna pull in the module that has those additional plugins in it.
[00:03:30] So install both of those. That should take just a second cuz they're pretty tiny, these are two or three k each, so they're really small. [COUGH] And those of you that are already here, you're already gonna have that in your node modules directory. But to verify that you do have in fact installed correctly.
[00:03:47] You wanna be able to see asynquence and asynquence-contrib. Now that we have those installed in our project, we're able to [COUGH], sorry, I'm in the wrong window here. Now that we have them installed in our project, we're able to use them from inside of our code. Now, [COUGH] I primarily wanna use it inside of my module.
[00:04:09] So, really the style here with module dependencies is you include them wherever you need them. And Node takes care of making sure it's not re-requesting the same file over and over and over again. So we're just gonna make sure we definitely require it here. So I'm gonna say var and I like to call mine capital ASQ.
[00:04:27] You can call it whatever you want. But that's my typical symbol that I use. That's the symbol that it automatically attaches to when you use it in the browser as a global. So ASQ is what I like to use [COUGH] and call it whatever you feel and include the asynquence library.
[00:04:42] Now there's something a little bit non-idiomatic or non-standard in the way that I do asynquence and asynquence-contrib. Is that you also are going to require the asynquence-contrib, but it doesn't return anything for you. So you don't need to save it somewhere you can, but it doesn't return anything.
[00:05:01] So you don't need to save it, all it's going to do is attach itself directly to the asynquence library. So, it's sufficient for you to just simply require it in asynquence-contrib. And it basically has a no return value that you don't need to worry about. Okay, so now we have asynquence available to us.
[00:05:24] Let's take a look at this code that's using these nested timeouts. Let's break it down. How could we express this as a sequence of things? Well, clearly the first step is read a file and wait for that file reading to finish. That's the first step. The second step is call a timeout and wait for that to finish and then the third step is send the contents out.
[00:05:47] So we can express that series as an asynquence call. So what I'm gonna do is we're gonna get rid of the callback cuz we're not gonna deal with the callback calling style anymore. And I'm gonna rename this function from say, I'm gonna call it just read file cuz that's what it's doing, that's its task.
[00:06:06] Then I'm gonna make another function down here which is actually the say function. He receives a filename. Now, what say is going to do is create a sequence that represents this series for us, okay? So the first two steps we definitely know if we want to create an asynquence.
[00:06:27] I'm gonna return this sequence, the first step is read the file, put the filename. That's gonna give us a sequence back and then we wanna delay ourselves a little bit. So I will call then(delayMsg) for instance is what I might call.
>> Kyle: So let's implement the delayMsg which is pretty simple.
>> Kyle: He receives a done trigger the way asynquence callbacks receive a done trigger. It receives a done trigger and he receives the message that was passed along to him which in this case is the contents of our file.
>> Kyle: And he sets up a timeout.
>> Kyle: That 1,000 milliseconds from now he will call that done trigger and pass along the contents.
[00:07:30] Nothing rocket science here, we're just taking in a value and delaying its return. We're simulating again some kind of call to a database or something like that.
>> Kyle: Now let's come up to the readFile function and let's reimplement the readFile function in a much simpler way. So I'm gonna create a sequence here, I'll call it sq.
[00:07:54] You create a sequence just simply by calling the symbol ASQ or whatever you called it. Open close parentheses, that creates an empty sequence that's ready for you to start chaining off of. Now I need to do fs.readFile. Here's where you're gonna start to see some very interesting things.
[00:08:13] We get to get rid of all of that code, my code sucked anyway.
>> Speaker 2: [LAUGH]
>> Kyle: And all we need to do is return our sequence. So let's follow this flow before I fill in, there's a missing gap there. But before I fill in the gap, readFile, we call that function with the filename.
[00:08:30] And he returns us back a sequence, it's like returning us back a promise. See that on line 16. So on line 2, we create the sequence and on line 6 we return it, in between we need to do something with our sequence. So I could have done something like sq.then or whatever.
[00:08:46] But I wanna show you another capability of asynquence, another sort of syntactic sugar that makes things nice. Because we might have done, we could have done something like sq.then and we could have had a function with a done on it. And we could have called our fs.readfile inside of that and that would work.
[00:09:06] But that's kind of crufty, there are some extra, creating a function, an inline function there. And of course, you're gonna have to name your functions and all of that junk. So that kind of sucks, I don't wanna do it that way. Turns out there is an easier way to do it.
[00:09:21] Whenever you need to present somewhere, whenever you need to pass somewhere, one of those special error first callbacks. Remember I said Node is famous for those error first callbacks which is the error is always the first parameter and the success is always the subsequent parameters. So if you need an error first cal back which we do, we need a callback for the readFile function.
[00:09:43] Cuz the built in node one is not asynquence aware nor is it promise aware. So we need to essentially construct a callback. Now we could do it manually like we did before. We could do it and put our error in our contents here. We could write it all out but that's sucks, nobody wants to do that.
[00:10:02] So asynquence has a feature in it that allows you to say sq., which is our sequence and we can call errfcb, which stands for error first callbacks. So that's gonna generate an error first callback that is attached to our sequence for us. It automatically does all that wrapping of the if and the error condition and all of that stuff for us.
[00:10:26] [COUGH] I was gonna make sure I'm not getting too far off track with my plans here.
>> Kyle: Okay. So we've generated a callback from our sequence to pass it in. This is one of those adapters that makes it easy to work with asynquence in environments that are not fundamentally promise aware.
[00:10:54] There are also ways for you to adapt asynquence to work with native promises, there are also helpers for that. So it attempts to try to make it very easy for you to use asynquence in other environments that aren't already sort of promise aware and doing that sort of thing.
[00:11:09] But we've constructed that error first callback, that's gonna automatically wire in both the success and failure callbacks to our sequence the way we want. So everything here is done. We've now clean up this code, so that we have a single place in our logic that we can go and look at.
[00:11:24] I know that my flow is read the file then delay the message. And if there were three or ten or 20 steps, I could read it very directly. There'd be a place in my code where that sequence would be expressed.