- Overview
- Transcript
6.5 Deferreds
Deferreds are a relatively new addition (as of version 1.5) to jQuery that allow us to register multiple callbacks for any number of asynchronous actions. I'll show you why they're important, and how to use them in your projects today!
1.Introduction1 lesson, 00:38
1.1Welcome00:38
2.The Basics7 lessons, 1:33:04
2.1Hello jQuery10:52
2.2Not So Fast, jQuery06:54
2.3The Basics of Querying the DOM15:21
2.4Events 10119:58
2.5Events 20115:21
2.6Bind...Live...Delegate...Huh?10:49
2.7Creating and Appending Content13:49
3.Effects9 lessons, 2:39:59
3.1Slides and Structure23:46
3.2The this Keyword09:35
3.3Modifying Effect Speeds05:47
3.4Creating Custom Effect Methods07:16
3.5Full Control With animate21:34
3.6Homework Solutions12:15
3.7The Obligatory Slider (First Attempt)32:50
3.8Prototypal Inheritance and Refactoring the Slider34:03
3.9Your Questions Answered12:53
4.Utilities4 lessons, 59:31
4.1$.each and Templating12:49
4.2Say Hello to Handlebars15:09
4.3The Twitter API22:11
4.4Filtering with jQuery.grep09:22
5.Custom Events1 lesson, 25:17
5.1Custom Events and the Observer Pattern25:17
6.AJAX5 lessons, 1:33:13
6.1Loading Pages Asynchronously11:10
6.2Interacting with the Server-Side11:24
6.3PHP and jQuery: Part 122:37
6.4PHP and jQuery: Part 228:30
6.5Deferreds19:32
7.Plugin Development1 lesson, 45:57
7.1Head First Into Plugin Development45:57
8.Exit1 lesson, 01:06
8.1Goodbye01:06
6.5 Deferreds
[SOUND] Welcome back to 30 days to learn jQuery. And in today we're going to be focusing on jQuery deferreds. And this is more of and advanced topic but hopefully I can break it down so it's easy for everyone to understand. So firstly, if you'd like to learn a bit before you view this screencast you can check out api.jquery.com. And take a look at all of the various deferred methods that are available. Now we won't be taking a look at all of these but we will review a good handful of them. So to be begin, let's figure out what the dilemma is and this is something that we've touched on a bit in this course. So let's say we have a time out. And this time out is going to execute in two seconds. Now what we're going to do here is, let's just create a global variable. We'll call it myVar, and that will be undefined for the time being. Okay so, after two seconds though, we're going to do some kind of operation. This two seconds is going to simulate an Ajax request, anything like that. But when that's complete, we are going to set myVar equal to my value. And then at that point we will log it to the console. Okay, let's view this in the browser. Load the page, wait a couple seconds, and in the bottom you'll see my value has been logged. So now at this point the dilemma is we can no longer access the value of myVar without running timeouts or figuring out, maybe, some kind of pub-sub model. So if I change this to myVar in the timeout. And then below this, we try to log myVar one more time. We get undefined. And then in two seconds, once that value has been assigned to the variable, we have that available. So especially when you're learning, the dilemma is how do I access the value of myVar when I'm not actually setting its value until some point in the future. And again, this will be an Ajax request. You don't really know. It could be a hundred milliseconds. It could be two. But you wanna continue on with that value. So what most generally do at first is from that point on, they just continue writing their code inside of this callback function. And then they'll do something else, and they have another callback function. And you end up with all of this nested code because you're trying to make it so that certain bits of code don't execute until a callback or an AJAX operation has completed. So we've reviewed a couple solutions to this in the jQuery course. We looked over pub-sub. And pub-sub is definitely a way to solve this. Within here, we could publish some kind of announcement. And then outside of that code, we can subscribe to it. So if that's confusing to you, view the pub-sub lesson in this course. But deferreds are a much nicer way to do this. So why don't we try this deferred method out. So we'll create a function and, this is mostly pseudocode, so we'll call it setVal, that will be the name of our function. And within here, we will create a new deferred. So we'll call it deferred, equals a new jQuery.deferred, the new keyword. You can either leave that on, some people think its much more descriptive its easier to wrap their mind of or creating a new deferred, but you don't have to. If you leave it off, they will take care of it for you, so its not really essential. Now we run our time out, so I will copy all of this and paste it in like so, do a little bit of cleanup, there we go. So we set a time out. In two seconds, we set the value of myVar. And again that would be some kind of Ajax request that will go over shortly. And then we need to announce that we are finished. So we had this deferred. Now we are going to resolve it. So we will say deferred.resolve, and there we go. So we've created a function setVal. Within this function, we created new deferred. We set our timeout. We do whatever it is we need to do, and when we're done, we're going to resolve the deferred. And then finally, we need to return the deferred. But what we're going to do instead is we're going to return a limited version of the deferred. So we'll do deferred.promise. Now, promises are confusing until you get it. Think of this as this function's way of saying, I promise to let you know when the operation is complete. So it's saying, I promise to let setVal know when this is resolved. And by default, it's in a state of pending. Its pending, pending, pending, and at some point we are complete, so it resolves and then at that point any callbacks that are attached will fire, so let's try it out now. We will run setVal, and now we need a way to say and once that is done then do this. So let's try a couple different ones. We'll say once that is done, there's a keyword. We will execute a function and we will say all done. Then we will log the value of myVar. So let's try this out. I'll get rid of that old console and check it out in Chrome. Reload the page, give it a couple seconds, we get all done, and then we get my value. So let's add one space there and try it one more time. Reload the page, it waits two seconds, and then we get the value. So think of deferred as a way to use multiple callbacks. So for example, maybe later in your code you need to be able to do something again with this value. Maybe you have a handful of modules and each module needs to listen for when this particular deferred has been resolved, and we have access to the value. Well then later you could use done again and you can stack these callbacks. Even after the deferred has already been resolved, you can still attach these callbacks to fire, and if it has result, it will just fire instantly. So this is a really nice way to specify when this function has run and it's been resolved, meaning it's complete, then do this. Now what if we want to listen for when it fails. Okay, well once again you may do an ajax request, maybe nothing is returned, and it has failed. Well, in that case, you would use, rather than resolve, you would use either a reference to reject, or you would call it like this, reject. Now the done method will never fire, done will only fire if deferred is in a state of resolved. And if you ever need to check that state, you can use deferred.state as a function. So now if we reload the page, I'll wait a couple seconds, but you're not going to see anything down here because the done callback never fired. So with that in mind, how do we fire a function when it fails? Well we instead use the fail method, done, fail and always. One more time. It'll wait a couple seconds, and then because it failed, we execute the callback that was attached. Now what if we need to do something where a function will fire regardless of it. We want it to run weather it fails or weather it succeeds. Well in that case you use the always method. And that means I don't really care weather it passed or failed, I, you wanna run this function on either condition. I'll reload the page, in two seconds it's going to fire. But if we resolve it as well, try it one more time, it's still going to fire. Now what if we want to be able to chain these so we can say if it's resolved run this function, but if it is rejected run that function. Well we can do it in two ways, I'll show you the manual way, and then I'll show you jQuery's helper way that we can do it instead. So we can say setVal.done, then we're going to log console.log all done, and then we're going to chain this. So add another one and we'll say .fail, then we run a function failed. So let's try this one out. Load the page, give it a couple seconds, and we get all done. But if somehow, maybe we have an if statement. If this, then we're going to resolve it. But if we did not get this, then we're going to reject it. Okay, let's try it now. Reload, give it just a couple seconds, and now we get failed. So this is how we can chain. If it's done successfully, then do this. If it fails, then do that. Well jQuery makes this a little bit easier by giving us the VIN method. Let's take a look at the jQuery source. I'm going to search for deferred just a couple times. Here we go, and this is where we are creating the deferred method. We're extending jQuery core and we're adding this new deferred. And what I want you to take a look at is within the promise object right here. We have this VIN method. And take a look at what it's doing. It's simply running the deferred and it's executing its done callback. And then it's executing its fail callback. And that's pretty much what we're doing right here. So that means we can replace this with then. And then we'll accept the success callback as its first parameter, and its fail callback as its second parameter. So we have then, then do this. Then we'll do a comma, and do the second parameter. So let's make this a little bit cleaner. There we are, that's a little more readable. So setVal.then, if we pass one parameter, it will fire when the deferred resolves successfully. If we pass two, this represents when it's resolved. The second one represents when it fails. So one more time, we run it. We get failed, but if we change this to resolve, the first parameter will fire. One, two. And then it fires, and we get all done. Cool. So now that we have a basic understanding of deferreds lets take a look at maybe a little bit more practical example. We're going to create a wrapper. We've been working with Twitter a lot, and the reason why is because its API is so simple to work with. We can do it in one line of code, and we don't have to worry about API keys if we just wanna search the timeline. So let's see how we can do this. We are going to create a method and we'll just stack it on to jQuery, and we'll call it searchTwitter, and that'll be a function. And this methods job is to query the Twitter API. So we can run this. We already know how to do this from the last few lessons, the URL will be search.twitter.com/search.json. Now this time, we're going to do it a little bit differently. Before, to make it as easy as possible to understand, we were adding the query directly to the URL. So we were doing query equals dogs, and the callback so we could have a JSON p callback equals question mark like that. And that's fine, but we can clean this up. Let's remove it all this time and we're going to change it. And now we're going to pass that query through as data. And we can say the query is going to be equal to, and we'll hard code it in for now, dogs. Next we need to specify. Rather than doing callback equals question mark, we can say let's set the data type to jsonp, and then jQuery will know how to deal with that. Now, for now, we're going to use that success callback that we've learned about. And we'll say console.log results, and that will accept the results. Alright, let's run this, we will fire the method, and see what we get. Reload the page. It's query the Twitter api, we have the results. And we can see all of these items where the tweet references, dogs. Cool, so that's dead simple. But, what if we want to pass in the value that we're searching for. All right, well let's accept the search, and we'll change dogs from being hard coded to the value that they user passes in, and now we're going to search for cats. One more time. We reload the page, we get the object back, we get the results. We look at the first one, and now we're looking for things that reference cats. And we can see that it does. But the problem with this older model is that all of our logic now has to be placed within this success callback. And what if maybe other modules in our project need to do something with that data. Well here, we're really limiting it to this one method. And then you'd have to use maybe pub-sub or some other technique to alert those other pieces of your code or the modules that this has completed. So let's instead use a deferred again. So I'm going to remove this success entirely. We will create a new deferred. This time we will call it dfd, this is fairly common for a new jQuery.Deferred. Once again, new is optional. You don't have to include it. So now we're going to file this ajax request. We're going to get everything that references cats, and on success, we're simply going to resolve the deferred, dfd.resolve. But this time we're not firing it instantly because we don't want it to resolve immediately. We only want it to resolve when the success method fires. So let's remove that like so. Finally, we're going to promise this searchTwitter method that we will fire those callbacks when the deferred has been resolved, return dfd.promise. So now if we load the page and I'll refresh. We're not going to get anything below, and we shouldn't. All we've done right now is set up a deferred, and we have resolved it, but we haven't done anything. So now let's say search.Twitter. And then [UNKNOWN] execute this function. The function will automatically be passed any arguments. So we can accept the results, and then we will lock those to the console. One more time load the page and we get those results. But now we're not stuck into this ajax method and we don't have all of this indented code and we can fire multiple callbacks. And what I mean by that is lets say we call this outer and that's going to be equal to jQuery.searchTwitter for cats. Now we can take this and say outer.then, then fire the results. Let's make sure that works. It does. And then later in the code we can do it again. So later, maybe in a different module, something like that. We can say outer.then and console.log something else with the results. One more time and you'll see that both of those have been registered. So when the deferred has been resolved, both of them fire, and they both can gain access to the results of that Ajax request. Pretty neat. Now the same thing, if you want to do error, you could do dfd.reject, and that way if there was an error, you can pass the second parameter to then. Or you can run .fail, and then you can execute some code in that case, maybe alerting the user that no results were returned. Now in that case though, we can clean some things up. By default with jQuery, its ajax methods will automatically return a promise. And what that means is, if we're already doing ajax and we know that returns a promise, why don't we get rid of this right up here, like so, and we will return that, so we will return ajax.promise. And now rather then success and error we can remove that as well and we clean up our code quite a bit. Reload the page and we still get those two objects and that's because the ajax methods including .get, getjson post. Those are already rigged to use deferred, so we don't have to repeat all of that work. So what I want you to note is this time we're running the AJAX method, but we're not bothering with the success and error methods. We are returning the promise, so within searchTwitter we have access to it within this outer variable. And now we can use outer.done or fail, or always, or we can use the helpers like outer.then. So now that we know that these AJAX methods are already set up for working with deferreds, you might be wondering, well when would I ever create a new deferred? And there's lots of different situations when you would. Let's comment all of this out right here and we will start from scratch at the top. And now we're going to take a look at using deferreds with animations. So let's create a couple boxes. So let's create a div with a class of box, my box, and let's just create three of them. Now we'll style them. Give them a width and height of 200 pixels. A background color of green and set their display to inline block, and give them some margin on the right. Let's see how that looks. Now we have three boxes, and what we want to do is incrementally fade each one out. And ultimately what we want to do is fire a callback once all of those animations have been completed. So let's take our first stab at this. We will grab the elements with a class of box but we're going to limit this to the divs, and we'll get a little performance improvement there. And let's just try fading out maybe over a second. What's gonna happen there? Well they're all going to fade out, so we want them to fade out sequentially. Okay, well how could we do that? Well instead, let's change this to each and iterate over them. And now we're going to get the element. And we're going to use a common trick where we delay. And we can say delay for one second times the index. So the index for the first box will be zero, then one, then two. So we're going to say delay for 1,000 times i and then increment that. So that way the first one will delay for 1,000 times zero. The next one will delay for 1,000 times one, then 1,000 times two. Then fade out over a second. Let's try that out. Reload the page, it fades out, it fades out, it fades out. But now we wanna figure out how can we run an operation when all of those have completed. Well we can try it here and let's see, console.log done. Will that work? Nope, we're getting done immediately. Well what if we're doing it outside, will that work? One more time, nope that's running immediately. So it doesn't seem like there's any real way to figure this out and we can by using promises. So check this out, we can say get the div with the class of box, delay them, fade them out and then we can say promise to let me know when your done. And when you are done, then execute this function, all animations have fired. Let's try it in the browser. Reload. One, two, three, and then notice at the very end, all animations have fired. So don't let that promise key word confuse you. Just think to yourself, it's a promise to let you know when they have completed. So when all of the animations related to the div have completed, then execute this. And from that angle, it is very readable. Promise to let me know when you're done. When you are done, run this function. Now, we can also use another method, called jquery.when, and this is where we can stack multiple asynchronous operations. So for example, we can say when all three of these Ajax requests have completed and we have the results, only then do I want to proceed. Up until now we've just been saying, when this one is done, then do this. But now we want to say, when all of these are done, then do this, and jquery.when is the correct choice for that. So let's create a function here. And we'll say, getTweets, since we've been working with Twitter. And once again, we're going to return a request to the Twitter API, URL. The data we'll send through will be the query, will be equal to whatever the user passes in. And we're going to be working with jsonp again, so we'll set that to jsonp. So when this function is called it's going to fire a request to the Twitter search API. It's going to pass in a query and it's going to return those results. All right let's try this. We will say when getTweets, we'll look for dogs. And then this could be another AJAX request, or we're just gonna call the same thing and look for cats. Now we can say when both of those are done, then run a function. And now we can have the results from each using results1, results2, whatever you wanna call it. We'll duplicate that, and replace it. Let's try this out now. I'll load the page, and notice now we get both of those results. Let's see, the first one, we have references to, if we scroll down, dogs. And let's go down to the second one. And now with here, we have results for cats. So isn't that really amazing? We can specify only when all of these asynchronous operations have completed. Only on that condition do I want you to fire the results, and the done method will accept in order that they were referenced any results that are available. Alright and that's going to do it for today. Deferreds are definitely something that's a little difficult to wrap your mind around of, but once you do it clicks its like anything else. Once you get it you know it forever. So let me know if your still really confused about this you can go to tutshplus.com/forums. Or again, you can tweet me at jeffrey_way if you need any clarification or more examples. I'll see you later. Buh-bye.