So Promises already give us better code flow and flexibility. Further calls are ignored. The built-in function setTimeout uses callbacks. Create a promise-based alternative.
The function delay ms should return a promise. That promise should resolve after ms milliseconds, so that we can add. Please note that in this task resolve is called without arguments. Rewrite the showCircle function in the solution of the task Animated circle with callback so that it returns a promise instead of accepting a callback.
Take the solution of the task Animated circle with callback as the base. Open the solution in a sandbox. The tutorial was recently rewritten from scratch. There may be typos, please use the "Edit on Github" link in left sidebar in an article, at the bottom to propose fixes. EN We want to make this open-source project available for people all around the world. Please help us to translate the content of this tutorial to the language you know.
Choose section Tutorial Courses. This is a real-life analogy for things we often have in programming: For instance, the code loads a remote script. Many functions may need that result. In terms of our analogy: The constructor syntax for a promise object is: There can be only a single result or an error. All further calls of resolve and reject are ignored: Reject with Error objects. We also can call resolve or reject immediately, like this: The state and result are internal.
On settled promises then runs immediately. Otherwise, if a promise has already settled, they execute immediately: But it only looks into that queue when the current execution is finished.
Thankfully this is usually what you want, or at least gives you access to what you want. Also, be aware that jQuery doesn't follow the convention of passing Error objects into rejections. We'll want to stop the spinner at that point too, else it'll keep on spinning, get dizzy, and crash into some other UI. Multiple data fetches, then do something when it's all done. Old APIs will be updated to use promises, if it's possible in a backwards compatible way.
The response is JSON, but we're currently receiving it as plain text. We could alter our get function to use the JSON responseType , but we could also solve it in promises land:. When you return something from a then callback, it's a bit magic. If you return a value, the next then is called with that value. Here we make an async request to story. This is when promises really start to stand out from simple callback patterns. We don't download story. As we saw earlier, then takes two arguments, one for success, one for failure or fulfill and reject, in promises-speak:.
There's nothing special about catch , it's just sugar for then undefined, func , but it's more readable. Note that the two code examples above do not behave the same, the latter is equivalent to:. The difference is subtle, but extremely useful. Promise rejections skip forward to the next then with a rejection callback or catch , since it's equivalent. With then func1, func2 , func1 or func2 will be called, never both. But with then func1.
Here's the above as a flowchart because I love flowcharts:.
Promises for asynchronous programming
Rejections happen when a promise is explicitly rejected, but also implicitly if an error is thrown in the constructor callback:. This means it's useful to do all your promise-related work inside the promise constructor callback, so errors are automatically caught and become rejections. Instead it moves onto the catch callback. As a result, "Failed to show chapter" will be added to the page if any of the previous actions failed. The above becomes a non-blocking async version of:. You may want to catch simply for logging purposes, without recovering from the error.
To do this, just rethrow the error.
We could do this in our getJSON method:. Thinking async isn't easy. If you're struggling to get off the mark, try writing the code as if it were synchronous. That works see code! But it's sync and locks up the browser while things download. To make this work async we use then to make things happen one after another. But how can we loop through the chapter urls and fetch them in order? This isn't Pulp Fiction, so let's fix it. We want to turn our chapterUrls array into a sequence of promises. We can do that using then:. This is the first time we've seen Promise.
If you pass it an instance of Promise it'll simply return it note: If you pass in any other value, e. If you call it with no value, as above, it fulfills with "undefined". We can tidy up the above code using array. This is doing the same as the previous example, but doesn't need the separate "sequence" variable. Our reduce callback is called for each item in the array. And there we have it see code , a fully async version of the sync version.
But we can do better.
Events aren't always the best way
At the moment our page is downloading like this:. Browsers are pretty good at downloading multiple things at once, so we're losing performance by downloading chapters one after the other. What we want to do is download them all at the same time, then process them when they've all arrived.
Thankfully there's an API for this:. You get an array of results whatever the promises fulfilled to in the same order as the promises you passed in.
Depending on connection, this can be seconds faster than loading one-by-one see code , and it's less code than our first try. The chapters can download in whatever order, but they appear on screen in the right order. However, we can still improve perceived performance. When chapter one arrives we should add it to the page.
This lets the user start reading before the rest of the chapters have arrived. When chapter three arrives, we wouldn't add it to the page because the user may not realize chapter two is missing. When chapter two arrives, we can add chapters two and three, etc etc. To do this, we fetch JSON for all our chapters at the same time, then create a sequence to add them to the document:. And there we go see code , the best of both! It takes the same amount of time to deliver all the content, but the user gets the first bit of content sooner.