WARD:
[Singing] Down by the sea, yeah, yeah... On a blanket with my baby, that's where I hold these...
[This episode is sponsored by Hired.com. Every week on Hired, they run an auction where over a thousand tech companies in San Francisco and New York and LA get on JavaScript developers providing to put the salary and equity upfront. The average JavaScript developer gets an average of 5-15 introductory offers and an average salary of over $130,000 a year. You just can either accept an offer and go right into interviewing with the company and neither with that any continuing obligations. It's totally free for users, and when you're hired, they'll also give you a $2,000 signing bonus as a "Thank You" for using them. But if you use the Adventures in Angular link, you'll get a $4,000 bonus instead. Finally, if you're not looking for a job but know someone who is, you can refer them to Hired to get a $1,337 bonus if they accept the job. Go sign up at Hired.com/AdventuresinAngular.]
[Does your team need to master AngularJS? Oasis Digital offers Angular Boot Camp, a three-day, in-person workshop class for individuals or teams. Bring us to your site or send developers to ours -AngularBootCamp.com.]
[This episode is sponsored by Wijmo 5, a brand new generation of JavaScript Controls. A pretty amazing line of HTML5 and JavaScript products for enterprise application development. Wijmo 5 leverages ECMAScript 5 and each control ships with AngularJS directives. Check out the faster, lighter and more mobile Wijmo 5.]
[This episode is sponsored by Digital Ocean. Digital Ocean is the provider I use to host all of my creations. All the shows are hosted there, along with any other projects I come up with. Their user interface is simple and easy to use. Their support is excellent. And their VPSes are backed on solid-state drives and are fast and responsive. Check them out at DigitalOcean.com. If you use the code “angularadventures” you'll get a $10 credit!]
CHUCK:
Hey, everybody! Welcome to Episode 55 of the Adventures in Angular show. This week on our panel, we have Lukas Reubbelke.
LUKAS:
Hello!
CHUCK:
John Papa.
JOHN:
Hey!
CHUCK:
Ward Bell.
WARD:
Hey, hey, hey!
CHUCK:
I'm Charles Maxwood from DevChat.tv. This week, we are going to talk about "Promises in Angular".
JOHN:
I'm really upset that Ward just triple heyed my hey...
WARD:
I'm going to quadruple it! Hey, hey! Hey, hey!
JOHN:
[Laughs]
CHUCK:
He just resolved that Promise like 4 times or something.
WARD:
Exactly. Now, the thing you got to know about Promises is that they're meant to be broken.
JOHN:
I reject that insinuation. And, I defer to you, Charles.
CHUCK:
[Chuckles] Thanks.
WARD:
Ohh! I've used a pun, go to prison, John. [Laughter]
JOHN:
We are such geeks.
CHUCK:
I know, right?
JOHN:
Here's the deal, guys... Promises, we all use them, and you're in Angular, you can't escape them. You may want to try to run and hide, but you cannot. It's not just Angular, it basically disconnected asynchronous world to either in callbacks, or Promises, or something similar. But, a lot of folks that get into Angular don't have that experience, maybe you haven't gone down that road. So, I think it would be interesting talk about "How do you get into the world of Promises and deal with it?" "How do you deal with making sure you understand your position in callbacks and Promises?" And then also, "How do you deal with the common pitfalls that definitely ensued?" It's not a perfect world, alright.
CHUCK:
So, let's start at the beginning. What's a Promise?
JOHN:
A Promise is when you're inside of Angular (because we're talking at Angular Show), you want to go do something that's asynchronous, but you don't want to have reblocking action. For example, you might want to go back to by the API and say, "Go and ensue new data." I want to get that data, but I know how long it's going to take. But, I don't want to block the UI and say, "Hey, sorry, Mr. User, you're not going to be able to do anything."
So, what a Promise does is to say, "Okay, go get the data, I'll give you an IOU and say, "I promise to return and let you know when the data is here." In the meantime, carry on [incomprehensible] and do with your thing." And then when the Promise comes back and says, "I've got your data now", it will actually notify you and cache in the IOU and say, "Here's your data," and it'll do your data binding, do your thing, and show it on the screen.
CHUCK:
Do Promises trigger events? Because I always thought of them more as bookmarks where it's like, "I need this information, and I know I need to get it asynchronously," so I make the request. And then when I come back around and needing it, then I cache in my IOU or I go look in the bookmark and the information is there now.
JOHN:
I think that's a good way to explain it, the bookmark analogy there. I haven't used that before, but that works. It's not an Event. You can call an Event in the Promise (I've seen that before as well), but it's not the same as an Event. It's a way to deal with the asynchronous nature of the browser because it's not connected to the backend directly, right?
CHUCK:
Right.
WARD:
Well, actually, it's a way to deal with any asynchronous thing. So, you could be waiting for the user to click on some kind of a model box, actually, anything adventure of them at all is an opportunity to represent that as a Promise. A set Time-out can be represented as a Promise. It just happens to be in the age of JavaScript that there are all these different ways to deal with Events, which by the way is what you're also dealing with when you're waiting for the server to respond. They're waiting for an Event.
LUKAS:
An analogy, if I could make one here that has worked well for me is, a Promise is like when you go out to dinner and you go to, let's say, you're going to Chilly's, you're going to [incomprehensible] and say, "Party for 4," and the hostess serves you a pager that you take with you. Now you have a token or a physical thing that represents something that's going to happen in the future. And then, when your table is ready, the beeper goes off and then you basically take that and exchange it for a
table for four, for instance, and that is how Promises has to work. This is to make a call, so begin some asynchronous action, and you receive essentailly this object that will be resolved in the future. Once it's resolved, essentially your code beeper goes off, and it is then translated into your request or resolves into the actual value or whatever issue this application you were doing.
CHUCK:
Right. So then, how do Promises play into Angular? How do I get my nice little Angular beeper?
LUKAS:
Promises are a part baked into some of the underlying services that come with Angular. The $http service, for instance, for ng-animate has Promises paged to them. But you can also own your own Promises using the q service.
WARD:
Yeah, there's actually an object called $q a service; so, service like any other service. But, it's a service that is capable of producing Angular Promises and managing them.
CHUCK:
Doesn't that actually wrapped around the q library?
WARD:
It doesn't. In one point, I was really disappointed about that because qJS, the library, was and is a great Promise library, very rich. But, it's also quite big and the Angular team also wanted to put something into it that is not in qJS, which is the ability to mock it and test it. So, they device their own Promise service called the $q and I think all mashed to qJS, stripped it down bare bones, it since gotten a little sugar on it, and therefore, they have their own Promise library that is very much modeled on the fundamentals of qJS.
JOHN:
Ward, I am really amazed. It only took you 5 minutes to turn this into a testing discussion. [Laughter]
WARD:
Hey!
JOHN:
But it is a great point. When I start getting into Angular, I was aware of q as well and so was Ward, and that was one of the things he's started looking at. I was like, "Okay, q is pretty awesome!" q is a great Promise library and, at the time, it was definitely the best. Now, it may be debatable for this or isn't, but it allows you to do a lot of things to write Promises because made of JavaScript didn't have anything like that at the time.
With q, you can do things like set up a Promise, you can reject the Promise, you can resolve it -reject and resolve are just fancy words for "this Promise came back" and I liked the answer or I didn't like the answer. For example, use it at http, if I go off and use a Promise to get some data, if something happen on the server, that will then be a clot in the .catch and then you could then reject that Promise and say, "Yeah, I got an answer, but it's not what you want and you won't be happy about it."
CHUCK:
You mean like a 404 or 500 or something that isn't useful?
JOHN:
Right. It's not what you expected. You didn't get back your list of customers; you got back that pile of poop. That's when you would reject that. And resolving it is, "you got what you wanted, here you go, resolve that," and continue on your merry way. So reject and resolve are like an article methods that usually hang off a Promise when you deal with those.
One of the reasons a lot of Angular developers may haven't heard of these words is that $http is probably the most often used service in Angular that uses Promises that people may not necessarily know that he's using Promises. When I say $http.get() -- I hate writing pseudo code on a podcast -- but $http.get(URL), that's a function that's going to often run away and do something asynchronously. And then if I .chain the .then and the .catch after that, the .then is, "Hey, everything works, I can then operate on it, and the .catch is something went bad and I can operate on it."
WARD:
You mind if I back up on that a little bit, John, your verbal programming, because I want to do some verbal programming, too. When you take that $http and you call .get() on it, what you're getting back is a Promise object. You get that back immediately, and it's in an unfulfilled state. So, Promises are either fulfilled or unfulfilled. That's just like when you hand me that beeper that Lukas was talking about, it hasn't rung yet. That's what you're holding in your hand immediately and then your code continues to execute. That's an important thing to bring across -- that your code, right after you make that $http.get() call, continues to operate. Now you got that Promise object and then you can .chain on to it things that you want to have happen when that Promise is fulfilled. And the .then and the .catch finally are the methods by which you .chain on to a Promise, what it is that you want to do when the Promise is fulfilled. Does that helped at all?
LUKAS:
It's also worth mentioning real quick, it doesn't get used a lot, but also it's actually essential like .notify callback if you're doing, for instance, like asynchronous operation that is long...
JOHN:
Yes.
LUKAS:
You can actually track the progress so like, for instance, at least when I'm upward in [incomprehensible] to like S3 or whatever, then you can also say, "This is partially resolved, and here's the status of this," so this is good for any animation or any feedback to the user.
JOHN:
That's a great point, Lukas, because we use .notify quite a bit for Promises that we want to have some progress notified about. Let me say it in another way: for example, if I'm doing a progress bar and I have some sense of, "this thing is going to take a little while," and I want to be notified along the way so I can change that progress bar [incomprehensible] a Promise, the only way I can really do that effectively is through the .notify that you can .chain. But, I probably wouldn't use that in the case where I have a Promise and there's nothing for to notify about, like it has no idea what is actually happening. So, the .notify is for like progress bars and stuff, but it's not used a whole lot.
It's not used as often as the .then and the .catch, I would say.
CHUCK:
Can I clarify something here really quickly? If you make an assignment unlike the $http.get() and then you do a .then on the response, let's say that you get some JSon back, are you saying that you can actually massage the data there and then assign that back to something?
JOHN:
Yes, and that's one of the big reasons I like the .then better than doing the, there's a helper function that comes with Angular as http called .success, it's one of the reasons I like the .then. The .then gives you the opportunity to be, first of all, more in-line with the Promise library's offer for an API, the .then. And, it also allows you to have the option to say, "I've got the response back, I can strip off the headers, the data, the status, I can get rid one other there," and it's really useful for -- have you ever had a data set, Charles, that you hit it back in and for some reason, it's stored in some structure that's like mydata.todaysdata.customer.firstcustomer.
CHUCK:
Uh-huh.. yup.
JOHN:
And all you wanted was the thing that's like the fourth or fifth dot.
CHUCK:
Yup.
JOHN:
This then allows you to then say, "You know what, strip all that beginning crap off, and let's just return the thing I actually want."
CHUCK:
Right.
WARD:
Actually, to be precise, it returns a new Promise that would return the thing you actually want.
JOHN:
Yes. So, Promises are almost short-circuited -- not shor-circuited -- but you do a Promise and then you return another Promise.
CHUCK:
Promises all the way down.
WARD:
Any individual Promise is immutable, but all these .then and .catch and all the methods that hang off of it, they all return a new Promise that's based on the previous Promise.
CHUCK:
Right. And that's how you get the chaining; it's because all these Promises have the same [incomprehensible], actually.
JOHN:
Yes. And that's a great point, Ward. You're not having one long Promise; if you keep chaining .then, you're actually creating a new Promises and resolve them as you go.
CHUCK:
One other thing that I'm seeing here, if you call .catch then -- because .catch handles some error state or something -- if you make an assignment then, for the result, then you might get that error result or the result that you expected. So then, do you have to handle that further down in your code? Or, is there a clean way to handle that as part of the Promise?
JOHN:
Yeah. In the .catch, you generally want to handle -- something bad happened -- so generally, something it'll do in perhaps, say, services or factory in Angular would be, first, you might want to log that thing, whatever codes it might have, and then you might want to set some state, not always, but sometimes, you want to say, "I was expecting this or that, but I want to set some state." But then, probably the most important thing is something Ward has kicked me a couple of times for forgetting to do. And Ward, what's the most important thing you should do inside of a .catch for a Promise?
WARD:
Make sure you return a new Promise. And, that's either a Promise that usually, since you now know what you want to do, it's a Promise that is already fulfilled as to say that is ready to be chained, too, and actively use. Out of exception one, out of the .catch one, it's typical that you would return a rejected Promise that has the reason in it so that the next consumer in the .chain can behave as it wants to behave.
JOHN:
Let's talk about "Wire". Let's say you're in a controller -- the controller is a customer controller -and he's calling some service at the Angular service saying, "Go get customers," something went bad, and you .catch that service (let's say you didn't do what Ward just said), you did not, inside the .catch, return a rejected Promise or any kind of Promise. Inside the controller, and the customer was waiting for that answer, it's not actually going to do what you think it's going to do because now, the .catch didn't return back that Promise so it really doesn't have a good way of knowing that that thing failed.
WARD:
As a matter of fact, it's worst than that. It will, if it train to then on it, it will now think that it succeeded. Because in a .catch, you can actually put it back -- there's like, out of a Promise, there's a happy path and a sad path. The happy path is the .then, success path, and the sad path is the .catch, bad exception there. But you can theoretically return, out of that, a new Promise that is back on a happy path again. Maybe you're able to fix whatever was wrong. And by returning nothing, you implicitly cause the Promise library to return essentially a fulfilled success Promise that has no body in it, has no data in it. So downstream, if you had like your View model, you had some controller waiting for your service to respond from the server with data. And if it went to the .catch phase but you didn't return a rejected Promise, the outer calling of a controller is going to think that your call to the server succeeded and returned an empty data option, not what you had in mind.
CHUCK:
So, what you do in that function then is you create the Promise, you reject the Promise, and then you return the rejected Promise?
WARD:
Exactly. But you can do it in one step.
JOHN:
Yes and no. Yeah, that's the point, right. Most people have to do, when you say that, is they say they'll create the deferred by $q.defer, create it, they'll do something with it, return it, but more pointed out to Mr. Promise. There's a little method that you can do there, $q.reject, and that will actually reject it right away for you. You don't actually have to set up the deferred and do all the work, it's a short cut method on $q that actually makes it just a one-liner. And the thing I always forget is, I forget to return that.
WARD:
Yes.
JOHN:
So actually, I'll $q.reject, but I'll forget to say return $q.reject.
WARD:
Right. Let's suppose you wanted to have a .catch way down there in the data service and all it did was log that there was a problem and then it said, "but I don't know what to do about it," so I'm just going to pass the error along to the next guy who called it. So then, your function would be very simple. You even have your $catch, and inside that, we put your handler, just a function that takes the reason, and in there, we go "let me log the reason," and then I'm going to turn right around, then forward this on. So, I'd return $q.reject(reason), and I'm out. It's a two-liner. And all I really did there was to pipe line in the ability to locally log the reason that came back from the server and then without doing any more massaging, I just passed that along to the color so the color can do something with it. That's the simplest kind of pipeline passed through of a .catch that you can do. It would be probably more useful if the data service actually took that reason, parsed it, may try to make sense out of it, and then gave back to the View model something that boiled down -- cause there are like 50 possible ways that they can go wrong -- it might boil that down into something that the controller could reasonably understand.
JOHN:
That gets to an interesting topic. Let's say you're in the controller sample again and you're asking for data from a service, and the service is a Promise to go get it, and let's say it fails, you do want to log that information there, probably the wrong information logged there, so it has some idea of what went wrong. But, that's not the information you want to show the user and nor would you want to show a dialogue to a user in a service. So you bubble something back through rejecting the Promise in the service to the controller, and then, Ward, at that point, you're in the controller and you've got a .catch statement, what do you do then there? What do you recommend to do to show the user, "Hey, there's something bad happened"?
WARD:
Well, one hopes that now, what you've got is a reason that you can actually execute on as a controller. And if something that all of the muddy details of talking to the backend have been stripped away, now you've got something that you can make a decision on, and it would be something that you had negotiated as part of your API with the data service, and it would send you back a reason that you could do something with. What could you do with that? Now, you really have to think, "What is my user experience supposed to be here? What am I supposed to tell the user? And, how am I supposed to make the View change?" It could be that you would throw a reason into a toast that would pop up and let the user know. But hopefully, you would then also say, "Here's what you can do about it." It would be an opportunity for the controller to communicate in some way to the user that would explain it in user terms, not programmer terms...
JOHN:
Yeah. Well, let me interject first, Ward, because something I think is important, too, and I agree with what you're saying. Also, what don't you do in the controller in that .catch? I affirmly believe, most cases, you should not be logging in the service and in the controller because I've seen this a lot.
WARD:
Yup.
JOHN:
Every time a Promise bubbles through a service to a service to a controller...
WARD:
[Chuckles]
JOHN:
I end up looking in the log, I see the same darn message in 3 places.
WARD:
That's Demoware.
JOHN:
Yeah, log it the first place, log where it happened. And at that point, after that, it's really UI decision, right?
WARD:
Yup!
JOHN:
It's "how do you want to get this information to user?" Or, do you want to just basically just pretend it never happened and don't show the user anything?
WARD:
That is a good choice...
JOHN:
Which is, I guess, another option.
WARD:
Good choice. But, the user is not the person to say for or forward to, right?.
JOHN:
No, no...
CHUCK:
Can I clarify something here real quick, though, because I haven't really played with these Promises, and I've always just done the success/fail functions in my Angular so I'm really interested in this. If I have a .catch and it say, for or forward, so I log out to the console, it for or forward, and then I pass it along, then it'll trigger the other .catch on the http in my controller?
WARD:
Presumably, your controller isn't... I don't know. Did your controller actually called $http?
CHUCK:
Some of my older code does... If I have a service, though, the service will handle that.
JOHN:
Right.
WARD:
Right. So you would've said, service.getmedinner.
CHUCK:
Uhmm-hmm..
WARD:
It would've gone off, and it would've come back and said, "I can't get you dinner."
JOHN:
Now, I'm hungry.
WARD:
Now, I'm hungry. 404, no dinner found.
CHUCK:
Dinner not found, right?
WARD:
Right. Dinner not found.
JOHN:
302, re-direct to 304. [Laughter]
WARD:
You don't have the right to have dinner! Anyway, whatever it is, the service would hand you back something that would be ready for you to interpret. Now, inside the controller in your .catch, you have to say, "How am I going to tell the user that they can't have dinner?"
CHUCK:
Okay. So, the service just sends out or returns a message that's not a...
WARD:
It returns a reason object, a reason object... It could be a string, and that's what your .catch, your controller .catch function is going to receive that reason object.
CHUCK:
Okay.
WARD:
And now, it's the controller's job to turn that reason object into something that would make sense to a user.
CHUCK:
Alright. So I'm not passing the rejected Promise out to the controller?
WARD:
The service did return a Promise, a rejected Promise, with a reason. And then your .catch, which you wrote inside, your controller would've said, service.getmedinner.then(blahblahblah).catch, and then you would have a little function that you put in there that, let's say it was called getdinnerfailed...
CHUCK:
Okay.
WARD:
Now, you write a method called "getdinnerfailed"
CHUCK:
Right.
WARD:
And has a reason object, and that's what you go downtown on inside your...
CHUCK:
Okay.
JOHN:
That method in the controller, Charles, when I was [incomprehensible] or whatever it's called, the name in the method .failed, or the name of that .succeeded. So, the name of the method .failed, to get to know .failed actually has a parameter pass there, which is basically, whatever their value was that you pass to reject. So very specificallly, in the service, when you have a .catch, you'll say, return $q.reject(reason), maybe a string, an object, or whatever you want. That thing that you pass in that reject is actually the parameter that goes into the function and the .catch statement in your controller.
CHUCK:
Got you.
JOHN:
And then you could decide what do I do with this? Just play it with user, show a <blink> tag, dancing Lukas is on the screen, whatever you like.
LUKAS:
Pa-pa-paw!
CHUCK:
[Chuckles]
JOHN:
[Laughs] All I could ever think of now, Lukas, is you dancing with the tear away pants that you did on ng-conf...
LUKAS:
Oh, man... [Laughter]
LUKAS:
Immortalized...
WARD:
Ohh... I'm so sorry, I missed that.
JOHN:
We need a link to that video [laughs].
CHUCK:
[Chuckles]
JOHN:
I'm going to find it now [laughs].
CHUCK:
The pants weren't leather, Ward. Sorry to disappoint.
WARD:
Darn it!
CHUCK:
I'm really digging this. Now, I got to go re-write some code.
WARD:
Right. So you're always passing along a Promise; always passing along, passing it down the .chain.
CHUCK:
What about .then? Does .then return a Promise? Or, does it just return results?
WARD:
Absolutely! .then always returns a Promise.
CHUCK:
Okay.
WARD:
All those methods return Promises, that's really [incomprehensible]..
CHUCK:
Okay.
WARD:
Alright? It's just when those Promises are fulfilled by this successfully, or not successfully, that matters that then kicks off the call to some function inside. And only when that long .chain of thing, for a particular .chain of Promises, when that's finished, it doesn't end, it doesn't stop passing along.
Now, one of the cool things, though, is -- remember we said that the Promise is immutable -- once the Promise has been fulfilled, once you had it, if it was in its success state, it's successful, if it's in a failed state, it was failed, but it's going to stay that way. That means, for example, let's suppose I had a red asynchronous-ready method, my application has to start up, this data service has to go do some stuff, asynchronous stuff, and it's going to return a Promise when it's ready.
JOHN:
So, that might be a scenario where like you go out and you get a bunch of data, for example, maybe 7 different calls, and you want to go get data there and they're going to fill dropdown boxes. And it's not ready until that's all done.
WARD:
So you have like 6 controllers that are all asking this data service. Are you ready? You don't know which one of those controllers and the data service is going to call the data service first. It's real simple. They all call data service, are you ready or ready. What happens is they all get the same Promise back because inside the service, the first time it's called, you realized that you haven't initiated it, you don't know who's calling it, but you know, "Hey, this is my first time, I'm inside the service, let's get ready." So it starts the asynchronous processes of getting whatever ready means getting the data, and then it has a Promise. But, subsequent controllers who ask for ready get that same Promise back. The same one was return to the first guidance started this whole thing. So, all 6 controllers are all waiting, sitting on the same Promise. When that Promise resolves, it's resolved once and it's resolved forever, meaning that the service was ready. And every controller, now or an hour from now, that asks for ready will get that same fulfilled Promise, and therefore, it won't wait. Every controller from now, it took forever, calls ready, will have an instantly ready Promise in its hands and will be able to continue.
CHUCK:
Was this automatic? Or, is this how you should write it?
WARD:
That's how Promises work.
CHUCK:
Okay.
WARD:
And I use that ready strategy a lot.
JOHN:
Yeah, because you can write your own Promises. So, you get some for free, which is the cool thing about Angular. Any time you do $http, you're getting a Promise for free, which is why many of us, when we first started, we don't really think about it. There's a pitfall that is very easy to walk into, that I want to make very clear off, too, let's say I've written this code -- so I'm not framing anyone else from the bus but me -- let's say in the controller, you write this line of code, I've got a variable called 'customer', and it's a customer name, and I need to go get that. So in my controller, I've got script for that customer or VM.customer in the View controller apps. I could say VM.customer=$http.get(myURL). Now, that line of code seems inaccurate because you're saying, "Ah! It's very instant! I just went off and called myURL, and myURL is going to return the customer, and I'll just set the return $http.get() right to my customer. Looks like it's going to work." In Angular, I believe, Angular 1.1 up into that point, that actually did work. The problem is, that's actually, now and [incomprehensible] so, returning a Promise. So now, what you've actually done is you've gone off, gotten the data, and then return the customer, the customer goes into ether because you didn't do anything with it, but instead, you actually set the Promise to the thing that you're binding on the screen. Have you ever done that, Ward? Because I know, I did a really...
WARD:
Well, back in 1.1, the binding library understood that you could either be binding to a customer or to a Promise that return the customer. It would be able to tell the difference. And if you return the Promise that would return a customer, then it would go down to different code path and attach itself to the .then and wait, and then when the customer arrive, it would then bind to the customer. It was like a nifty little sugar feature so the developer didn't have to understand Promises. That was great for the success case, but it wasn't too good if the call to the server failed.
JOHN:
Right.
WARD:
And so they stopped, they took that out somewhere along the line -- I don't know if it was 1.2 or somewhere along the line -- they stopped with the ability to bind to Promise.
JOHN:
And it's actually a good thing because as you started Angular after that, you probably never knew about this, which is great so...
WARD:
Yup.
JOHN:
You cannot listen to the last one minute of this show if you just gotten into Angular [laughs].
WARD:
But it's interesting because things like Firebase and stuff like that and a lot of data libraries, they assume that a developer really can't handle asynchrony so they try and do some variation of that. For example, you might have a controller that hands to one of these that says, "Go get customer," and you handed an object that the data library is suppose to populate when it comes back from the asynchronous call.
So, there are various things that try and follow that model where you actually returns, you give it some kind of container for the asynchronous thing to ultimately put something in and that's how it flows back. And then the developer can delay that awful day when they have to learn about asynchronous programming. However, that day will arrive -- the day will arrive when you must face the music and realize that you're programming asynchronously.
JOHN:
I'm going to make a completely one statement, I'm going to flip completely around. And that's, I believe firmly that Promises are not that difficult and the situation you just talked about. Meaning, any developer can pick these things up and after an hour playing with them really [incomprehensible] how the Promise works right at Angular. Now, here's where I flip, the place it gets more confusing in my mind is when you have a situation where -- I'm not saying this is the right situation, but let's just say you had a situation where you need to go get some data, wait for it to come back, then after you get that data, you need to make 2 simultaneous calls to go get other data at the same time. You don't want them to .chain right after; you want to do both in the parallel, and then when both of those return, then perform some action.
WARD:
Uhmm-hmm.
JOHN:
That is where I think people get turn off sometimes. It's that I have to make a couple of trips -- it may not be $http, but maybe it's also just animation is done, then do this, and then do that as well, but how do you run steps in parallel versus in series in Promises. I think it's the place where people get tripped up pretty easily. Don't you think, Ward?
WARD:
Yeah, I think that's the next level. That's next level of understanding Promises; it's how to .chain them sequentially and in parallel, which as you know, John, is actually easy to do once you learn the pattern, but it's the next step in the learning process.
JOHN:
I think it is, but I wouldn't try and get across, I guess, and may not do very well is I don't think it's an event scenario. Meaning, I have never written an app of more than a demo that hasn't had some case where I had to .chain more than one Promise together, and that's where I think, people can sometimes get tripped up. And what I see sometimes is they'll do the .then, then inside of the .then, they'll start using callbacks.
WARD:
Yeah.
JOHN:
They'rel like mixed callbacks with a Promise. And while that functionally works, it's kind of mixing apples and oranges.
WARD:
Yeah, and it's unpleasant. It is so simple to do a sequential .chain and a parallel .chain to Promises once you know the pattern, it's really so simple that it's worth learning.
JOHN:
I think, if I give anybody any tips, the thing I would do is when you write Promises, one of my favorite things to do is, let's say I have a function called gogetmycustomer data, in that function, I would say return $http.get(myURL).then().then().catch(), the things that are inside those .thens and the .catches, they would actually be a function name, a delegate if you will. I don't actually embed the functions. I know you can, you can embed anonymous functions inside of those .thens and .catches, but I don't do it and it's for 2 very simple reasons: one, if it's not a name function (it could've been better if you really wanted to), it's hard to debug stack traces; and then two, once you start embedding functions inside of those .thens and .catches, you end up with the same problem you have with callbacks (in my mind) in this pyramid of doom of how do you read that logic? So, it's really hard to read that code, whereas, if all my code says getmycustomer return $http.(URL)gogetmycustomers.then(callthis).then(callthat).catch(), I'm done. It becomes much simpler to read that kind of code.
WARD:
Totally agree. I am extremely layer up putting [incomprehensible] this off, and functions in the .then or .catch parameter. I've really liked the idea of having name functions because just mix it, as you say, John, easy to read and easy to debug. So, pro tip there.
CHUCK:
One thing that I'm wondering about, we mentioned $http, we keep mentioning $http, and we've also mentioned user input, are there other good places where we should be using Promises?
WARD:
Well, animation is also a place where you'll use it, I think, this built into the animation library. You don't have to wait for an animation to finish, any place you would want to do a time out, any place where you would want to do an alert box -- I don't mean literally the JavaScript alert -- but where you want to do, your chaining things together and you want to ask in the sequence of things whether somebody is, you ask the user, is this okay or not? Is it a good place to wrap that in a Promise? Because that's an asynchronous thing, too, if you think about it.
JOHN:
Yeah. And I think when you're using those, too, definitely check into the .all because that's a pretty powerful method right in itself. So, you can say $q.all if you want to make your own Promise because sometimes, you might want to call 3 things in parallel...
WARD:
Yup!
JOHN:
And then when all of them are done, because you don't know which is going to finish first, when all of them are done, the .all will actually call the .then.
LUKAS:
And then on the flip side is, if you're already having this data that you just want to resolve, then you can use that .then, safety.net because if it's a [inaudible]...
JOHN:
Yeah, it's a great point, Lukas. Because, otherwise, if you're setting up a deferred again and trying to resolve it blah, blah, blah, but if you just want to resolve and pass some data back in the Promise, $q.when, you just pass the data you want, it works great.
WARD:
That's particularly helpful. Let's suppose you have a service API, and from the controller's point of view, it's like gogetcustomer. But, behind the scenes in your data service, maybe you're caching that customer, just taking it, stalling it away somewhere.
So, the first time through, you'll look and say, "Do I have this customer in cache? No, I don't." Well, that's going to be an asynchronous call to the server and then you're going to return the value back. But, what if you do have it in cache? Then you're not going to call the server, and yet, the calling structure, the controller of the call to service called it in an asynchronous manner expecting a Promise back. That's the perfect opportunity to finish that customer out of the cache and return $q.when(customer), and now you have asynchronous behavior within the data service. But from the controller's perspective, they never had to know; they just had a completely asynchronous Promise-based interaction with the data service.
2:
the first one being the callback if the Promise succeed; and the second one being the callback if it fails. But you'll notice that we have always been saying use .then for the success path and then .chain on the .catch for the fail path. And, we do that for a reason...
JOHN:
And it also has the third parameter, doesn't it?
WARD:
It's the .notify one...
JOHN:
Yup.
WARD:
If the behind the scenes, if the thing you're talking to actually supports that kind of progress notification.
JOHN:
And I'll say it right now, when I see the multiple parameter .then, I don't like it. And maybe it's just a person thing, I just don't like it, I've been bitten by it, plus on top of that -- I think you've explained why you get bit by it -- top of that, it's hard to read which parameter is what.
WARD:
Yeah, it is. It is. I'm with you, John. I'm just more [incomprehensible] perspective. I prefer being explicit about which path I'm trying to solve with a single function parameter passed in.
However, there's actually a structural reason as well. Suppose I wanted to try to use the 2 parameter .then call. For the first one, it's the success handler, second one is the fail handler. If the success handler that I've written, itself has an exception, what's going to happen? Where's that going to go? It's not going to go to the fail handler because they're at the same left. So, if I have to throw an exception in there, bad things is going to happen and I won't necessarily be able to deal with it unless I have a downstream .catch.
So, it is often the case that we write our .catch logic to deal with anything that could go wrong, whether it's the failure of the remote server to give me with the data I wanted back, or I made some collosal mistake inside my success handler. They way to be sure that your fail path, your .catch path, can deal with both ways in which the request can go wrong, you want to have the failure path handled separately in a separate .catch.
CHUCK:
Got you.
JOHN:
Yup. That's a good point because if you do a .then and you do more logic and that thing crosses an error, you want to make sure that that's handled properly. So, it's really saving yourself. So why do people hate these things? Because I run and do all these all the time, and my job is dealing with this kind of code, and I have hundreds of developers I work with, literally. Why don't they hit these problems early? I'll tell you why: because we test inherently by nature -- I'd go back to testing, Ward -- we test the happy path. When everything works, you can pretty much code success.then put the 3 parameter .then on, whatever you want to do, it'll always great. But, if you don't test the failure cases, you're missing out because they're maybe in bugs in your code. And I think that's the take away I take out of this -- make sure you're paying attention to these tips because if you have an error and you're not testing the .catch case of a Promise, you want to make sure that that bubbles back the way you think it's going to bubble...
WARD:
And if you don't put the .catch in there and something goes wrong, you're sitting on the screen and wondering, "What the heck happened?" because nothing froze, nothing goes wrong, you see nothing, but the data didn't show and you don't know why. So, you just got to put the .catch in there and then, as John says, you want to make sure that in your test practice, whatever your test practice is, you'll look both at the happy path and at the unhappy path, the sad path.
CHUCK:
The sad path.
WARD:
The sad path.
JOHN:
Does that helped, Charles?
CHUCK:
Yeah! Oh, yeah. I'm looking forward to digging into some of this some more.
JOHN:
It's sounds simple, but there's always little new ones, aspects...
CHUCK:
Oh, of course.
JOHN:
I think the reason Ward and I have brought it more lately is because we hit just probably every bad way you could do to a Promise [laughs].
CHUCK:
I trust the two of you to find every bad way to do something.
JOHN:
[Chuckles]
WARD:
That's right. We can't seem to find the good way, but we have no trouble finding the bad way.
CHUCK:
[Laughs]
JOHN:
I had a great conversation with Dan Wahlin on Sunday, and Ward, we are Skyping back and forth each other doing some code, which is really fun code, SystemJS and Angular. It was funny that one point, Dan said to me, "You know," maybe I'm of training confidence here, but I hope he gets the joke, he said, "You know, if anybody saw all the silly things that we were just trying,[laughs] they would laugh at us and be like, "Oh, my gosh! These guys don't know what they're doing!" and it's so true because Ward and I, specifically in this case, we have literally tried and failed at many different ways of doing things, and you come out at the other end and learn from that. I've often said, it's really valuable to fail because if you don't fail at something, you're not going to know the right way to do it.
CHUCK:
Yup!
JOHN:
So, it's a learning experience. And doing Promises, you're not going to get it right the first time; you got to try and give it a go.
CHUCK:
Absolutely.
WARD:
I'm going to throw a bomb in here just as the show ends, "Nooo! [Incomprehensible]." This is new thing coming down -- you may have heard that Promises are passe. And at Angular 2, some of the time, you're going to hear about this because they're going to say, "No! Observables are the way to go." And I think we shoud do a little show on Observables because there's a point that they're making about how Observables are kind of a super set and a stronger way to approach things, asynch even Promises, we're clearly out of time to do it, but I just want to register that. And by the way, I love my Promises and they ain't going anywhere. But the Observables thing is the next step in the road towards talking about how to handle asynchronous stuff, then we should talk about that sometimes.
CHUCK:
Yeah, we should! We'll put it on the list.
JOHN:
We should also talk about how the wakey Ward has helped us some other languages of Promises.
WARD:
Ahhh... True enough.
CHUCK:
Alright, well, let's go ahead and do some picks. Ward, do you want to start this with picks?
WARD:
Sure! I am reading the book, "The Science of Interstellar", the movie. Did you guys seen Interstellar?
CHUCK:
Nope.
JOHN:
Yup, I saw it.
WARD:
Fantastic science movie. It's just a must-see. It's a mind-blowing thing to look at. Anyway, what you may not know is that that was based on the work by Kip Thorne, who's a fine man professor of theoretical physics at Caltech. It's driven entirely by a science-based movie in the sense that everything that happens in there, no matter how bizarre, is either really true from a Physics perspective or you could get a bunch of drunk physicists in the room and they would talk about it as if it might be true.
CHUCK:
[Laughs] I like that second definition [laughs].
WARD:
[Laughs] Alright. So, you know there could never be flat because that's just not true. But, warm holes, I watch in 1 hour at the time and a planet close to a black hole and it turns to 7 years back on earth, maybe, that could work. Anyway, the book, The Science of Interstellar writen by Kip Thorne is all about the science behind the movie, it's beautifully illustrated, it's written in plain English, there's lots of it that I still don't understand, but I haven't been able to understand quantum gravity anyway, but I had a fun time smack at my head against it, and maybe you will, too. So, go get the book.
CHUCK:
Awesome.
JOHN:
That was really deep, Ward. I hate following up Ward's picks because mine is not nearly as deep. I also saw a movie that would probably was on the same scale as Interstellar, it was "Ant Man". I loved it! I had very low expections going in beyond it, and I love Marvel, but I really loved it. It was a lot of fun and a lot of good jokes in there, there was a good plot, a lot of good side characters, I think they played it up really well. And of course, classic Marvel fashion, they had, not one, but this time they had 2 extra scenes in the credits at the end. My kids loved it, it's for all ages, from 6 to 16. We had a good time. I definitely recommend checking out Ant Man for a good fun movie to watch.
CHUCK:
I have to admit, I have been highly skeptical that's why I haven't seen it, so I have to go check it out.
JOHN:
Yeah. I saw it because it was one of those that kids wanted to see, but it wasn't like I was looking forward to it like it was Avengers and Captain America, but I really enjoyed it! It's different, but it's still a lot of fun.
CHUCK:
Cool! Lukas, do you have some picks for us?
LUKAS:
My pick of the week is the "You Don't Know JS Series" by Kyle Simpson. I actually just finished the scopes and closure books the other day. It's about 60 pages, very detailed, really good write up around JavaScript and some of the internal workings of JavaScript. So, being an Angular developer, it's very easy to just use the Angular mechanisms and pass some line details of JavaScript obscured, this is a really good tour into some of the underpinnings that framework developers don't always think about. So, I highly recommend it. Kyle is a very good author, I love all of his books.
CHUCK:
Alright. I'm going to jump in here with a couple of picks. As many of you may or may not know, I have a video series about how to do Ruby on Rails called Rails Clips. It was supported by KickStarter campaign and one of the levels was that I would do people a favor, a small favor like mentioning them on the show. So, one of our listeners, Daniel Egger, I'm sure that that's not how you say it because he's from Switzerland, he has an AngularJS course that he teaches out in Switzerland. So, if you are in Europe and you want an Angular course, then go check it out. I'm not going to try and read the domain because it's iterativ.ch/angularjskurs or however you say it in German or whatever. But I really appreciate him backen the campaign and I'm sure it's a terrific course, so go check it out.
The other thing that I'm going to mention here is "Angular Remote Conf". So far, we have 4 speakers that have committed to speak. Brad Green is going to talk, Lukas is going to talk, Kent Dodds who is on the show is also going to talk, and we got Jafar Husain who's also going to talk. We're working on lining that up. If you want to speak, we are opening up the call for proposals, and the tickets are not expensive. So, go check them out. I think I'm going to do half price on an early bird if you pick them up before August 25th. That's $100 ticket before August 25th, and then the conference is going to be September 24th-26th. I'm looking forward to it, it should be a lot of fun, and I look forward to seeing you all there!
Well, I'm looking forward to talking to you all next week! Take care and we'll see you all then!
[Hosting and bandwidth provided by The Blue Box Group. Check them out at bluebox.net]
[Bandwidth for this segment is provided by Cache Fly, the world’s fastest CDN. Deliver your content fast with Cache Fly. Visit cachefly.com to learn more.]
[Do you wanna have conversations with the Adventures in Angular crew and their guests? Do you wanna support the show? Now you can. Go to adventuresinangular.com/forum and sign up today!]