14. Asynchronous Code in JS Öffentlich

14. Asynchronous Code in JS

Kostas Diakogiannis
Kurs von Kostas Diakogiannis, aktualisiert more than 1 year ago Beitragende

Beschreibung

Working with asynchronous operations in JS

Modulinformationen

Keine Merkmale angegeben

Kontext

Course's Objectives 1. Participants learn the difference between synchronous and asynchronous code 2. Participants understand how the event loop works, that determines the order of execution for asynchronous operations. 3. Participants understand what callback functions are. 4. Participants realize how to use nested callbacks to chain asynchronous operations. 5. Participants recognize what callback hell is and why it's a bad pattern. 6. Participants realize what promises are, their states, and why they are used. 7. Participants learn how to chain promises together by using the .then() function. 8. Participants learn how to catch promise errors and do something with them. 9. Participants learn what AJAX calls are and why are they so important?  10. Participants learn how to perform AJAX calls through the fetch API. 11. Participants learn how to use the 'async / await' pattern and what kind of syntax benefits it offers.
Weniger sehen
Keine Merkmale angegeben

Kontext

Try-catch blocks Try...catch blocks have nothing to do with asynchronous code. They are blocking operations, thus their code that runs inside, run in the same sequential fashion we have been learning so far. However, is important to know how they work and what do they provide to us for catching any type of errors and handling them manually before we reach the promises section that does the same in a bit of different way. Take a close look at the code in the picture above, if you ignore the weird syntax and these try...catch... finally keywords that we know nothing of them for now, you normally expect that the code in line 17 will never be executed, because there is no array with the name of names as used in line 4. This will produce an uncaught reference error normally that will crash our code and prevent it from being executed. Right? Well...not exactly. In this case putting this 'dangerous' piece of code inside a try block, we explicitly ask from the browser to try to perform this operation. If this is not possible and an unexpected error occurs, then execute whatever we have defined inside the catch block that follows, instead of crashing everything. Thus, you can catch errors that could go wrong without preventing anything from being executed afterwards. The finally block, is a block of code that will be executed regardless of an error or not. (either way). This block is an optional block. It is not mandatory to have it. But try and catch blocks are connected. Learn more about try catch error messages here!
Weniger sehen
Keine Merkmale angegeben

Kontext

Synchronous vs Asynchronous Code If you take a look at the code above you probably have no problem to identify what is going on. At the beginning we set a variable that depicts the name of the  current ruler of Westeros. This is happening now. Now is Cersei Lannister and her name is printed in line 44.  Then we created a function that accepts a name, and when called takes the names and sets it as the new ruler of westeros. Thus after line 53, the ruler changes and the new ruler is 'Danny'.  But it didn't last long because night comes always at the end of the day. Thus after line 57 the new ruler is the night's King! Now regardless if you are a big game of thrones fan or not, this code is synchronous. It will run sequentially one after the other. Every line must perform it's task and be executed before the next one starts. This kind of operations are called blocking operations or synchronous code. But what happens if a specific operation takes more time to execute? What if i want to define NOW, who the ruler is going to be after 50 years? Do i define it now, and then wait for 50 years to proceed to the next line? What if  i want to send my credentials to a server and want to get a response back with my personal homepage? This operation could take more than 2-3 seconds! Do i have to wait during this time paused and doing nothing? That doesn't look very good in terms of performance, let user's experience apart!
Weniger sehen
Keine Merkmale angegeben

Mindmap

Mind map overview about asynchronous code and asynchronous requests. How does the non-blocking execution works?

Kontext

Asynchronous Code For such a reason we have asynchronous code. Asynchronous operations, are non-blocking operations. That means we can define them to execute on specific events, or after a specific amount of time and in the meantime either the user or the server can do other things. For example, you can ask for some specific piece of data from a server or from an external resource (another service on internet) through a click, this operation could take more than anticipated. Asynchronous code gives us the capability, not to block, pause or freeze the whole website until the data comes back. That means that in the meantime the user can still interact with whatever he has in front of his screen. Code that we have defined below the async operator can be immediately executed. But if this is the case, then when will exactly my async operation be executed?
Weniger sehen
Keine Merkmale angegeben

Mindmap

Mind map overview about asynchronous code and asynchronous requests. How does the non-blocking execution works?

Kontext

