Skip to content
CBT Nuggets
DemoBook a Demo

Intermediate Promise Concepts

This skill delves into intermediate concepts of using Promises in JavaScript, particularly within the Node.js environment. It covers creating custom Promises, handling asynchronous operations, and managing errors effectively. Learners will also explore advanced techniques such as chaining Promises, introducing delays, and converting callback-based functions to Promises. The skill culminates in a practical challenge to reinforce the concepts learned.

Full skill from JSNAD. Preview the training IT teams trust.

Updated

48m

Skill 10 of 42 in JSNAD

What you'll learn

  • Create a custom promise with the Promise constructor and an executor function that calls resolve or reject
  • Explain how resolving or rejecting a promise determines whether the chain runs then or catch
  • Organize multiple asynchronous operations into sequential results using a then chain
  • Wrap setTimeout in a promise to create a reusable delay that can be chained with then
  • Convert callback-based functions such as fs.readFile and fs.unlink into promise-returning functions
  • Build a generic promisify function that converts any Node.js callback-style function into a promise

Free 48-minute video lesson with a full transcript and knowledge checks. No account needed to watch.

Creating Our Own Promises

In the previous skill, we learned the basics of using Promises to manage asynchronous logic. Ah, but where do Promises come from, you ask? That's what we're going to start off by talking about this skill. In this first video, we take a look at how to use the Promise constructor, as well as some of the finer details of when Promises actually start running the logic we give them.

Knowledge Check

The "executor" function that we create a new Promise with will be called when we ________.

Resolving and Rejecting Promises with Values

Now that we've seen how to create a simple custom Promise that just resolves (pretty boring, right?), let's take a look at how we can have our Promises reject as well. We'll also take a look at how resolving or rejecting Promises allows us to pass the resulting values to the .then callbacks.

Knowledge Check

Passing a value to "reject" will make that value show up in the callback that we pass to _________.

Creating a setTimeout Promise

Finally, let's get down to business and do something a little more interesting. In this video, we see how to create a custom Promise that allows us to insert delays into our Promise chains. Let's take a look.

Knowledge Check

If we want to have more control over when a custom Promise begins executing, we can simply __________.

Challenge & Solution: Promisifying Callback Functions

Now it's time for a challenge! In this challenge, you'll be asked to write some code that converts some of the fs package's functions to promises. Watch the video for more information.

Also, here's the link to the specific commit on Github in case you want to use Codespaces for this challenge.

And now that you've attempted the challenge, I'll show you how to solve it.

Knowledge Check

Is this your first time creating custom promises or using the Promise constructor?

This interactive assessment is available in the full learning experience.

Want to answer questions like this yourself?
with no purchase required. Already have an account?

View Transcript

Creating Our Own Promises

0:00Hi, Sean here and welcome to this skill where we're going to continue talking about how to work with promises effectively in Node.js. Now in the previous skill, which I'd highly recommend you take a look at, we learned the basics of promises and we saw how to do simple things like create chains of sequential asynchronous operations as well as things like handling errors in promises. So again, if you haven't seen that yet, you probably want to go back and take a look at that.

0:30But what we're going to take a look at in this skill is some somewhat more advanced things that we can do with promises. And while these are slightly more advanced than what we learned about previously, they're still going to be incredibly useful for you when writing Node.js code because what they'll allow you to do is actually write more reusable promise based code. You'll see what I mean by that shortly. But anyway, the first thing that we're going to take a look at here is we're going to see how to create our own custom promises.

1:03So what we'll do first here is just create a new file called custom promises dot JS. And the first thing that you should know here is that in order to create custom promises, we're going to create instances of the promise class, which is automatically included in the JavaScript runtime. So we don't need to import that like we've had to do with some other things in in Node JS, like FS, of course. So here's the basic syntax for creating our new promise.

1:36We're going to say const will say my promise equals. And then we can just say new promise. And that will create a promise for us. Now there is one notable thing lacking from this statement here. And that is we need to define what we actually want the promise to do in the first place, right? Now the way that we do this, it's a little bit strange. So just bear with me here, you'll, you'll see how this works. You'll get a sense for how this works as we go through this skill.

2:09But we're actually going to pass a function to the promise constructor. And this function is going to take two arguments itself, which are generally called resolve and reject. Now what these things do, I'll describe that much more detail shortly. But anyway, inside this promise function that we're passing, which by the way, the name of this thing, you'll see this in the promise literature, in many cases, this is actually called an executor function.

