# Promises chaining Let's return to the problem mentioned in the chapter : - We have a sequence of asynchronous tasks to be done one after another. For instance, loading scripts. - How to code it well? Promises provide a couple of recipes to do that. [cut] In this chapter we cover promise chaining. It looks like this: ```js run new Promise(function(resolve, reject) { setTimeout(() => resolve(1), 1000); // (*) }).then(function(result) { // (**) alert(result); // 1 return result * 2; }).then(function(result) { // (***) alert(result); // 2 return result * 2; }).then(function(result) { alert(result); // 4 return result * 2; }); ``` The idea is that the result is passed through the chain of `.then` handlers. Here the flow is: 1. The initial promise resolves in 1 second `(*)`, 2. Then the `.this` handler is called `(**)`. 3. The value that it returns is passed to the next `.this` handler `(***)` 4. ...and so on. As the result is passed along the chain of handlers, we can see a sequence of `alert` calls: `1` -> `2` -> `4`. ![](promise-then-chain.png) The whole thing works, because a call to `promise.then` returns a promise, so that we can call next `.then` on it. When a handler returns a value, it becomes the result of that promise, so the next `.then` is called with it. To make things clear, here's the start of the chain: ```js run new Promise(function(resolve, reject) { setTimeout(() => resolve(1), 1000); }).then(function(result) { alert(result); // 1 return result * 2; // (1) }) // <-- (2) ``` The value returned by `.then` is a promise, so we can add another `.then` at `(2)`. When the value is returned in `(1)`, that promise becomes resolved, so the next handler triggers with it. Unlike the chaining, technically we can also add many `.then` to a single promise, like this: ```js run let promise = new Promise(function(resolve, reject) { setTimeout(() => resolve(1), 1000); }); promise.then(function(result) { alert(result); // 1 return result * 2; }); promise.then(function(result) { alert(result); // 1 return result * 2; }); promise.then(function(result) { alert(result); // 1 return result * 2; }); ``` ...But that's a totally different thing. Here's the picture (compare it with the chaining above): ![](promise-then-many.png) All `.then` on the same promise get the same result -- the result of that promise. So in the code above all `alert` show the same: `1`. There is no result-passing between them. In practice we rarely need multiple handlers for one promise. Chaining is used much more often. ## Returning promises Normally, a value returned by a `.then` handler is immediately passed to the next handler. But there's an exception. If the returned value is a promise, then the further execution is suspended until it settles. And then the result of that promise is given to the next `.then` handler. For instance: ```js run new Promise(function(resolve, reject) { setTimeout(() => resolve(1), 1000); }).then(function(result) { alert(result); // 1 *!* return new Promise((resolve, reject) => { // (*) setTimeout(() => resolve(result * 2), 1000); }); */!* }).then(function(result) { // (**) alert(result); // 2 return new Promise((resolve, reject) => { setTimeout(() => resolve(result * 2), 1000); }); }).then(function(result) { alert(result); // 4 }); ``` Here the first `.then` shows `1` returns `new Promise(…)` in the line `(*)`. After one second it resolves, and the result is passed on to handler of the second `.then` in the line `(**)`, that shows `2` and so on. So the output is again 1 -> 2 > 4, but now with 1 second delay between `alert` calls. Returning promises allows us to build chains of asynchronous actions. ## Example: loadScript Let's use this feature with `loadScript` to load scripts one by one, in sequence: ```js run loadScript("/article/promise-chaining/one.js") .then(function(script) { return loadScript("/article/promise-chaining/two.js"); }) .then(function(script) { return loadScript("/article/promise-chaining/three.js"); }) .then(function(script) { // use functions declared in scripts // to show that they indeed loaded one(); two(); three(); }); ``` Here each `loadScript` call returns a promise, and the next `.then` runs when it resolves. Then it initiates the loading of the next script. So scripts are loaded one after another. We can add more asynchronous actions to the chain. Please note that code is still "flat", it grows down, not to the right. There are no signs of "pyramid of doom". Please note that technically it is also possible to write `.then` directly after each promise, without returning them, like this: ```js run loadScript("/article/promise-chaining/one.js").then(function(script1) { loadScript("/article/promise-chaining/two.js").then(function(script2) { loadScript("/article/promise-chaining/three.js").then(function(script3) { // this function has access to variables script1, script2 and script3 (*) one(); two(); three(); }); }); }); ``` This code does the same: loads 3 scripts in sequence. But it "grows to the right". So we have the same problem as with callbacks. Use chaining (return promises from `.then`) to evade it. Sometimes it's ok to write `.then` directly, because the nested function has access to the outer scope `(*)`, but that's an exception rather than a rule. ## Bigger example: fetch In frontend programming promises are often used for network requests. So let's make an example of that. We'll use the [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) method to load the information about the user from the remote server. The method is quite complex, it has many optional parameters, but the basic usage is quite simple: ```js let promise = fetch(url); ``` Makes a network request to the `url` and returns a promise. The promise resolves with a `response` object when the remote server responds with headers, but before the full response is downloaded. To read the full response, we should call a method `response.text()`: it returns a promise that resolves with the full response text when it's downloaded from the remote server. The code below makes a request to `user.json` and then loads it as text from the server: ```js run fetch('/article/promise-chaining/user.json') // .then runs when the remote server responds .then(function(response) { // response.text() is the new promise that resolves when the server finishes sending data return response.text(); }) .then(function(text) { // ...and here's the content of the remote file alert(text); // {"name": "iliakan", isAdmin: true} }); ``` There is also a method `response.json()` that reads the remote data and parses it as JSON. In our case that's even more convenient. We'll also use arrow functions for brevity: ```js run fetch('/article/promise-chaining/user.json') .then(response => response.json()) .then(user => alert(user.name)); // iliakan ``` Now let's do something with it. For instance, we can make one more request to github, load the user profile and show the avatar: ```js run // 1. Make a request for user.json fetch('/article/promise-chaining/user.json') // Load it as json .then(response => response.json()) // Make a request to github .then(user => fetch(`https://api.github.com/users/${user.name}`)) // Load the response as json .then(response => response.json()) // Show the avatar image (githubUser.avatar_url) for 3 seconds (maybe animate it) .then(githubUser => { let img = document.createElement('img'); img.src = githubUser.avatar_url; img.className = "promise-avatar-example"; document.body.append(img); setTimeout(() => img.remove(), 3000); // (*) }); ``` The code works. But there's a potential problem in it. Look at the line `(*)`: how can we do something *after* the avatar is removed? For instance, we'd like to show a form for editing that user or something else. To make the chain extendable, we need to return a promise that resolves when the avatar finishes showing. Here's how: ```js run fetch('/article/promise-chaining/user.json') .then(response => response.json()) .then(user => fetch(`https://api.github.com/users/${user.name}`)) .then(response => response.json()) *!* .then(githubUser => new Promise(function(resolve, reject) { let img = document.createElement('img'); img.src = githubUser.avatar_url; img.className = "promise-avatar-example"; document.body.append(img); setTimeout(() => { img.remove(); resolve(githubUser); }, 3000); })) */!* // triggers after 3 seconds .then(githubUser => alert(`Finished showing ${githubUser.name}`)); ``` Now when `setTimeout` runs the function, it calls `resolve(githubUser)`, thus passing the control to the next `.then` in the chain and passing forward the user data. As a rule, an asynchronous action should always return a promise. That makes possible to plan actions after it. Even if we don't plan to extend the chain now, we may need it later. Finally, we can split the code into reusable functions: ```js run function loadJson(url) { return fetch(url) .then(response => response.json()); } function loadGithubUser(name) { return fetch(`https://api.github.com/users/${name}`) .then(response => response.json()); } function showAvatar(githubUser) { return new Promise(function(resolve, reject) { let img = document.createElement('img'); img.src = githubUser.avatar_url; img.className = "promise-avatar-example"; document.body.append(img); setTimeout(() => { img.remove(); resolve(githubUser); }, 3000); }); } loadJson('/article/promise-chaining/user.json') .then(user => loadGithubUser(user.name)) .then(showAvatar) .then(githubUser => alert(`Finished showing ${githubUser.name}`)); // ... ``` ## Error handling Asynchronous actions may sometimes fail: in case of an error the corresponding promises becomes rejected. For instance, `fetch` fails if the remote server is not available. We can use `.catch` to handle errors (rejections). Promise chaining is great at that aspect. When a promise rejects, the control jumps to the closest rejection handler down the chain. That's very convenient in practice. For instance, in the code below the URL is wrong (no such server) and `.catch` handles the error: ```js run *!* fetch('https://no-such-server.blabla') // rejects */!* .then(response => response.json()) .catch(err => alert(err)) // TypeError: failed to fetch (the text may vary) ``` Or, maybe, everything is all right with the server, but the response is not a valid JSON: ```js run fetch('/') // fetch works fine now, the server responds successfully *!* .then(response => response.json()) // rejects: the page is HTML, not a valid json */!* .catch(err => alert(err)) // SyntaxError: Unexpected token < in JSON at position 0 ``` In the example below we append `.catch` to handle all errors in the avatar-loading-and-showing chain: ```js run fetch('/article/promise-chaining/user.json') .then(response => response.json()) .then(user => fetch(`https://api.github.com/users/${user.name}`)) .then(response => response.json()) .then(githubUser => new Promise(function(resolve, reject) { let img = document.createElement('img'); img.src = githubUser.avatar_url; img.className = "promise-avatar-example"; document.body.append(img); setTimeout(() => { img.remove(); resolve(githubUser); }, 3000); })) .catch(error => alert(error.message)); ``` Here `.catch` doesn't trigger at all, because there are no errors. But if any of the promises above rejects, then it would execute. ## Implicit try..catch The code of the executor and promise handlers has an "invisible `try..catch`" around it. If an error happens, it gets caught and treated as a rejection. For instance, this code: ```js run new Promise(function(resolve, reject) { *!* throw new Error("Whoops!"); */!* }).catch(alert); // Error: Whoops! ``` ...Works the same way as this: ```js run new Promise(function(resolve, reject) { *!* reject(new Error("Whoops!")); */!* }).catch(alert); // Error: Whoops! ``` The "invisible `try..catch`" around the executor automatically catches the error and treats it as a rejection. That's so not only in the executor, but in handlers as well. If we `throw` inside `.then` handler, that means a rejected promise, so the control jumps to the nearest error handler. Here's an example: ```js run new Promise(function(resolve, reject) { resolve("ok"); }).then(function(result) { *!* throw new Error("Whoops!"); // rejects the promise */!* }).catch(alert); // Error: Whoops! ``` That's so not only for `throw`, but for any errors, including programming errors as well: ```js run new Promise(function(resolve, reject) { resolve("ok"); }).then(function(result) { *!* blabla(); // no such function */!* }).catch(alert); // ReferenceError: blabla is not defined ``` As a side effect, the final `.catch` not only catches explicit rejections, but also occasional errors in the handlers above. ## Rethrowing As we already noticed, `.catch` behaves like `try..catch`. We may have as many `.then` as we want, and then use a single `.catch` at the end to handle errors in all of them. In a regular `try..catch` we can analyze the error and maybe rethrow it if can't handle. The same thing is possible for promises. If we `throw` inside `.catch`, then the control goes to the next closest error handler. And if we handle the error and finish normally, then it continues to the closest successful `.then` handler. In the example below the `.catch` successfully handles the error: ```js run // the execution: catch -> then new Promise(function(resolve, reject) { throw new Error("Whoops!"); }).catch(function(error) { alert("The error is handled, continue normally"); }).then(() => alert("Next successful handler runs")); ``` Here the `.catch` block finishes normally. So the next successful handler is called. Or it could return something, that would be the same. ...And here the `.catch` block analyzes the error and throws it again: ```js run // the execution: catch -> catch -> then new Promise(function(resolve, reject) { throw new Error("Whoops!"); }).catch(function(error) { // (*) if (error instanceof URIError) { // handle it } else { alert("Can't handle such error"); *!* throw error; // throwing this or another error jumps to the next catch */!* } }).then(function() { /* never runs here */ }).catch(error => { // (**) alert(`The unknown error has occured: ${error}`); // don't return anything => execution goes the normal way }); ``` The handler `(*)` catches the error and just can't handle it, because it's not `URIError`, so it throws it again. Then the execution jumps to the next `.catch` down the chain `(**)`. ## Unhandled rejections ...But what if we forget to append an error handler to the end of the chain? Like here: ```js untrusted run refresh new Promise(function() { errorHappened(); // Error here (no such function) }); ``` Or here: ```js untrusted run refresh new Promise(function() { throw new Error("Whoops!"); }).then(function() { // ...something... }).then(function() { // ...something else... }).then(function() { // ...but no catch after it! }); ``` Technically, when an error happens, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is no such handler in the examples above. Usually that means that the code is bad. Indeed, how come that there's no error handling? Most JavaScript engines track such situations and generate a global error in that case. In the browser we can catch it using `window.addEventListener('unhandledrejection')` as specified in the [HTML standard](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections): ```js run window.addEventListener('unhandledrejection', function(event) { // the event object has two special properties: alert(event.promise); // [object Promise] - the promise that generated the error alert(event.reason); // Error: Whoops! - the unhandled error object }); new Promise(function() { throw new Error("Whoops!"); }).then(function() { // ...something... }).then(function() { // ...something else... }).then(function() { // ...but no catch after it! }); ``` Now if an error has occured, and there's no `.catch`, the event `unhandledrejection` triggers, and our handler can do something with the exception. Once again, such situation is usually a programming error. In non-browser environments like Node.JS there are other similar ways to track unhandled errors. ## Summary To summarize, `.then/catch(handler)` returns a new promise that changes depending on what handler does: 1. If it returns a value or finishes without a `return` (same as `return undefined`), then the new promise becomes resolved, and the closest resolve handler (the first argument of `.then`) is called with that value. 2. If it throws an error, then the new promise becomes rejected, and the closest rejection handler (second argument of `.then` or `.catch`) is called with it. 3. If it returns a promise, then JavaScript waits until it settles and then acts on its outcome the same way. The picture of how the promise returned by `.then/catch` changes: ![](promise-handler-variants.png) The smaller picture of how handlers are called: ![](promise-handler-variants-2.png) In the examples of error handling above the `.catch` was always the last in the chain. In practice though, not every promise chain has a `.catch`. Just like regular code is not always wrapped in `try..catch`. We should place `.catch` exactly in the places where we want to handle errors and know how to handle them. For errors that are outside of that scope we should have the `unhandledrejection` event handler. Such unknown errors are usually unrecoverable, so all we should do is to inform the user and probably report to our server about the incident.