Async/Await: Modern Concurrency In JavaScript

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Oh tonight we're gonna talk about concurrency we're gonna talk about a sink and a weight as a sort of modern approach to doing concurrency in JavaScript and I'm Simon so I'm a JavaScript and react engineer I've been been doing JavaScript for a long time and react for a number of years now I have a have a team in just outside of Jakarta code fox is our company and so you can also find me on twitter with SST you are underscore so today we're going to talk about concurrency and it's kind of a technical topic so I'll try to explain it as simply as I can and but you're definitely gonna see some code so we're gonna go through some code and talk about some concepts does maybe we should start with who is it is everybody a JavaScript programmer or does write JavaScript in here okay cool just checking and so like and what about like using kind of more modern es6 constructs is everybody using es6 at least has some familiarity with that yeah okay cool cuz just because that's what the the code slides that we're gonna look at is all es6 so so let's talk about what is concurrency so concurrency is doing multiple tasks in a period of time right so typically one core can only do one thing at once as well as other other pieces of hardware can only do a single thing at a time but over it over a stretch of time we want to do multiple things right so a web server wants to serve multiple requests for a web page a script wants to read multiple values from a database before it returns the request and so so what we're really talking about is doing multiple tasks over over a period of time and some of those things we can do in parallel and some things we have to do one at a time and so generally order dependent or partially ordered units oh sorry order independent or partially ordered units of work is where concurrency happens so this is just an example of like multiple tasks that may start at various different times but are kind of running over the same period of time right so they're running in parallel essentially some of these are right and so this is this is something that is is pretty hard to do in computing right in in particular it's hard to reason about it's hard to it leads to race conditions it leads to difficult models of things changing over time right and so concurrency is specifically important when we're dealing with user input I'm sorry input/output is in like network requests also user input users interacting with the system and reading writing from disks anything that is kind of considered a synchronous type of activity right something where the CPU needs to stop and wait for the user to do something or the CPU needs to stop and wait for some data to arrive from the database or the network right and so there's typically two ways for a program to do those kind of tasks right so to do i/o specifically we have the blocking or the synchronous style which is it's easy to write it's what PHP and Ruby and Python and most web especially server-side technologies are doing synchronous or blocking i/o right and that's and in order for that to work we need multi-threading because if we stop one thread we need another thread to be able to continue to serve user requests right and so but it's easy to write because you write everything sequentially however there's a memory overhead to that because every thread requires memory and there's context switching so when the CPU switches between the threads right so there's overhead there the other sort of paradigm the other pattern is the non blocking or the event loop style and so that's single threaded so everything runs in one thread which means we cannot block the thread we do get high concurrency and we at low memory so that's the win and it's really good for UI so user interactions stuff right that's why the browser uses the event loop pattern and it's also good for i/o bound services things that are doing a lot of reading from network or database or but it's not great for things that are cpu-bound right because anything that's using the CPU is is blocking other things that can be happening so that's kind of the the overview of the two patterns and so JavaScript as you know is the second one and so all the browsers and and most JavaScript server-side frameworks are all event-driven or non blocking there are some exceptions to that like historically like really older engines but in general every JavaScript you ever write will be event-driven non blocking so you might have any questions about that stuff so so what happens if you block in JavaScript well there are some things that will block the thread right that will halt any other execution and so alert right is the most common alert or prompt and so we also have a synchronous style of XML HTTP requests and it's it's rare because that was probably a mistake in the API they probably did not intend for that to be synchronous or at least it wasn't a good idea and then we have some stuff in node world that are intentionally synchronous for various reasons but I think the one that everybody's probably familiar with is the sort of alert style where it blocks everything else that's happening and so that's this one right and so that's like the page puts a dialogue up and everything pauses right you cannot interact or press a button you can't even scroll and so that's kind of a bad user experience right and so that's what we mean when we say a blocking interaction or blocking the thread so how do we write concurrent software without blocking so generally in JavaScript world as you know we use callbacks right and it's it's pretty straightforward we just pass a function and when the task is finished JavaScript will call our function and that would be for instance an on-click event so you pass a function to the to the document to the browser and whenever a user clicks a button then your callback gets it's executed similarly a network request right you you're waiting for the network requests to finish and then the callback happens so this is the callback style all right we're gonna read a file this is more maybe server-side we read a file and the files finish reading we have our content so does everybody understand the callback style I mean that's pretty much every every JavaScript program you've written right so what's the good days it's a good low level abstraction for concurrency for asynchronous actions it performs really well it has low overhead there's not context switching involved and we can do almost any asynchronous tasks using callbacks right what's the downsides well doing things in callback style is really difficult right so doing things in sequence is difficult doing things in parallel is even harder you give up your constructs that you're familiar with like for and while and try catch and those are things that people come to JavaScript world from PHP background or from Java or from any almost any other language and they don't understand why you can't do these things right and so error handling right as you know you always have to check this error parameter things could fail during the middle of a network request it needs to be handled and your code readability goes downhill pretty fast so systems become kind of hard to maintain when there's a lot of callback asynchronous code and so this is kind of the example of the first one which is like the or sorry they're giving up the constructs that programmers are familiar with right this is your typical example right so it's like every day on Stack Overflow somebody asks what's wrong with this code right and does everybody can anybody see why this code will not do what the programmer will expects right so here we're defining an empty variable and here we're returning an empty variable and then somewhere later that variable will be set after the networker guest comes back but to a programmer from a non asynchronous world this is a strange thing right this is a strange thing and so that's why and that's why I like people new to JavaScript are always on Stack Overflow asking this question right so you know callbacks they add complexity it's messy to chain them right if you need to do three things in a row simple things right like like write something to the console wait a second write another thing to the console wait another second and write something to the console that's like a lot of callbacks right so it's so it becomes messy to chain things and it's pretty hard to do things at the same time and so this is your sequential stuff and so this might be the most code you see on any slide but but I want you to get the idea of how the complexity grows when you're using this style right and we're not even doing any error checking here right this is like the simplified version this is just taking three files and we just want to total the number of bytes in those files so we have to read three things from disk right so we're gonna so we're gonna stat file one add it to the data size to the total stat file to add the size to the total and do the same thing with number three every time we're getting more indented and then eventually we have our total and we can do our callback right and so is anybody written code like this all the time right so if we want to do so these three things these three files we don't have to do them one by one right we can just read all the files and then get the total number of bytes so if we want to do it in parallel it's a little harder right so this is kind of my naive version with no error handling of doing the same three files in parallel so here we we put the files in an array with four each and we stat each file individually right the problem is how do we know when we're done because they might the the answer might come back in any order so we have to keep track of the number that are finished and we got a count every time one finishes we got to add that number and when we've reached three then we know we're done right so this is kind of parallelism in whole back world right and then there's air handling so which we didn't even look at yet right so what do you do like we spend a lot of effort just just checking if there's an error when we've read file one but file two has already completed successfully how do we handle that and if we look at the same example with air handling it doesn't even fit on a slide right because now we have to like if there's an error we need to log it and return and that's not even good error handling like that's the minimum error handling just a log the error and just give up right and so we have to do it every time and so we have readability issues so when when readability is this bad you know systems become difficult to maintain a difficult to read it's easy for sort of like errors and bugs in your code just to sneak in so so let's talk about promises so we've kind of talked about call backs right so promises do a little better so this is basically a thin wrapper a thin abstraction around call backs and so it gives us the chaining that we need you know doing tasks one waiting till it finishes doing task two and continuing in sequential order it gives us helpers to do things in parallel it gives us error handling and there composable right because the way a promise works is we can pass around a representation of a future value right so a promise is an object that represents what the value will be when the operation finishes and so it kind of looks like this so we read a file and then we get two things we get then and catch so we can so then we can in a sense attach a handler or a callback that gets executed whenever the data is finished so it's better than callbacks like it it may not seem so at first it may seem more complicated but it is better and so it looks if you look at this code like all we did was take our callback and put it in then right but we still have our call back but it's better because we can because we can do some chaining with that and so I'll give you an example with so this is so this is a very simple function that uses set timeout and it just waits for 1000 milliseconds for one second and then it fulfills the promise right so it's the same idea as a set timeout but in promise world so so the idea is that here we want to just wait one second and log something and wait another second and log another thing and so we don't want all of that nested callbacks so we we get a better more readable pattern here right so at least we can see first we sleep then we do this thing and then we do this other thing and then lastly we do this this final thing so at least it reads in a very sequential order and so the first thing you might notice is that we're returning something from inside our our callback right so inside of our event handler we're returning another promise so remember whenever we call sleep it returns a promise that will fulfill or will finish in the future resolve it will resolve in the future and so when we return to promise from in here that's what allows us to call another then and then the last one we didn't return anything so we're finished right so even this right might be a little better than callbacks but you know it's not amazing but we get flow control so when we go back to our example of doing certain things that we have to do in series we have to wait for one to finish and then do the next other things we want to do in parallel at the same time and so that's really hard with call backs but it's easier with promises so the example would be we want to fetch some data about maybe the user that's logged in right now and then we're gonna get back we're going to get back this user object and then we want to fetch the friends of that user and then we'll return that promise right so every time we call fetch JSON we're gonna get this promise and so we'll return that promise and then when that finishes we have the IDS of all the friends and so now and and this is where it becomes powerful is now we can take these IDs and we map through them so we so we go through each individual friend ID and we want to fetch the user details of that friend and what I do here is I actually create an array of promises so each time for each ID we return a new promise and since we're calling a radar map we get a map we get an array of promises and then we can call promise that all which just waits for them all to finish gets all the results and we can return that promise and then when we're finally finished our last then we have like all of the details for all of the friends in the order that they were provided in the same order that they were in this array so promise that all is the magic that I wanted to understand from this slide so here we're doing something that would be really hard in callback world because we're doing the first two things in sequence right they have like we can't get the friends until we get the user right so we have to do some things in sequence but other things we can do in parallel so so that's the power of control flow with promises so promises even though they don't seem great at first or at least when I first learned them it seemed like a not much better than callback so once you explore the chaining and the control flow it is better so error handling we get a catch and so we don't have to check every single time for this error we can just attach one one catch function at the end and so exceptions will bubble through our code or bubble up the way we expect it to work in normal programming with try-catch so in this case if we go back to our same user profiles example I can just add one catch at the very end and grab that error and we just know an error happens somewhere in one of these and error happened all right everybody with me so far cool but we're still putting callbacks inside of then right I mean we're still doing callbacks it's a nicer way to do callbacks but ultimately we eventually have to have to pass these functions so can we do better so what do we want to do like we want to just like have normal sequential programming like we're used to in other languages and so here's what we want right we want we want to get our promise we want to somehow wait for the promise to finish and then we just want to log the result right with no then anywhere and so the problem is javascript is fundamentally single-threaded so we can't block and what I mean by that is we can't just pause the program right here wait for the promise to finish and then resume because we don't want to block our entire thread and then you have the same problem as when you use alert and then the whole software is just not doing anything useful so javascript is single threaded we can't block however there's a special thing called a generator function and so what that is is that allows us to pause just one function and we'll go back to it later so we're not gonna pause our whole program we'll just pause this function and so as anybody used or seen this generator syntax with the with the star so that's the special thing right here is the star and then the keyword yield so generators are generators are not about asynchronous or event loops or they're not about they're not about doing concurrence the concurrency they're just about pausing stuff basically right so we get this thing called yield we can just wait so the idea is we just pause the execution of just this function we wait for something and then later we will be resumed and we just console dot log the string that says we're back whenever somebody has resumed us so there needs to be something controlling this some function needs to needs to resume this whenever the whenever the fetch has completed so generators are pretty complex and I don't want to spend a lot of time on them I just want you to know that generator is the only thing that kind of allows this to work because we can pause our function and it's and it's important to understand it just pauses this function not everything so we take promises we combine it with generators and we get awesome stuff so this is the this is the whole the whole point we get a single wait so it's basically just a layer of syntax on top of generators and promises and it looks like this so instead of a function star we have this async function but it's really similar to that function star right we use the word a wait and we put a promise after that so a wait and then a promise and it always has to be that way and then what that does is that a way we'll pause it in the middle of in the middle of executing this line we just pause the whole function and we just wait but we you know we let the we'd let the event loop do other stuff and then eventually when this fetch has completed this function gets resumed and then we have our results and now we can just console dot log the results so this is the whole idea of a single way to things remember you can only use a way inside of an async and it has to work with promises so it's a win it's a win because we get back our traditional constructs right we get back our for a while or try catch it becomes readable I mean it's got its got some keywords in there that might seem strange at first but but it flows like normal programming in any other language and normal thread blocking programming right and we get this kind of interrupts with promises so we can use any other promise library and use it with a single way because it just uses promises so the same example or a new example of reading a file here with async/await is that here we just use this read file which is a function that we've kind of made up that presumably just returns a promise and then we just wait for that and we get our content and then we convert it to a string we parse it with JSON and we log the result so it's so it's it just reads sequentially like you'd expect and and the point of this slide is that now we have our try catch back so two things could go wrong here right something could go wrong with the reading of the file the file doesn't exist the the USB stick has been unplugged the permissions aren't there so this thing could fail the other thing that could go wrong is parse there was invalid JSON it just it doesn't the the JSON dot parse can't handle what the file had in it so these two things could have gone wrong and so this thing is going wrong in async world right something is happening asynchronously while our function is paused but this exception is just a traditional normal regular JavaScript exception and async await doesn't care the try will work exactly how you expect it to and and it will it will send the error to your catch no matter which problem happened so we can kind of not really think about async callback errors be different from any other error and so we also get back our for loop right things that are just normal programming constructs that have been around since the 70s and we understand them and so in this case we're combining just a regular for loop with async/await and we get something really cool so in this case we just take a regular browser element and we're gonna loop through a hundred times and move it a little bit to the right by increasing the left value by one pixel every time we're just gonna increase it to the right but we we want to do it every frame right it's an animation we want to wait in between the frames and so we're gonna wait 16 milliseconds between every between every adjustment of the of the value of the style so in this case in like four lines we've written in an animation with a hundred frames that happen 60 frames a second that would be really difficult to do with callbacks but because async await just pauses our function in the middle of a loop it just does what we kind of expect it to do and it's understandable anybody have any questions about this so it's just promises an async function itself returns a promise and so when we await a promise our function pauses until the promise is already or until it's resolved so we can still use our normal promised stuff right so promised out all which we talked about earlier it still works with with async/await and here's an example of that so here we're gonna fetch our we're gonna get our user and we're gonna get our friends and then we're gonna map over our friends and create our array of promises just like before but in this case we're using a wait for both of the first things because we can't do the second thing until we finished with the first thing right so we have to wait on those but these promises that the fetching the friends we can do those all at the same time so we just create an array of promises just like before we call promised at all and then we await that so we await them all to be done so we just so we as long as the thing on the right of the await word is a promise everything's cool so the thing on the right needs to be a promise and then and then we console dot log it at the end but the thing I want you to know about this is that this whole entire function because it has this async word itself returns a promise right so if I call the function get user friends if I call this function it returns a promise yes yes you can you can await another async function because it's just promises so a few pro tips don't forget to use your await if you accidentally leave out this word because you're so you're so excited that everything is like you know normal programming again that you kind of forget to leave out the await it's not gonna do what you think it does so don't forget your await be careful about doing too many things in sequence if you can actually do them in parallel so now that we have our four loops back that we love it's very tempting to just put a weight inside of a for loop and we have to remember that if we do that it's gonna pause that for loop every single iteration so don't do too much in sequence if you can do it in parallel and so the other thing is that using a weight in a map or filter function probably doesn't do what you expect and so I should show you an example of that but the idea is that a filter function right a radar filter should take a function that returns a boolean but if you pass an async function it's gonna return a promise and so probably not gonna do what you think it does so you just have to remember that so map it's fine to use but just remember that if you pass an async function into map you're gonna get an array of promises which might be what you want and so even though it looks synchronous also remember that your code has been paused and then started again later so some of the assumptions that you might make about global state or global variables might no longer be true after your function has been resumed because things have changed in the world while you were paused so user input right it's been traditionally hard with nodejs if you've ever written a CLI tool you need to read some data from the user and then you gotta have a call back and then you got to read more data from a user and it becomes really nested and so this is just our final example it's just that we can just create a read line function that returns a promise and we can now await it and write command line programs just like you wouldn't Python or any other language but we get all the advantages of note as well so you can use this today async/await is in chrome it's in Firefox and it's a node for any of the other browsers you should use Babel but but it's actually not that bad to use Babel because async/await is a pretty thin layer on top of just generators and promises so most browsers support generators anyway so there's not too much stuff that Babel needs to do to make this work so thanks for listening
Info
Channel: Coding Tech
Views: 115,567
Rating: 4.9097266 out of 5
Keywords: javascript, async, await, concurrency, web development, real time web
Id: NsQ2QIrQShU
Channel Id: undefined
Length: 29min 42sec (1782 seconds)
Published: Sat Dec 02 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.