2:43I don't actually know if that's the way you pronounce it executor executor. I don't know. It sounds like a Pokemon name to me. But anyway, that's what that function is called. And the reason it's called this is because that is the actual logic that the promise is going to execute, right? So hence the name, I suppose. But anyway, inside this, whatever, whatever kind of functionality we want to write inside our promise, the important thing is that at some point we call one of these arguments.

3:17That's right. We need to actually call either resolve or reject because they are in fact, functions themselves that we can use to indicate to this promise what happened, right? What the overall result of our application or of this operation rather was. So let me just get started here by giving you the simplest case. We're just going to call the resolve function inside of this promise.

3:47And then what we're going to do is we're going to say my promise dot then. And we'll just say something like console dot log done inside of there. All right. Now what's happening here is we're creating a promise. This part might confuse you a little bit here because we've been used to seeing things like FS dot read file, right? Something that actually clearly does something. And then we can say dot then. But something that you may not have realized is that because things like FS dot read file and by this I'm referring to the promises version.

4:23So let me actually just import that here to make it a little bit more clear. We're going to say const FS equals require FS dot promises. And if we call FS dot read file, we'll just use file one dot T XT here. And then, oops, let's try that again. We'll use UTF-8 for the encoding. Well we saw that this returns a promise. So I'll just do this one more time, although I did it in the previous skill as well. If we print this thing out here, what we're going to see if we run our code with node custom promises dot JS is that that actually returns a promise with the state of that promise, which can be pending, fulfilled, or rejected in curly braces here.

5:08All right. So anyway, that's the basic idea here is that operations like this will generally return promises. But if we're defining our own promises like we are here, then we can just call dot then on the end of that promise, and that will assign a callback to it. So in this case, what we're going to do is if we if we run our code with this new custom promise that we just created, all right, and again, we'll use node custom promises dot JS for that, we're going to see that it'll just print out done, right?

5:41Very simple. But what's happening here is we're calling resolve inside this promise, this executor function. And that is telling the promise that it should call whatever callback is in the next dot then method, right? In other words, when we call resolve here, it tells my promise to look inside its dot then method. And it gets this callback, which it calls and we see done printed to the console.

6:12All right. Now, if we wanted to create a few promises here, right, we'll just say my promise, then we'll do my promise to and then we'll do my promise three. All right. Well, we can chain these together just like we saw in the previous skill just by using multiple dot thens, right? So we can say my promise dot then. And maybe we can log something out to the console here still. So we'll say something like console dot log first promise completed, something like that.

6:47And then what we'll do is we'll actually return my promise to and then we can call dot then after that. So we'll say dot then and we'll say console dot log second promise completed. Oops, there we go. And then what we're going to do next is we're going to say dot then and we'll have our last or our third promise here. What we're actually going to do is we're going to say return my promise three.

7:18And what this is going to look like is we're going to say dot then and then we 'll say console dot log and we'll say third promise completed. Okay. So in this case, if we run our code, what we're going to see is sure enough, it logs out first, promise completed, second promise completed, third promise completed. And that's pretty much what we saw with running operations such as reading files sequentially using promises as well. Now one thing that's very important to realize here with our promises is that just like when we called fs dot read file, or really anything else fs dot on link, as soon as we create the promise, it's going to start running its executor function.

8:04All right. In other words, when we created these three promises, they've already started to execute their, their little executor function right here before we've even said my promise dot then. Now here's where people get confused. It certainly looks like just from the output, it certainly looks like our promises are executing sequentially, right? First promise, second promise, then third promise. But that's actually just because we're returning those promises and listening for them to complete, right?

8:40So in other words, it's a little bit confusing because a promise can have no listeners attached to it and it can complete, right? So it can jump to the fulfilled state. But then another piece of code could come along and attach a listener to it by saying dot then, and if that promise is already fulfilled, then that callback function is going to be called immediately, right? So in other words, if we add dot then to an already fulfilled promise, that, that callback is going to be called, and it's going to look like our promises only started to execute when we added dot then.

9:19So hopefully that makes sense. Let me just demonstrate this in a little bit more detail for you. What we're going to do is we're actually going to print something out inside these executor functions. So we'll say console dot log, we'll say something like promise one is starting. All right, and then we'll do the same thing for our other promises. So we'll just copy this and paste this in the other ones. We'll say promise two is starting and promise three is starting. And if we run our code again, what we're going to see is sure enough, all three of those promises start before we even see the first promise log out.