The Event loop In order to answer such a question we need to understand the order of execution of asynchronous operations. This is pretty straightforward in a synchronous world. Everything is executed serially and line by line. When an operation is ready, the next operation takes place and executes itself until we have no more code to execute. This is not possible with async operations though, because of their nature passing the torch to the next function, and because most of the times we don't know exactly how much time the operation will take to finish. To be honest, this 'problem' is the biggest benefit of those operations. How do we know then?  Well the answer is simple. Any time we define an async function, the browser (or the server, depending on what kind of script we write) performs this operation (let's say send data to server from a client), but in the meantime 'passes the torch' to the next executable piece of code, the next line! Thus the rest of your code executes itself normally as we know.  When the response from the server comes back, the browser gets back to the asynchronous function definition and executes the function that has to be done. This is happening after all the rest of our code has already finished!  That's good. But what happens in case there is still code being executed and the response from the server comes back? What is the order of precedence in this case? The event loop is clear again. All the asynchronous operations that are on 'pending state' will be  executed last and after the whole code stack is cleared. That means even if the response has come back from the server, in order to do something with it, we need to wait until the browser has no other task to do at the time. Highly recommended: If you want to know exactly how the event loop works: Watch here:
Weniger sehen
Keine Merkmale angegeben

Kontext

Callback functions Let's take a look and try to write some asynchronous operations with our latest game of thrones example.  Some of the commonest async operations that are provided by the browser is the setTimeout and the setInterval function. We will use the latest GoT example to change the ruler of westeros asynchronously after 5 seconds (yes the battle didn't last long!) from 'Cersei' to 'Danny'.  This is exactly what we did on the code above, but it doesn't seem to work. Why? Well, because as we explained in the event loop, we may call the function in line 38, but this function contains an asynchronous operation, the setTimeout function. The setTimeout has a callback function inside of it. This callback is the piece of code that will be executed after 5 seconds. But UNTIL then, the ruler will remain 'Cersei Lannister'! And because setTimeout is asynchronous function passes the torch to the next piece of code in this case line 39. That prints the ruler. But the ruler now is Cersei! It will be danny AFTER 5 seconds and only if there is no other code left that the browser is dealing with! So in order to print the danny message after 5 seconds we need to do it inside the callback function! Don't forget this callback contains the code that will be executed after 5 secs!
Weniger sehen
Keine Merkmale angegeben

Kontext

Nested Callbacks Now we are getting closer to achieve the same effect as before, but without blocking our code from further execution. What we did is that we added the 'Nightking' as the ruler of westeros ('Night finds them all' after all), right 2 seconds after danny's reign. As you probably guessed we used another timeout function right after danny's sit on the throne. This is what we call a nested callback, and they are very common when we want to perform an asynchronous operation right after another has finished.  For example i make a request to youtube server to bring me the video with the title i wanted. Youtube parses this request and finds the video. In the meantime i can do other things on my screen, when the video is ready and back, automatically another async operation can be made sequentially (like bringing on the aside section of the screen relevant videos that were uploaded from the same account etc). Then we must used nested callbacks, to clarify that WHEN the first async operation is finished and was succesful, do again something else.
Weniger sehen
Keine Merkmale angegeben

Kontext

AC-1 Add more rulers Use the nested callbacks pattern to add more rulers. Set 'Cersei' as the current ruler for initial state. After 5 seconds the ruler should be 'Danny'. 3 Seconds after that the new ruler should be 'Jon Snow' 2 seconds after that should be 'Euron Greyjoy' 2 extras seconds and the ruler should be 'Jaimie Lannister'. Finally a second after the kingslayer, the ruler should be 'Nightking'.   Golden Rule: Use only setTimeout function for this purpose.
Weniger sehen
Keine Merkmale angegeben

Kontext

Callback Hell / Pyramid of Doom If you did something like that, in order to solve the previous exercise you are right. Congrats! However you also understand that this code is very hard to maintain and to debug. We have only 5 nested callbacks and it is already very hard to identify what is inside what. Where something begins, and most importantly, where does it end.  This situation is called callback hell, or pyramid of doom. Is the pattern when the code we write spreads more horizontally than vertically (similarly to putting multiple nested if statements one inside the other).  Well thankfully there is a way to perform exactly the same thing but in a syntactically more straightforward and easier to debug way. The promises!
Weniger sehen
Keine Merkmale angegeben

Kontext

