API Design in Node.js (using Express & Mongo) Nested Promises
This course has been updated! We now recommend you take the API Design in Node.js, v4 course.
Transcript from the "Nested Promises" Lesson
>> Scott Moss: So one thing to note here is whatever you return inside a .bin is automatically wrapped in another promise for you. So what that means is if I just return the value 'hey'
>> Scott Moss: I'm able to obtain, excuse me.
>> Scott Moss: I'm able to chain another .then on here because it returns a promise.
[00:00:35] And I get a value of whatever the previous promise was. Which would be like a word, which is hey. So then I can just console.log word.
>> Scott Moss: So if I run this, hey. Whatever I return inside of a .then, I can return something else here. And I can do .then note as well.
[00:01:07] That also means that I can also just return other promises in here too, right. So if I just wanted to return read file again, and this time maybe it reads a different file I could do that. I could return a promise, I could return a regular value. Whatever I return here is gonna be a promise.
>> Scott Moss: So, that's how you would deal with nested call backs and chaining of the promises. Just make sure that you return something to one of these. So, a good pattern what people do is they would just define these callback functions somewhere else. So, there's okay, read file .then(logfile) and .then(sendEmail), .then(callHome) whatever, and then, okay .catch.
[00:01:56] Now tell me what happened. So this is a pattern you'll see a lot where these functions will just be defined somewhere else. And what happens is these functions will consume the resolution of the promise before them. So whatever read file resolves will be fed to log file. Whatever log file resolves will be fed to send email.
[00:02:18] Whatever send email resolves will be fed to call home. And then if any one of these errors it'll immediately go here. So, during the time of, let's say we do read file, everything happens. We resolve and then we go to log file. And then somewhere inside of log file it errors out.
[00:02:34] It'll skip these two and come down to catch, throw you the error.
>> Scott Moss: So, this is a common pattern you'll see.
>> Scott Moss: Any questions on this?
>> Student 1: Do you have nested promises in each of those functions [INAUDIBLE] doesn't belong execution of-
>> Scott Moss: Yeah, it will change.
>> Student 1: It does?
>> Scott Moss: Yeah, so if you have, let's make log file, so let's say log file was this. Var logFile was a function that returned readFile which is a promise and then it had its own promise chain in here that returned readFile again.
>> Scott Moss: All right. So what's going to happen is first, we'll do the read file.
[00:03:34] That'll get resolved. It will then go to log file which will come into here. It'll do read file again. That's going to resolve. It's also going to do read file one more time. That's gonna resolve and then it comes down to sendEmail.
>> Scott Moss: So, it will recursively go down that chain synchronously.
[00:03:55] So, it pretty much turns your asynchronous operations to synchronous operations.
>> Scott Moss: Hm?
>> Student 2: There's also all-.
>> Scott Moss: Right so, I was gonna get into that. So any questions on this? Yes, Mark.
>> Mark: They're asking on when you're changing all those thens do you only need to call the catch once?
>> Scott Moss: Yeah, you only need to call the catch once but sometimes you want more fine control for when these things happen. So for instance this .catch is gonna happen if anyone of these things errors out. Which is great but what if you wanna know what happens if logFile errors out specifically, right.
[00:04:35] There's two ways you could do it maybe you can throw specific errors and check for the error types and catch. That could be okay but let's say you didn't make log files so you can't really define the specific error types. So what you would do is, most promise libraries take a second argument and that's what you'll see.
[00:04:50] What I'm doing in mongoose has a second argument that you can pass to them and that's the error call back. And that's optional so it may happen or it may not happen. So if that end result was resolved, it will call this call back. If it was rejected than it 'll call this call back that way you can get notified about this promise.
[00:05:12] So it's like just stop execution right here and tell me what happened. But yeah, you could just call .catch at the end of a chain and it will tell you where. Any other questions on this? Okay. One more thing I wanna show you that is very helpful in the context of database is another method called promise.all.
[00:05:38] I think this is very helpful, because if someone had shown me this a long time ago versus me trying to figure out what it does would have been great. So, promise.all will resolve an array of promises and it will wait until all of them are done before it goes on which is think is very helpful.
[00:05:53] So it allows us to things in parallel you can say. If we have database query that don't rely on previous queries. We can just run them in parallel and when they all come back we can merge the data. So, yep? Mm-hm?
>> Mark: I'm just kind of following up on the last.
[00:06:08] If you are doing the air call backs, then you do not need the .catch? So, if you're catching those airs on each one of them?
>> Scott Moss: Yes, if you're catching airs on each one, you don't need the .catch.
>> Scott Moss: So, let's say we have, an array of promises.
[00:06:26] So we'll say var read or we'll call it promises. And it's just read file like three times. Cuz that's a promise. So we'll call readfile three times. So you have an array of promises, cuz we know when you call readfile, it returns a promise. So we have an array of promises right now.
[00:06:50] They've been executed. But now what we want to do is we don't wanna go to the next thing until all these things come back. So what we'll do is we'll make a new function that will say var readAllFiles. That's going to be a function and we'll move promises up there, and instead of returning a new promise this is going to return promise.all.
[00:07:17] So promise.all is just going to take this array of promises.
>> Scott Moss: And that's it. And now we're able to call .then on the readAllFiles. So now if I come down here and let's just get rid of this promise chain. Let's say readAllFiles .then.
>> Scott Moss: And I say files.
[00:07:51] It's gonna be an array of all the files in the order that they were arranged here.
>> Scott Moss: So, if we run that we'll see that's an array. I'll just put .length, because that's really hard to see. All right, so there's three things in the array, and as you can see, they're all that file, three times.
[00:08:20] So it weighted, so all those promises came back, and it's like okay, here you go, and they might come back at different times.
>> Scott Moss: So this is why a third party libraries are cool because they have other methods to even add more functionality to this. Like do the same thing for objects or maybe don't go on if all things in the array don't resolve or continue if one thing fails or stuff like that.
[00:08:44] There's so many cases. I don't think the built-in promise library has spread built into it but we can check it. If it has it I'll tell you about it, if it doesn't, let's see. Yeah, it doesn't have spread so what spread would do, it would just spread these files into multiple arguments.
[00:09:01] So instead of sending back an array, it would just be like file one, file two, file three. And different arguments. It'll just spread the arguments. So, there'll be file1 here, file2 here, file3 here.
>> Scott Moss: So, that's what third party stuff has that the built in one doesn't. But, as you can see it's pretty simple.
>> Scott Moss: Any questions on this stuff? So, the reason they made the A plus spec, is so that you can use different promise libraries together, and you can be sure that this method will have the same syntax on it. That's why I might be able to use a native promise function call with a Mongoose promise function call.
[00:09:49] Cuz they're both following a spec, or at least, Mongoose uses impromise, which I don't think uses a spec 100%. But you can mix them up. And most third parties [INAUDIBLE] has a method that allows you to wrap another promise, so it will work. Like Bluebird allows you to do that.
>> Scott Moss: So that's our foray into promises. Like I said, there's a lot more. I recommend reading up on a lot more if you're gonna be doing a lot of new development. A really good practice actually is just build your own promise library. It seems really hard.
[00:10:22] But it's actually just a whole bunch of like caching and call backs and events. It's really not tough. There's like a thousand tutorials online that shows you how to build your own promise library. Probably take you an hour to get some basic functionality. That's what kind of worked for me after I did that.
[00:10:36] You had questions Mark?
>> Mark: Yeah a couple. Have you ever used fibers?
>> Scott Moss: I've never used fibers but I've been, my friends been telling me about it. So if anybody's used it and they have an opinion on it that would be great.
>> Mark: And then are promises available in the ES spec?
>> Scott Moss: Yes, they are.
>> Mark: Native promises?
[00:11:10] So it is right there, all the documentation is there. I think it is even built into the browsers now so if I open up chrome and I type in promise it is right there. It is built into chrome too.
>> Scott Moss: Cool, any other questions on this? All right.
>> Scott Moss: Will this be helpful? Should I check this in to source code for anybody that might need this? Okay, I'll do that. It'll be on step nine fix.