9:58Another demonstration of this just to give you another demonstration here. And for this, I'm just going to comment out all of this temporarily. But let's say that we created our own promise. Again, we'll just use const. And actually here, we'll go up and use my promise again. So that I don't have to type that out again. Let's just uncomment this. And what we'll do is we'll say set timeout. And inside here, what we're going to do is we're going to just say my promise dot then. And we're going to log something out, saying something like, you know, we'll just say console dot log inside dot then.

10:33All right. And we'll set that for maybe two seconds. But we'll also say console dot log after the resolve, we'll say something like promise one has resolved. And what we're going to do here is we're going to run our code. And sure enough, we see that it says promise one is starting, promise one has resolved. And after two seconds, we see it log out inside dot then. So in other words, clearly the promise has already finished here before this console dot log thing gets logged out.

11:10It's just that it's not until we add dot then to that promise that we see that happen. So it can definitely be a little bit confusing at first, but notice what this allows us to do, right? What we've, what we've done here when we say my promise dot then dot then dot then is it's allowed us to take three asynchronous tasks, right? Three asynchronous operations. We have no clue how long they're going to take or when they finished or if they 've finished. And it's allowed us to easily organize those so that we have the results in a sequential manner.

11:42All right, and that's the real power of how promises work and how this whole resolving and rejecting thing works as well along with the dot then chain. So anyway, I hope this has made sense so far. The next thing that we're going to take a look at is how we can get a little bit more detailed and perform some more useful tasks with our promises. So anyway, I hope this has been informative for you and I'd like to thank you for viewing. [BLANK_AUDIO]

Resolving and Rejecting Promises with Values

0:00All right, so at this point, we've seen how to create a new promise with the promise constructor, but so far our promises aren't really doing anything interesting, right? They're just logging something out to the console, resolving and that's about it. So anyway, what we're going to take a look at next is how we can actually use the reject function, right? This reject argument that our promise executor function has in order to actually determine if something went wrong or determine if a promise failed and let the chain that our promise belongs to know about that.

0:37So here's what that's going to look like. First of all, I suppose let's just change my promise to and we're going to change this to call reject instead of resolve. All right, now perhaps you already know what's going to happen here. If we run our code and actually, well, I guess this is going to be kind of a spoiler, but let's just say dot catch at the bottom here and we'll just log out the error here. We'll say console dot log error. And if we run our code now, what we'll see is that it says first promise completed and then it logs out undefined.

1:11Now undefined might not be exactly what you were expecting here, but basically when we call reject, we get to pass an error message to this that will actually be picked up by the catch callback down here. So if we say something like reject, this promise is stubborn today, something like that, then what we're going to see is if we run our code here, sure enough, we see that that message gets printed out. Now this can be an actual error. If we want to say something like new error, this promise is stubborn today, something like that, then that'll work as well.

1:48And that'll just make it a little bit more detailed, right? That'll add the whole stack trace details onto there. So anyway, that's the basics of rejecting a promise. And one thing that I kind of want to go back to here is I want to show you that resolving a promise, when we do that, we can actually pass the result value to this thing , right? So in other words, let's change this down here to say something like first promise completed with the value. And then what we'll do is we'll actually make this an argument here.

2:21We'll say something like, well, we'll say result there, and we'll do the same thing down here with promise two, and we'll do the same thing with promise three. We'll just say result result result. Oops, let's try that again. There we go. And now if we say first promise completed with the value result, second promise completed will do the same thing. And in fact, I'll just copy and paste this to make it easier. And we'll change that to second promise. And then with our third promise here, we'll say third promise completed with the value.

2:52There we go. Cool. So if we go back up to our promises here, and let's change my promise to back to resolve, we can actually pass the result of whatever operation that we, that this promise is meant to represent to the resolve function, right? So we can say something like resolve one, if this one may be resolved with a an object of some sort, we could say a one, b two, perhaps. And then we could resolve my promise three with an array of some sort, right?

3:25We'll say a, b, c. All right. So what we're going to see now is if we run our code, sure enough, we see that the first promise completed with the value of one, the second value completed with the value, well, just printed out an object here, but you get the idea. And then we see that our third promise completed with the values, a, b, c. So essentially what's going on here is when we create a promise and we call resolve, and then we put that promise into a chain by calling dot then on it, the, the value that we call resolve with is going to be the argument that gets passed to the callback that we passed to dot then that's got to be about the longest, most convoluted explanation I 've ever given.