Promises So what are promises?  Promises is just another syntactic sugar that was invented in order to avoid the pyramid of doom from happening.  Promises are objects and any time we have an asynchronous operation, we can create a new promise object like we did on line 7 by using the new constructor for objects. Now as we know, objects have some properties and promises are no exception to that. Promises have a state and a value. And if you run the code above you can see both of these properties to your console and the promise object itself (because we console the promise on line 12). Until the asynchronous operation has finished and we have the data back, the browser can do other things. That means, that in the meantime the state of the promise is PENDING and the value undefined.  Once the operation is finished the  state changes either to RESOLVED or REJECTED (in case of an error, data was not there etc) and the value corresponds the data that came back.  Attention! In order for our function to be an asynchronous function it is always mandatory that we don't return a value, but the promise itself. The value is returned indirectly by passing it as an argument into the resolve function as seen above.
Weniger sehen
Keine Merkmale angegeben

Kontext

Chaining promises - the .then function What about this syntax advantage that promises offer against nested callbacks then?  What if we want something to happen AFTER the promise has been fulfilled and the new ruler is indeed danny?  Well in order to chain promises as we call it, you need to use the .then() function as seen above. The line 17 executes the changeRuler function, which sets Danny as a ruler after 3 seconds and returns a promise. If we want to do something after this promise, we use the .then and inside we pass as an argument another function.  This function (anonymous) will define what is going to be done directly after the previous promise was resolved. If we need to have access to data from the previous function, we can! The inner function accepts an argument that represents exactly that! The data that has been passed from the previous function inside the resolve! In our case the ruler's name. In fact, resolve acts like a return statement for promises. Not only defines that a promise's state is fulfilled, but also which data will 'travel' into the next step of this journey (the next .then() function in this case). In our case the data argument corresponds to the value of the ruler variable that was passed into the previous resolve.
Weniger sehen
Keine Merkmale angegeben

Kontext

Chaining multiple promises What if we want to still go further and chain more promises? We can chain .then after another promise again. As seen, in line 19 we call the same function (that returns a promise) with the name of Jon Snow, so as we know after 3 seconds the ruler will change to Jon Snow. Bear in mind: The inner function must return a promise, in order to chain with the .then function afterwards and console the 'Jon Snow' name.
Weniger sehen
Keine Merkmale angegeben

Kontext

AC-2 Promisify the 5 rulers exercise Rewrite in a promised way the 5 rulers nested callback exercise you did before. Chain all promises together as many times as needed. Stick to 3000ms as a time for the setTimeout for all names this time. Bonus: If you have done the first part, try to rewrite everything, using again a promise chain, but by no typing the .then() function more than once! Scratch your head on it a bit.
Weniger sehen
Keine Merkmale angegeben

Kontext

The Catch block The catch block goes normally at the end of the promise chain and it executes itself only in case something went wrong or the reject function was called. In our case, and because we all dislike Jon Snow, at line 9 we have explicitly defined that in case the ruler's value is 'Jon Snow', this promise will be rejected with some meaningful message. The message is being passed into the reject function, and it can be accessed through the only argument the inner function of the catch block accepts. There you can see it.  Bear in mind! If a promise is rejected at ANY point the whole chain breaks and the remaining .then() blocks will be ignored. The pointer moves from directly to the .catch block and executes this.
Weniger sehen
Keine Merkmale angegeben

Flussdiagramm

Ajax requests to a server. Server sends back to client a browser.

Kontext

AJAX Calls AJAX was introduced back in 2005, and it stands for Asynchronous JavaScript And XML. In reality when we refer to AJAX calls, we mean the communication between a client and the server that takes place asynchronously (at the background).  Normally the client-server model requires a client to make a request from a server in order to send or receive some data from it. This operation could take more than 1-2 seconds, and if we do it synchronously, then we will pause the execution of our page until the response comes back.  Luckily by making AJAX calls the whole request-response happens on the background (the user many times doesn't even notice) asynchronously. That means that when the response comes back from the server, the whole page doesn't need to be reloaded as before. Thus we update only a small portion of the page, without refreshing everything.
Weniger sehen
Keine Merkmale angegeben

Kontext

The fetch API Fetch is an asynchronous function that is provided by JS natively in order to perform AJAX calls, either locally or, most of the times, externally.  Fetch accepts a couple of parameters, but most importantly is the url address of the service, that we are requiring information from.  In the example above we just require to bring asychronously an image to load it into our html later. Don't forget that fetch (due to it's asynchronous nature) returns a promise, thus  in order to see the results (the response that comes back) a  .then() function must be chained afterwards.  The response that comes back is a response object that shows if the communication between the client and the server was successful or not (status code etc.)  In our case we use the url property of the response object in order to fill the src attribute of the freshly created image.
Weniger sehen
Keine Merkmale angegeben

