API Design in Node.js, v4

Performance Management with Async

Scott Moss

Scott Moss

Superfilter AI
API Design in Node.js, v4

Check out a free preview of the full API Design in Node.js, v4 course

The "Performance Management with Async" Lesson is part of the full, API Design in Node.js, v4 course featured in this preview video. Here's what you'd learn in this lesson:

Scott explains the difference between blocking and nonblocking code. Whenever possible, developers should use asynchronous APIs because they are nonblocking and more performant for the server when executing multiple requests.


Transcript from the "Performance Management with Async" Lesson

>> Let's keep it moving. Now what I want to talk about is a little bit performance stuff. I want to talk about blocking code and how dangerous this is in Express. So now that you all are pretty much good at writing APIs and Express at this point. I'm gonna show you how to be great at writing APIs and Express.

And not have to make rookie mistakes. So, there's something called blocking code and something called non blocking code. This actually has nothing to do with Express but everything to do with JavaScript, let's talk about that. So, I'm going to write my little test file that always do. I'm gonna call this blocking.js, this is just me scribbling on some code you don't have to follow here.

So let's talk about some blocking code, what is blocking? Blocking code is like this, this is blocking code. All right, this code is blocking as JavaScript won't go to this next line until this first line has completed. This is not an issue because creating a variable is quick, it's easy.

Java is not gonna struggle with creating a variable. But if your blocking code was I don't know, CPU intensive like some heavy math calculation like Fibonacci or something like that. If Javas gonna have to wait on that to go to the next line. Maybe it's also not a problem because nothing else is waiting on.

Nothing else is in line to be processed, so maybe it's also not a big deal. But not take that concept and apply it to a server, okay? You have some route that's running a Fibonacci. That's gonna take, I don't know, 30 seconds plus on your MacBook. In the meantime, you have 100 other requests coming in.

How is JavaScript gonna process those requests when it's too busy running your Fibonacci. Okay, now it's a problem. So blocking code can become a problem in the context of having routes where you want to be able to handle as many requests per second as possible. But now you can't because that one request is taking too long.

That's called blocking. So you don't wanna write blocking code. So, how do you write non blocking code? There's really only two ways in JavaScript, or at least a node. One is to make it async, right? The way asynchronous code works, is that it's scheduled to be done later, right?

It's not gonna happen on the current stack, it happens on a different stack. So if you could think about, well, what if someone made a request to an API and that brand Fibonacci. But it ran an asynchronous version of Fibonacci that is happening over here and is scheduled to complete later.

Well, then JavaScript can continue to ingest more requests and move forward with things. So you basically just wanna make sure you're not doing CPU, intensive things synchronously. All right, and we might be guilty of some of that. Let me see if we did anything synchronous. If we did, it would be the bcrypt stuff.

Okay, now this is async. If we did the sync version of this, this could be blocking. I mean, this also isn't gonna take long, but it's still blocking. I don't know, maybe bcrypt decides it wants, it takes ten seconds today. Within that ten seconds, you had 20,000 requests come in.

They're probably not gonna get processed. Or if so they'll get really processed very slow, they won't just get blocked but they'll have to wait until this is done. Eventually you will run out of memory where you can't hold on to all those be processing some requests will get dropped if you're doing something crazy.

So you wanna always use the synchronous version of things. And I would say a common one that I see people do all the time is file system stuff. They always do file, they wanna read the file system, they do the file system sync. I have an example of it here.

Do something like this right and actually let's just run it, let me show what I'm talking about. So if I import file system, I had to use require online. So, FS equals require a first. So if I import the file system's gonna read the package.json, Which will be like process that.

I gotta do a whole thing, I forgot. I haven't done this in so long. It'll be like path.join, process or it'll be their name, something like that. And then like, package.json. Okay, and then I can get the result. I think I haven't done that in so long. Let's find out.

I'll call that blocking. Okay, so there we go, we got this is a buffer version of the file but that's the file, system buffer firm or you can convert that to a string if you want to, UTFA. There we go. So there's a package.json. That's blocking because check this out.

This results didn't log or actually, just doesn't make it more clear, I'll put another log here. Hi, if I put hi here. Actually this one, log first. I guess that's not blocking. [LAUGH] I don't know why they didn't block, no way, I'm sorry. No, it did, I just had the result come after, I'm stupid.

No, it definitely did block. This is the result is logging after hi. So let me kind of switch that up there. But yeah, that's blocking because this didn't actually log until this was already processed. And we know it was already processed because the result printed out and then high logged.

Which means hi this log for hi wasn't able to be processed until all this was done. And this was just a simple package.json. I don't know, imagine open up a CSV, a gigabyte CSV. [LAUGH] And then tried to take in more processes, it wouldn't work. Okay, so what you would wanna do, is you would make this async.

So I can say, give me the promises version. And I want this to be, async. So I would say, read async, like this, I can go in here like that. And then I can say, read, right, so now it's not blocking. So if I say, that it's a promise, we didn't wait for it to come back.

It just gave me back a promise that it will come back, but in the meantime, it went on to hi, right? And then, whenever I want, I can resolve that promise. I didn't return anything criminal. I don't know why they didn't let me do the promise thing, maybe it's not [LAUGH] a promise thing, let's see, okay, there we go.

I just didn't like the way that I did the non chanting syntax. So by using the promise version of it, we can basically not have to wait until that's done and we can just proceed on. So, basically using the async stuff is the way you wanna go. You don't wanna be using synchronous stuff inside a route handlers for CPU intensive things.

That doesn't mean every line that you write has to be inside of a promise. It's just like CPU intensive things, if you think of an algorithm that is linear or quadratic in nature. Or I guess pretty much anything that's probably gonna be slower than linear. And then it potentially has ability to have a hi input.

Yeah, you probably just wanna be careful with that. This is not JavaScript, a cup of tea, JavaScript is not a multi threaded, or node is in a multi threaded environment, a single threaded environment only uses one of the threads. So your CPU by default, you can tap into the other ones.

But by default, no. So that's one way. The other way is to use a child process. So not sure if you ever heard of a child process but a child process is basically, kinda like a web worker in the browser. It's actually literally the same thing. So you can spin up a different process adjacent to this process in which your servers running in and you can tell that process to go do this work.

And that won't block the current process. But you only can do so many child process, infinite amount of those. You can throw money at your server and get more cores. But eventually you can't do all those, right? So those are more for CPU intensive things that you have control over and not something that a user has control over.

A user can use your API over and over and over, really kicks off the child process. You probably don't hit a limit really quick. But this is like, this is a cron job that I run twice a day and I need to do it in the background. Yeah, that makes sense because that most is gonna happen twice a day.

So yeah, you could do that. But those are really the two ways you wanna get past blocking code. So don't block code use promises. Next time one of your colleagues submitted a pull request, tell them stop blocking the code.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now