4:10But anyway, hopefully you can see the connection here. This one becomes this argument in the dot then callback. All right, that's how that works. And the same thing is true for all of these as well, although it's, it's obscured a little bit by the fact that we're just returning these from the callback. But if we were to say, you know, my promise here, let's try that again. If we were to say my promise to dot then, and then we could just log that out here, that's going to give us whatever we called resolve with here, right, that result is going to be whatever we called the resolve function with cool.

4:51So anyway, that's the basics of resolving a function and rejecting a function that we've created. Let's move on now and see how we can do something slightly more interesting. What we're going to do is we're going to create a promise that has, you know, we'll say like a, we'll say that it has a 5050 chance of resolving and rejecting. So here's what this is going to look like. I'm just going to comment out all of the other code that I had above there, just so that you have it for reference in the GitHub repo. But we're going to create a promise here called something like 5050.

5:24And this is going to be a new promise. And oops, I forgot my const before that. All right, so this is going to be a new promise. And it's going to have resolve and reject as arguments, just like we just like we've seen before. And here's what this is going to look like. We're going to flip a digital coin here by using the math dot random function. And if it's above a certain value, we're going to call resolve. If it's below a certain value, we're going to call reject. So here's what this is going to look like. We're going to say, oops, there we go.

5:57We're going to say, if math dot random, and this will give us a random number between zero and one. So we can say if math dot random is less than 0.5, that'll give us about a 5050 chance there, then we're going to resolve the promise with some value. We'll say something like, yay. Otherwise what we'll do is we will reject the promise with another message. We'll say something like darn. Okay.

6:28Cool. So here's how this 5050 promise is going to work. We can call dot then on it, right? We're going to say 5050 dot then. And what this is going to look like is the dot then callback, that'll just be a message. We'll say something like console dot log, and then we'll say success. And then we'll just add that message onto the end. So we'll say plus message. If it fails, though, we're going to have to catch it with dot catch. And in this case, we'll say message, and we'll say something like console dot log, and we'll say something like failure.

7:07And then we'll say plus message there as well. Cool. So anyway, let's run this code a couple times because we're going to need to run it a few times in order to see just different results. So let's run our code. Oh, it looks like it failed the first time. So it says failure darn if we run it again, we get failure again, we get success now, failure again, failure, failure. It looks like this one's failing a lot. Wonder what the chances of that are. But anyway, that is the basics of creating a promise that will succeed or fail with a about a 5050% chance and either resolve or reject in that case, right, which will then translate into either calling then or calling dot catch.

7:53So this is just a very introductory example of something that's kind of useful that we could do with a promise, right? Because what you could do here is if you needed something that would happen with a 5050 chance in your in your program, you could actually take advantage of promises syntax in order to do that. But nevertheless, we will see some more useful things in following videos. So I hope this has been informative for you. and I'd like to thank you for viewing.

Creating a setTimeout Promise

0:00All right, so hopefully at this point, you're getting a basic sense of how this new promise constructor works along with the executor function that we pass to it. So what we're going to do here is we're going to do something that can actually be very, very useful when you're working with promises. We're going to take a look at how to create a promise that will wait a certain amount of time before resolving, right? This can be very useful for doing things like introducing delays.

0:30So if you want to create some sort of web scraper that needs to wait a certain amount of time before it sends another request or something like that, this can be very useful in that kind of situation. So what we're going to do here is I'm going to actually leave this 50/50 function commented out just so you have it for reference again. And underneath it, I'm going to create a promise that will wait. Let's say something like one second before fulfilling. So here's what this is going to look like. We're going to say something like delay, or in fact, we'll call it one second delay.

1:06And this is going to be a new promise here. And what that's going to look like is we're going to say new promise resolve reject. And we're going to use set timeout here in order to wait one second. So we'll say one second. And for the callback, here's what that's going to look like. We're just going to have the callback of set timeout call resolve with no arguments. Right. Now, if we wanted some kind of value there, we could always pass it, but I'm just going to leave that empty.

1:37Now, this is actually very, very simple, right? You might not have thought that it would be this simple to create a promise that would introduce a delay. But what we can actually do here is we can say one second delay. And what this is going to look like is we can say dot then, and we'll say something like console dot log. That was a long wait. All right. Cool. So if we run our code now, what we're going to see is it'll wait for a second, and then it'll print out that was a long wait.

