Marius Schulz: 0:00 Here we have the code from the previous lesson again. The only change I've made is that I've replace the commas by new lines, just so we can read our statistics a little more easily. If I go to the browser and refresh this page, we can see that indeed, we are fetching the counts for films, planets, and species.
0:17 Let's now make one of our request fail and see what happens. I'm going to go over here and I'm going to access an endpoint that doesn't exist. Let's head back to the browser, open the Chrome dev tools, we'll go to the network tab and we're going to give this page a refresh.
0:35 You can see that the request to the movies endpoint failed with a status code of 404, while the other two requests succeeded. Because the request to the movies endpoint failed, this first promise ends up being rejected. As a result of that, the promise return from Promise.all ends up being rejected as well.
0:53 We're not executing this fulfillment handler. Instead, we are executing the rejection handler. You can see the error message printed to the screen.
1:25 First up, I'm going to take a look at Promise.all again. Promise.all accepts a bunch of promises. In this case, I'm going to pass a promise that has been resolved with a value 42, and I'm going to pass another promise that has been rejected with an error. We can see that the promise return by Promise.all has been rejected.
1:47 The return promise is only fulfilled if all of these promises are fulfilled. If any of them is rejected, then the return promise is rejected as well. Let's contrast this with Promise.allSettled now.
2:04 Interesting. The promise returned by the allSettled method has been fulfilled, not rejected. Let's take a closer look. The Promise.allSettled method accepts a bunch of input promises and it returns another promise. Let's call that the "settlement promise." The settlement promise is fulfilled once all of the input promises have been settled.
2:25 That is, once they are no longer in the pending state. It doesn't matter if the input promises have been fulfilled or rejected, it just matters that they have been settled. The value of the settlement promise is an array of settlement objects, one for every input promise. In our case, it's an array of length two because we have passed two input promises. Each settlement object has a status property and it describes how the corresponding promise has been settled.
2:56 In our case, the first promise is fulfilled with a value of 42. Our settlement object has status fulfilled and a value of 42. Our second input promise is rejected with an error. Therefore, the corresponding settlement object has status rejected and it has the error as its rejection reason.
3:16 Using the status property, we can tell apart which promises have been fulfilled versus which ones have been rejected. Notice that the promise returned by Promise.allSettled is always fulfilled.
3:30 The only exception to that is if you pass a value to Promise.allSettled that cannot be iterated over. If you're not passing an array or some other iterable, then you'll get back a rejected promise. Otherwise, the promise will always be fulfilled.
3:46 Let's now go back to our code and replace Promise.all by Promise.allSettled. Within our fulfillment handler, I'm going to add a bunch of console.log statements so that we can see which values we have for films, planets, and species. All right. Let's give this a refresh.
4:07 In the browser console, we can see our three settlement objects. The first promise was rejected. Therefore, the first settlement object has a status of rejected. The reason property contains the corresponding error. The other two settlement objects have a status of fulfilled. They've been fulfilled with our planets and with our species.
4:32 Now, you can see that the UI is currently clearly broken. We're seeing undefined films, undefined planets and undefined species. This is because we're accessing the length property on films, planets, and species. However, the only properties available are status and reason or status and value, depending on the settlement of the promise.
4:53 Also, we only want to show the statistics for successful responses, for promises that have been fulfilled. Let's go ahead and refactor our code.
5:04 Initially, we don't know how many successful responses we're going to have. I'm going to collect our statistics in an array. At the end, I want to join together all of our statistics and write them to the inner text of our output div. We're going to take all statistics and join them with a new line.
5:23 Now we have to take a look at all three responses individually. If the status of the settlement is fulfilled, then we want to add a statistic to our array.
5:36 Notice that our films themselves are stored in the value property. This is why we're accessing films.value.length. We're going to do the same thing for planets and species. Let's see if this code is working. Our films couldn't be loaded because the movie's endpoint doesn't exist. However, we could load the data from the planets and species endpoints. We're displaying 60 planets and 37 species.
6:09 Now, let's test the happy path. We'll make the film's endpoint work. Indeed, now we're seeing three statistics and no more errors. We have built a UI that is resilient against occasional failures. We could stop here. However, there's a few things I would like to clean up first.
6:29 I don't really like the repetition that we have in our fulfillment handler. If we add another statistic in the future, that will lead to more copy and paste. I want to get rid of that. Here's how we could do that. We can tuck on a fulfillment handler to every promise that we're passing to the allSettled method.
6:46 In here, we can transform the result that we're getting back. In here we could say f.length films. Let's do the same thing with planets and species as well. Let's rename this to p for planets. Do that one more time, this time with species. We can now build our statistics in a generic way. Instead of this destructuring pattern, we're going to use a single parameter called results.
7:20 For our statistics, we're going to read the value of all fulfilled settlement objects. Let's make sure our code still works. Indeed, that's looking good. It's now really easy for us to add more statistics. Let's load the vehicles in the "Star Wars" universe as well. We're going to fetch them from the vehicles endpoint, rename this to v, and change this to vehicles. Another refresh and there we go, 39 vehicles.
7:57 There is one last thing I want to tweak. What happens when all of the requests fail? I'm simulating that here, but accessing endpoints that don't exist. In that case, we're simply going to show an empty list of statistics. Instead of showing nothing, we should probably display an error message.
8:17 Let's add a check for an empty statistics array. Let's see if that works. OK, cool. We're seeing the error message. If we restore all of these, reload one last time, and there we go, 6 films, 60 planets, 37 species, and 39 vehicles.