How JavaScript Promises Work Under the Hood

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we'll learn how to implement promises in JavaScript on the ride I've kept my promise class which at the moment is just an empty class and on the Left I'm importing my promise class then I have a test suite but I've prepared for this video now I'm just going to start from the top and make these tests pass one by one let's look at the first test it says executors function is called immediately execute a function refers to the function that is passed into the promised constructor to make this test pass we can just simply call the executed function it's the only argument passed into the constructor let's look at the next test it says resolution handler is called when promise is resolved so resolution handler refers to the function that is passed into then and what this test is asserting is that when when the promise is resolved with a value then the resolution handler should be called afterwards with that same value we're on the test we'll see that we get an error promise that then is not a function okay so let's first create a function called Ben next we get an error is saying resolved it's not a function so we need we need to pass a function to this executive function so I'm gonna use underscores here to denote private methods so I just create a method called resolve and then I pass it into the executor now it's important to remember to bind it to this because otherwise we can't use this inside the resolve method of course this won't still work we still have to implement these functions let's first understand in which order this lines of code are executed so first we create our new promise so we call the constructor and then we call promise that then a pass in this function and then a hundred milliseconds later you see we're using set timeout the promise is resolved so because we first call then and then resolve the promise that means inside this promise object we have to store a reference to this resolution handler the resolution handler is passed into then and now we have to take that and store it somewhere now the interest of time I'm going to anticipate some some of the future tests here I know already that it's possible to attach many resolution handlers to a single promise so I'm going to create an array called resolution Q and that's where we're going to put our resolution handler now that we have a reference to our function we need to call it inside resolve so here we iterate over the array we always take the first one off the array the first function and then we call that function and we call it with the with the value that is passed into result let's move on to the next test so this one says promise supports many resolution handlers now I said I would anticipate a little bit so because we use this array and we push this resolution handle Street and then in resolve we go through the whole array and just shift off all the functions and call them this test should pass already there we go let's move on to the next one since resolution handlers can be chained so in this test we called Ben but in sight then in this handler we return a new promise and we call then let's see what happens if we just run the tests so we get cannot read property then of undefined but the first thing we have to do is return a new promise and then so let's think about this a little bit we have to capture this promise and when that promise is resolved we have to resolve the promise that we returned from then and that's going to trigger this resolution handler that we attach to the return promise so how do we capture this well that's the return value of the resolution handler we call it here so we could just assign the return value to a variable we check if the return value is an instance of my promise and then if it is we'll call then and here we should resolve the promise that we return from them so we need to store a reference to this promise because the resolution handler and the new promise are inherently associated with each other I'm going to create a new object that stores references to both of them and then push that object to my resolution queue because items in the resolution q are objects now we have to change this to refer to the function by saying resolution to handler and here we can resolve the Associated promise let's move on to the next test it says training works with nonprofits return values so what we're testing here is that if we return something that is not a promise then the next promise will be resolved immediately with that value now at the moment if we return something that is not a promise we're not even resolving the Associated promise so this function is never called we need another condition here another test works but I noticed that we should add a test here to make sure that the return value exists let's look at the next test it says resolution cameras can be attached when promise is resolved if the promise is resolved already it should still be possible to call then with a new handler that handler will simply be invoked immediately with the stored value of the promise so here we call promise that then and give it a handler and in sight the handler we wait for 100 milliseconds and then attach new handler just that same promise if I run the test we'll say that this fails this inner function is never going to be called so we'll need to add a check too then so if state is resolved we have to do something special in the constructor I give the state the initial value of pending and if the state is resolved we actually just want to process this resolution Hana and the problems we just created and pushed into the resolution queue now we could maybe just call this dot resolved but the problem is we don't have this value that is normally passed in to resolve so the first thing we have to do is we have to store that value so assign the value to this down underscore value and I changed the code below to refer to this down underscore value instead of just value and now I can extract this part to its own method I'll call this new method both in the result method and in them the test still fails because I forgot to change the value to resolved in this result function so let's do that okay let's move on to the next test calling resolve second time has no effect so in this case this call to resolve should have no effect whatsoever the test fails because every time we call resolve we set this the underscore value to the new value passed in to resolve so let's fix this we only do anything if the state is pending otherwise we'll just skip the whole method Jackson handler is called when promise is rejected rejection happen refers to the function that is passed into promised catch and what we're testing here is that if we reject the promise then the rejection handler should be called if we run the test the first error we get is promise that catch is not a function okay so we need a catch method and I already know that the next error is going to be reject is not a function so we need a reject method as well and we need to pass the reject method into the executor okay and now we get this test exited without ending that we've seen a few times before and that just tells us that the handler is never called and T dot and is never called either so we want to do basically the same thing that we're doing resolve but in this instance we want to save the handlers into another array so I'm going to call that array rejection queue okay our reject method is almost the same as a resolve method the difference is that instead of this to underscore value we're going to store the argument into a variable called rejection reason and the state that we're going to transition into is called the rejection and then instead of running the resolution handlers will run the rejection handlers and I need to implement a method called run rejection handlers next one rejection handlers it is almost the same as run resolution handlers the difference is this time instead of iterating through resolution queue we iterate through the rejection queue and instead of calling the handler with the value we call it with the rejection reason next we have to implement catch again catch is almost the same as its counterpart then the difference is that instead of storing the handlers to the resolution queue we push them to the rejection queue and instead of checking if the state is resolved which check we check if the state is rejected and then we run the rejection handlers and that take care of that test let's move on to the next one so this test this chaining works with rejection handlers but I think actually a better description would be rejections are passed downstream what we're testing here is if I reject the original promise then we'll skip all the following resolution hammers and go directly into this rejection handler that we're attaching here right at the bottom of this chain now this doesn't work yet because the only thing we do when a promise is rejected is we run the rejection handlers so in the reject method in addition to running all the rejection handlers associate associated with this promise we should also go through the resolution queue and reject all the promises that we return from the ven method this way the errors will bubble up to the bottom of the chain and this function should be invoked as well okay all the tests pass so far so let's go to the next test so what this test this test is called rejection promises returned from resolution handlers are called properly so what this is testing is that if we return a promise from a resolution Handler and if that promise rejects then the rejection Handler attached to that returned promise should be invoked so let's go here so this is where we resolve the promise if the returned promise is resolved now to do the same but for rejections we can simply call cash here an inside cast we just reject the promise returned from then that works next rejection hunters catch synchronous errors in resolution handlers what we're testing here is if I throw an error in a resolution handler that should be treated as a rejection and basically there's a rejection handler down below should be invoked with the error if we run the test we noticed that that error is thrown but it's never caught so where can we catch that error well we can catch it here where we call the handler in the first place we'll wrap it in a try block and then we simply catch the error and then we simply reject the Associated promise with the error and now our test passes let's move on to the next one it says rejection hunters catch synchronous errors in the executor function so we're basically testing the same thing but for the executor function instead what we'll have to do is wrap the executer invocation in a try block catch the error and then reject the promise with the error now this doesn't work yet and the reason is that we throw an error before we even attach any handlers to the promise and insight then we don't handle the case where this promise is already rejected so we'll have to add that in so we just reject the new promise with the rejection reason it says rejection handlers catch synchronous errors so this is the same that we did previously for the Ben method so if the rejection handler throws an error that should be treated as a rejection as well so what we'll have to do here is we have to wrap the call to the rejection handler in in a try block and then catch the error and reject the next promise with that error okay let's to the next test so this is just testing that if we return any promise from a rejection handler it should function just the same as when we call then so there's really nothing new here and and it works right out of the box because of the way that we implemented the catch it's basically the same thing as then but we run the rejection handlers instead of running the resolution handlers then we return a new promise and the way we treat the promise is identical with catch and then move on to the next test it says rejecting promises returned from rejection handles are caught properly so in this case we test that if the promise returned from a rejection handler is rejected then the rejection should be caught in the following rejection handlers now this doesn't work yet it's because we have to add a couple of lines of code now earlier we added these lines of code to run resolution handlers method and we have to do the same thing up here as well just change this to rejection let's move on to the next test says second argument in then is treated as a rejection handler so as you may know brick then takes two arguments first is a resolution handler and then it also takes a rejection hammer and we haven't implemented that yet so let's do that now the only thing we have to do here is just push that rejection handler into the rejection queue whoops and we get a bunch of errors and that's because we'll first have to check if this rejection handler actually is a function at all okay then on to our last test this description is second argument in then is attached to the Brahmas promise then is called on so I think this a common pitfall with people who are annuity promises when we pass this rejection handle into then that's not the same as just chaining it with catch because this rejection handler is going to be attached to the prompt the original promise whereas the rejection Handler that we pass in to catch is going to be attached to the promise that is returned from this then method and here we see why that's important if we return a promise that rejects then it's not going to be caught here this function is never wrong instead it's going to be caught here down below so this is what I don't really ever use this feature the second argument to then I just always add a catch method at the bottom of the chain and we don't really have to do anything to make this test pass I just wanted to add it there because this is a point of confusion for many people so it's just something that is good to keep in mind when you're when you're programming with promises okay that's it for this video we have a reasonable promise implementation it's not a perfect implementation there are some edge cases that we don't handle and it's probably not the most performant fastest promise implementation out there this is purely for educational purposes the goal for this video was to demystify promises a little bit and just take away some of that magic and hopefully you'll learn something leave a comment if you have any questions and see you in the next video thank you for watching
Info
Channel: vividbytes
Views: 6,676
Rating: 4.9157896 out of 5
Keywords: JS, coding, promise, programming, JavaScript, Web
Id: C3kUMPtt4hY
Channel Id: undefined
Length: 21min 30sec (1290 seconds)
Published: Thu Sep 21 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.