2:08However, there is one big problem with this. And this goes back to our discussion that we've kind of looped back to a few times already. And that discussion is that promises start to execute the moment that they're created. Right. So in other words, if we had some other fairly long running operation here, right, if we had, if we were maybe reading a large file or something like that, and then here, I'll just type this out. And then we said dot then, and then we wanted to introduce a one second delay into that, that sort of pipeline there.

2:46Well, that's not going to work, right here. What we're going to do, actually, I have the syntax of that wrong, let's say return one second delay, and then we could say dot then, well, this isn't actually going to introduce a one second delay from when the large file finished reading, it's going to introduce a one second delay from when we first created this promise, right? And you can see this if you just say something like console dot log, we'll say starting time out.

3:16All right. And then we'll say console dot log time out finished. Well, I guess we're just going to have to simulate this fs dot read file thing with another time out, not my favorite demonstration there, but let's just say set timeout, and then we'll say something like a one second delay. Oops, let's try that again. We'll say one second delay dot then. All right. Well, what that's going to look like is here, let's just introduce maybe a little bit of a two second time out there.

3:48What you're going to see is that this, that was a long way thing is going to be printed out after two seconds, although many people who are new to promises would think that this would print out after three seconds. So here's what this is going to look like. And actually, just to make this a little bit more visible, let's set this to two seconds to so it's a difference of two versus four seconds. What I'm going to do now is I'm going to run the file and one 1000 to 1000. And sure enough, we see that it prints out after two seconds instead of the desired four.

4:21Okay. So that brings up this question here of if we want to have control over when the promises start, how do we do that? Well, the good news here is that the answer is actually surprisingly simple. All we have to do is make this a function that returns a promise instead of just a promise itself, right? So there's a very subtle difference there in syntax, but a very big difference in the actual outcome.

4:55If we want to, if we wanted to be able to introduce a one second delay at any point, then we're going to need to just create a function that returns this new one second delay promise. All right. So that's what that looks like. If you want to, you know, if you prefer the regular old function syntax, you could say something like this, we could say return new promise like that. That might make it a little bit more visible for you. And actually we need to say function, we'll say one second delay.

5:27There we go. And now this will actually introduce, well, I guess it's not a one second delay anymore. This will actually introduce a two second delay from when we start it, but we do have to change the syntax a little bit when we use it, we need to say two second delay with parentheses after it, right? So in other words, this will create the promise here that we can add dot then two. And oops, I abbreviated second there. So we need to remove the O and D on the end.

5:58So let's try this again. And we should see that now it will actually wait for four seconds. So here's what this is going to look like. We're going to run our code. One 1000 two 1000 three 1000 four 1000. And there we go. It finishes. So also, did you notice the starting timeout didn't actually begin until two seconds in, whereas the old way when it was just a promise, you know, when two second delay was just a promise, that was printed out to the console immediately when we started running the program.

6:29So let's make this a little bit more interesting here. What we're going to do is we're actually going to maybe change this function here to something like, we'll call it something like create delay. And what we're going to do is we're actually going to allow ourselves to pass in the number of milliseconds that we want the delay to be. All right. So this is making this delay thing a little bit more, a little bit more flexible, because what we can do now is if we want to introduce a delay, right, we can say something like create delay.

7:03And then maybe we'll wait for one second. And we can say dot then, and then we can say something like console dot log, we 'll say, one second has passed. And then we can actually return another delay. We can say return create delay. And we can maybe create a two second delay. So we'll say here, we'll just copy this dot then thing, because I want to do a few of these, we'll say, maybe a total of three seconds have passed. So we'll say three seconds have passed.

7:38And then down here, we'll, we'll do one last delay. We'll do three seconds. And that would be a total of six seconds. So we'll say six seconds have passed. All right. And then we'll just not insert an extra delay there. And that should be all we need to do. So if we run this code now, what we're going to see is that these console dot log messages will be logged out after specific amounts of time, right, at specific intervals . And if we do that, here's what this is going to look like.

8:10All right, we see one second is passed, three seconds have passed, and then the last one here, six seconds have passed. And let's also go in here and actually delete the starting timeout and timeout finished things. And that'll give us more simplified version of our promise. And actually, we can simplify this even more by just having resolve itself be the callback for set timeout. All right. So that's just about the simplest version that we can have of that promise. All right.