Kontext

Receiving a JSON  Most of the times we aren't going to request data to fetch a local file. Let's be honest, if that was the case we could have included an tag from the beginning directly to our HTML and define the src there.  Most of the times we want to perform requests either to third-party libraries or to external public API's in order to get data from them. The most realistic scenario is that this data will come in a format of JSON. JSON stands for JavaScript Object Notation (and has nothing to do with Jason Statham :-)). It has exactly the same structure as a javaScript array or a JS object (it depends most of the times is a combination of both). The key difference is that the keys of this JSON object are strings (surrounded by "double-quotes"), thus the whole JSON is a 'stringified' version of a JavaScript Object.  That's because when exchanging data between a server and a client, this data that is being exchanged must be only a string and nothing else! In order to be able to work with it, we must convert it to a normal JS Object. In the next chapter we are going to see how exactly this can be done when we are trying to fetch data from the openweathermap api, to bring weather information for a specific city, or place all over the world.    You can have a look in the code above, if you want to understand what the structure of a JSON Object is. Attention: If you don't convert the JSON object into a normal JS object (or array in this case), you will not be able to access it's data or to perform normal JS object (or array operations).  Bear in mind, that this is only a piece of string for now! If you want to learn more about JSON files and documents in general, do it here!
Weniger sehen
Keine Merkmale angegeben

Kontext

Fetch weather data from open weather The code above describes step by step the procedure for fetching the weather forecast data for cities.  In line number 10 we make a fetch request to an external api. We use the url address we have defined in line 7. This url must be known from before, in order to know what to use, and how to use it. In addition we pass 2 parameters inside this query string, the first is the city that we are going to fetch weather forecast information for. In this case we have hardcoded defined that this city is going to be 'Milan' (line 5),  the other one is the authentication token we have already acquired from the official website of the application. Some API's require authentication tokens, or API keys, that you can require by registering or creating an account to the websites of these API's. Now i have already done it, i have required my Authentication token and pasted it in a variable in line 3. Both are used in line 7 if you watch carefully, especially at the end of the url string. Then everything follows the normal procedure. Fetch returns a promise of the response object in line 10. When the response is back, we can go further. But the data is JSON data, so in order to work with, after we have received the response (console it if you want), we must parse it and make it again a JS object. Thus we use inside the callback function, the .json() function that returns also a promise!  After the latest promise is also resolved, and the weatherResponse has been transformed from JSON object into a JS Object,  we can go further, with our normal weather data as always. This happens from line 17 and after. Highly recommended to read, how the fetch API works here.
Weniger sehen
Keine Merkmale angegeben

Kontext