8:41So anyway, that is the basics of using promises to insert a delay. And we also learned about how to make sure that we had control over when a promise started by having that promise get returned from a function instead of just assigned to a variable. And just another thing to note is that we also saw we've already seen this a few times, but I just want to tie this back in. We've also seen that functions like read file or FS dot on link, those actually return promises.

9:13So in other words, those are pretty much the same thing as this one here. Since the difference with our create delay function is that the asynchronous operation that it's performing is just waiting a certain number of milliseconds. All right. So anyway, I hope this has been informative for you and I'd like to thank you for viewing. [BLANK_AUDIO]

Challenge & Solution: Promisifying Callback Functions

0:00All right, so now that we've seen the basics of using this new promise constructor and plenty of other details around how to use it to maximum effect, it's time for you to apply what you've learned here by doing a challenge. Now, the challenge here is going to be a pretty interesting one because I guarantee you will find it useful in your JavaScript code whenever you're using some sort of some sort of library or something like that that doesn't natively support promises that can happen quite often where for one reason or another library that you need really only works with callbacks.

0:37Well, the good news here is that in general, it's always possible to convert a callback into a promise. And as you may have guessed by now, that is going to be your challenge. So your challenge here, let me just create a new file here, which I'll call something like promiseify challenge. There we go.js. Your challenge here is going to be to promiseify certain functions in the FS package without using the dot promises property that's cheating.

1:11You can't do that. So in other words, what you're going to do is you're going to use the same method that we just saw with using set timeout to implement a promise delay in order to promiseify, you can really choose what functions you want, but I'm going to be doing read file and on link. All right, and that should allow you to do things like say FS dot read file. And then you can obviously put in the details there and say dot then.

1:41All right, now you are going to need to create sort of a wrapper here, such as you might say something like function read file and actually create that promise inside of here. So that would look more like this. But you get the idea, right? You're going to need to create a promiseified version of FS dot read file and FS dot unlink and a further part to your challenge. If you dare go here is to create a generic promiseify function.

2:11All right, so it might look something like this promiseify that takes any function that returns a callback and returns a promiseified version of that function that can be used with the dot then syntax. Right. So this might look something like this if you have function promiseify and FN and to make it easier for you, you can also pass in maybe the number of the argument or the index of the argument where that callback is usually found just so that you'll be able to work with different functions that have that callback function in different places, right?

2:46So maybe we'll call that something like callback index. All right, so that is the second part of your challenge. If you can't figure that one out, don't worry too much, but you should be able to figure out the first part of it. So give this challenge maybe five to 10 minutes. And once you've given it a try, feel free to move on to the next video where I 'll walk you through how I solved this challenge. So I hope this has been informative for you and I'd like to thank you for viewing. [ … ]

Challenge & Solution: Promisifying Callback Functions

0:00All right, so hopefully you've given this challenge a try. What I'm going to do in this video is walk you through how I solve this challenge. So to get started here, let's take a look at how to create a promise version of the FS packages functions. We'll start off with read file. And what this is going to look like is we'll just say function, I'll call it read file, and this is just going to take the file path and the encoding and pass those along. Now, if we want to be able to say something like read file dot then and have the results of the file printed out, right, in that case, it would be the actual file contents there, what we would need to do is, well, the first thing that we know that we need to do anyway is have this read file function return a promise, right?

0:49That's the only reason that we're able to, oh, and here I actually did that wrong. This should be something like file one dot txt. And by the way, I'm just going to be using the file one dot txt thing here as an example with a bunch of lorem ip some in it. And then of course you could say, oops, let's try that again, you could say utf eight in there, right? If you want to be able to do this and add dot then, you know that this file is going to need to return or this function rather is going to need to return a promise.

1:19So just knowing that we at least know how to start, we can just say return new promise. All right. And then the next thing that we're going to want to do is obviously we're going to want to load the file based on the file path and encoding arguments that were passed in. Now what function do we know that is going to allow us to load files? Well, obviously the fs dot read file function. So what this is going to look like is we're going to say resolve and reject as the first two arguments here.

1:55And then we're actually just going to use the regular old callback based fs dot read file function in order to load the file. And here's where it's all going to tie together. What we're going to do is we're going to say fs dot read file file path and encoding. Let me actually spell that right there encoding and the last thing we're going to do is we are going to pass a callback here that has the regular old error and contents arguments.

2:26But what we're going to do is instead of actually doing anything with those arguments, we're going to just pass those along to either resolve or reject. So here's what this is going to look like. We're going to say if error, right, if an error occurred, then obviously something went wrong and we should reject the promise, right? So we're going to say reject and we're just going to pass that error along like so. And we're actually going to say return so that that will sort of short circuit that callback function. Now on the other hand, if everything went well, right, if the contents of the file were successfully loaded, then we can just call resolve with those contents.

3:05And that is all that we need to do, right? That will allow us to use this read file function in this way with really any file we want. So let's give this thing a try what we're going to do. Actually, we already have the code written out here, but let's actually add a catch block there because I do want to simulate an error at some point just to make sure that that part works as well. We're going to say console.error error error. And if we run our code now, what we're going to do, all right, oops, let's try that again, we'll say node, promiseify challenge.js.

3:38Sure enough, we'll see all of the contents of the file logged out to the console, right? And that is again, coming from our dot then callback, right? It's not coming from the original FS.read file function. I mean, not directly anyway, we've successfully converted this read file thing so that it's almost indistinguishable from FS.read file from the FS.promises portion, all right? So let's check out what happens if we have an error.

4:10Let's just misspell the file name here. And if we run our code here, sure enough, we'll see that it rejects our promise and we get the error logged out to the console. Cool. So that is the read file function. The other function that I told you to do was the unlink function. So I'm going to do the same thing here. We're just going to say unlink and that's going to take the file path that we want to delete. And same thing is going to go for this one, right? If we want to be able to say unlink dot then, we need to return a promise in here just for, just to give you a little visual here, I'll say unlink.

4:46And we'll, let's let's actually delete a file that we don't need as much. We'll delete file for here. We'll say unlink file for dot txt. That's all we need there, but what we want to be able to do is say dot then, and obviously have some callback in there and dot catch and have some callback there. Now, once again, in order to do this, we need this unlink function to return a promise. So here's what that's going to look like. We're going to say return new promise. And we're going to just have resolve and reject same old arguments as before.

5:20And the next thing that we're going to do is we're going to say f s dot unlink, just use that classic callback based unlink function, or the callback based version of that function anyway. And we're just going to pass the file path along to it as well as a callback. And that callback's just going to take an error in this case, because there's not really any result that we want to subscribe to. So in this case, we just have to say if there's an error, we're going to reject with that error.

5:51Otherwise, we're going to resolve. And since there's no result that we want to pass along, we're not going to pass any arguments to that. Cool. So let's try this thing out. Let's just add a dot then callback there. We'll say dot then and then we'll say something like console dot log deleted file successfully. Oops, I misspelled successfully there. Let's add another s. There we go. And for catch, what we're going to do is we're going to say error. And what we'll do is of course, we'll log out the error with console dot error.

6:26There we go. And then we'll say something like console dot log and we'll just log out something like unable to delete file. And oops, it looks like I had a spare semicolon there. So let's remove that. And now we should be able to run our code and the read file function is going to run as well as the unlink function. So let's just give this a try. We're going to run node promisify challenge dot JS. And sure enough, we see that it deleted the file successfully.

6:57At least that's what it says. And if we go in here, sure enough, we see that file for is no longer there. All right. And well, that is the solution to the first part of the challenge. The second part of the challenge was pretty interesting. And really what I was kind of hoping would happen for you was by seeing these two promisified functions that you created side by side, you'd notice that there's really very little that's different about those functions. And you could maybe create a more generic version of those.

7:30So basically what I did for this is I just took, I just took the read file function. And what I did is I pasted that inside here. All right, that gave me a good start for creating a generic promisify function. And in this case, what we would want to do is not return a promise immediately. We'd want to return a promisified version of this function. So what this would look like is we would say return function. And this would take some undetermined number of arguments there.

8:04And basically this function inside of here would be the one that returns the promise, right? In other words, look at what we had to do when we converted FS dot read file, we had to actually create another function that returned a promise. So in order to generically promiseify any function, we can just return a function that returns a promise. All right. Now, the tricky bit here is actually getting that actually tapping into the callback there because what we have to do is we have to say something like const callback.

8:39And this is just going to represent the generic node JS callback with error and result. All right. And what we're going to do with this is we're just going to have this callback function do essentially exactly what we did in here. So in fact, we can just take these two lines and put them inside callback right there. All right. And now we just have to make sure that this callback function gets to the right place in the arguments that we're passing through to whatever function we're promising.