AC-3 Fetch data from openweathermap API Use everything you have learned so far in order to fetch data from the openweathermap API. Instead of using a fixed city for your requests, create an input field, and make a request for the city, that the user has picked to fetch data for.  Create a new section in HTML for every city response you get back. This section should contain a heading (preferably an  tag with the name of the city), a small paragraph that shows how many degrees the temperature is (the temperature data get's back in Kelvin degrees, subtract 273 from that to convert it to Celsius Degrees!), and a span at the bottom of the section that depicts the description of the weather (small text) for that  city.  Bonus: Display the icon next to the span also if you can. Find the API endpoint for that and the correct icon data for that, and use it as a source for the small image. Extra Bonus: Paint the background of each section with a different color, depending on the temperature that you received back from the API.  The rules for the colors are:  Citytemp > 40  paint it 'Red' Citytemp > 35 paint it 'Orange' Citytemp > 30 paint it 'Yellow' Citytemp > 20 paint it 'deepskyblue' Citytemp > 10 paint it 'purple' Citytemp > 0 paint it 'grey' Any other temperature below 0, paint it 'white'. Try to do it the first way it came into your mind, then try not to use if-else, or switch-case statements. Maybe the mapping array technique will help you here. You can find the full result here if you want
Weniger sehen
Keine Merkmale angegeben

Kontext

Async / Await Async/await is another way to implement asynchronous operations in a more clear, concise and ... synchronous fashion.  In order to make something like that to work, you need to create a function with the async keyword before it. If you do this, then you can define for every asynchronous operation that lies inside this function, to stop (or yield) execution until the result is back, fulfilled and resolved. The way to do this is by typing the await keyword before the asynchronous operation like we did in line 7. That means that line 8 will not be executed until line 7 is fulfilled and the result has come back successfully.  Two things to notice! First: The pause of the execution is only for the function that we are currently in (the one with the async keyword, in this case the 'getWeather' function). The other functions can be executed in parallel, or at least the way the event loop orders. Secondly: The await method can be used before any function under one specific condition. That this function returns a promise  and a promise only! So you can't place the await keyword before a setTimeout function or a function that accepts a callback function and doesn't return a promise. In such case you would have to wrap the whole setTimeout inside a new Promise object, but this is a topic for later maybe...
Weniger sehen
Keine Merkmale angegeben

Kontext

AC-4 Refactor weather API the async/await way Rewrite the openweathermap application, but instead of .then() function and chaining promises, do it the async / await syntax you just learned.  Good luck!
Weniger sehen
Keine Merkmale angegeben

Kontext

Promise.all and arrays of Promises In the previous exercise we saw how we can perform multiple requests. Although we did it, we used async / await before every response. That means that every time we made a request to the API, we paused further execution of our function until the response came back. That means that the order of execution was: Request nr.1 made Response nr.1 received Do something with Response nr.1  Request nr.2 made Response nr.2 received Do something with Response nr.2  etc. Although we don't block any code outside the async function, we do it for the code inside the function. What if we want to change the order, to make all requests, at the same time, without waiting for responses? Like texting 5 times a message to 5 different friends, and then wait for them to answer. Maybe no-one answers, maybe all of them do at the same time, but for the in between time, the only thing we have is 5 promises. Now normally, if we want to save a new Promise object in order to return it later, we must save it into a single variable. But now if we make multiple fetches inside a loop, we can't. Instead we can use an array and push there every new promise for every fetch request we make. In the end we end up with an array of promises! Nice! But now we have another problem! normally we know that when a function returns a single promise, we can define what is going to happen after, by chaining a .then() function. But now we have an array of promises and we need to define that we want to execute further, only when all promises have been fulfilled (when all responses are back). So the execution order will be something like this:  Request nr.1 made,  Save the promise nr1. into an array Request nr.2 made, Save the promise nr.2 into the array Request nr.3 made, Save the promise nr.3 into the array Request nr.4 made, Save the promise nr.4 into the array Wait for all responses to come in any order. Then after we have gathered all the data objects into this array, we execute normally. We can now loop and create HTML elements for every data object that we have etc as before.  We use the Promise.all([promise1, promise2, promise3, promise4]) in order to express that the next .then() function (or the next line after the await) is going to be executed only AFTER  all promises have been fulfilled and all responses are back. Bear in mind! The data that is going to be passed in the next .then() function (or is going to be stored in the await variable depending on the method you prefer) is going to be an Array of values! In this case an array of object, that contains the data and all the responses.  As shown in line 18, 'allImages' variable is now an array, and if we want to show all of them we must create a loop, to create a corresponding HTML element every time, for every response we got back. Documentation of the Promise.all function
Weniger sehen
Keine Merkmale angegeben

Kontext

AC-5 Make multiple requests Refactor your weather application, so the input field doesn't only accept a string value of a single city by a collection of cities that are comma separated. So for example, the user can write in the input field something like 'Milan, Athens, Hamburg, Berlin, Stockholm'.  You must take this data and find a way to create a new request to the openweathermap api for every city's name that comes after the ',' separator. That means that after all responses are back and parsed, you are going to see all 5 sections with the data that corresponds to each city. Bonus: Instead of showing all the sections at the same time, try to make every section to appear after 0.4 seconds from the previous one.  Hint: Try to use async / await but don't forget that setTimeout functions accept callbacks, so maybe you want to wrap the whole setTimeout into a whole Promise Object. Then you can have the await keyword for it.  Good luck! See the final result here if you want, type multiple cities with a '-' dash between them.
Weniger sehen
Zusammenfassung anzeigen Zusammenfassung ausblenden