9:09So what that's going to look like is this, we're just going to say instead of FS dot read file, we're going to say FN, whatever function it is, we're promising there, we 're going to pass all of the arguments to it except for the last one. And I allowed you to add this CB index in there because that just made it a little bit easier. So what we would have done there is we would have said something like const new args equals args. And then what I did is I just said new args index callback index equals callback.

9:46All right. So now all we have to do is pass those new args to our function. Like so we're just going to say dot dot dot new args, which will pass all of those as arguments to our function. And we're just going to say, actually, we don't even need to say return there because any value will get passed through to either resolve or reject. Oh, and sorry, this should be a result here instead of contents. So that should be all that we need to do there. Oh, except for one other important thing, this callback actually needs to be inside this executor function so that it has access to resolve and reject.

10:24So let's just make that change there. And that should be all we need to do. So let's just try this promiseify function out. And to do this, I'm just going to move it. Actually, I don't need to move it anywhere. I'm just going to comment out our read file and unlink functions. And we'll replace those with promiseify called on FS dot read file and on FS dot on link. So here's what that's going to look like. We'll say read file equals promiseify. We're going to call that on FS dot read file.

10:56And then we'll say const on link equals promiseify. And we're going to call that on FS dot on link. And that should be all that we need to do. So if we run our code again, actually here, let's try deleting file three now. If we run our code again, we should see that both these things work in just the same way, but we now have this tremendously powerful promiseify function that will allow us to promiseify any function that doesn't that that doesn't natively support promises.

11:29So here's what this is going to look like. We're just going to run our code again. And oops, one thing I forgot is we need to actually specify the callback index in these cases. So that's going to be index two for read file and index one for unlink. So let's try that again. And sure enough, we should see that it deleted the file successfully. And lo and behold, that file is no longer there. Now again, I think I mentioned already, but if you wanted to make the assumption that the callback was always going to be the last argument, which is true in most cases in no JS, you could save yourself having to pass in that extra CB index argument there.

12:07But I'll leave that up to you as an exercise. So I hope this has been informative for you. And I'd like to thank you for viewing. [BLANK_AUDIO]

Frequently asked questions

What does the Intermediate Promise Concepts lesson in the JSNAD course cover?

This 48-minute lesson shows a developer how to create a custom promise with the Promise constructor and an executor function that calls resolve or reject, and how that outcome decides whether the chain runs then or catch. It also covers organizing multiple asynchronous operations into sequential results with a then chain and wrapping setTimeout in a promise to build a reusable delay.

What should a developer know before starting Intermediate Promise Concepts?

The trainer recommends watching the previous skill on promise basics first, which covers simple sequential promise chains and error handling in promises. The course as a whole is aimed at software developers with roughly three to five years of back-end JavaScript experience.

How do you convert a Node.js callback function like fs.readFile into a promise?

In the lesson's challenge, the learner writes a function that returns a new Promise and calls fs.readFile inside the executor, so the callback has access to resolve and reject. The same approach is applied to fs.unlink, then generalized into a generic promisify function that converts any Node.js callback-style function into a promise.

Why would a development team need to promisify callback-based libraries?

As the trainer notes, teams often depend on libraries that only work with callbacks, and it is generally always possible to convert a callback into a promise. Promisifying those functions lets developers chain them with then and catch, which supports the course goal of managing asynchronous control flow more consistently across a codebase.

How does this lesson fit into JSNAD preparation and the wider course?

This skill is one part of the OpenJS Node.js Application Developer (JSNAD) course, which spans asynchronous patterns including callbacks, promises, async/await, generators, RxJS, Async, and Bluebird. The full curriculum represents about 32 hours per learner, so Team Leads should plan it as a multi-week enablement effort aligned with internal coding standards for promises and async code.

How can a Training Manager track a team's progress through the JSNAD course?

Team reporting provides per-learner progress and completion, cohort and group views, and completion exports for audit evidence. Training Managers can use these views to confirm developers have completed promise-focused skills like this one before moving on to async/await and testing topics.

Team training path

Turn this skill into assignable team training

This free skill is a preview of the courses your team can assign, track, and report on with CBT Nuggets.

What's next?

Ready to keep going?

For your team

Bring this training to your team

See how CBT Nuggets helps IT teams close skills gaps, hit compliance targets, and prove training ROI.

Book a Demo
Just need JSNAD?

Learning on your own? Browse individual plans ($49/month, billed annually) →

Not ready to buy?
with no purchase required. Already have an account?
Book a Demo