Narrator: In order to escape the so called call back hell
that I showed you in the last lecture let's now learn about
promises, which is an ES six feature designed specifically
to deal with asynchronous JavaScript.
Now, before we start, what is a promise actually?
Well, it's a complex topic and it's easier to understand
in practice, but in simple terms a promise is an object
that keeps track about whether a certain event
has happened already or not, and if it did happen,
then the promise determines what happens next.
And with events here I mean an asynchronous event
like a timer finishing or data coming back from
an ajax call, okay?
So, a promise kind of implements the concept
of a future value that we're expecting.
It's like us saying, hey, get me some data from the server
in the background and the promise then promises us
to get that data, so we can handle it in the future, okay?
Does that make sense?
Now, since we're dealing with time sensitive code now
a promise can have different states.
So, before the event has happened the promise is pending.
Then after the event has happened the promise is called
settled or resolved which is the same thing, okay?
Now, when the promise was actually successful,
which means that a result is available,
then the promise is fulfilled, but if there was an error
then the promise is rejected, simple as that,
and this is actually important to know because we will
then be able to handle these two different situations
in our code.
Now in more practical terms we can
produce and consume promises.
So when we produce a promise we create a new promise
and send a result using that promise.
Then when we consume it we can use call back functions
for fulfillment and for rejection of our promise.
So let's now produce promises in order to recreate
the example that we built in the last lecture
and then consume those promises.
And again let's start by cleaning up here our code
from the last lecture by simply commenting it out.
So, with promises just like every other object type
we use the new keyword.
So we say new promise.
And then into that promise we pass in a function
called the executor which is a function that will
be immediately called once the promise is created.
Okay, so let's just add that here as well
without actually for now adding any code.
So again an arrow function and so that's our promise,
and of course let's also assign this promise here
to a variable.
So const get IDs, okay, and that's because we're recreating
the example that we had up here, okay?
And so basically this first promise here will be
responsible for doing that first part here,
with the first timer.
So basically where we get these four IDs here
from the server with this fake ajax call here, okay?
But now getting back to our promise here
and this call back function that we passed into
the promise, remember that this one here is called
the executor function, and this function here itself
actually takes in two arguments which are
the call back function's resolve and reject.
So resolve and reject, and that's because this executor
function here is used to inform the promise
whether the event it is handling was successful or not.
And if it was successful we're gonna call the resolve
function and if not we call the reject function.
So remember that we had these two states here,
resolve if the promise was successful,
and reject if the promise was not successful.
So if you don't have any result,
and in that case we can call the reject function,
and I'm gonna show you how we use these functions
here inside our promise in a second.
But for now let's put some asynchronous task here
in the executor function because that is usually
what happens in a promise.
So something like an ajax call or in this case
a set time out function.
So again to fake the ajax call so basically the call
to a server to get some data, okay?
So again, we're going to use set time out
with a call back function after one second, okay?
Or after one and a half seconds.
That gives us a bit more time to see the result.
Now, when this timer finishes here after one
and a half seconds we want to return the data
with some of the fake IDs just like we did before,
and so now it's actually time to call that resolve function
and remember that we call resolve in case that event
was successful.
So calling the resolve function here will basically mark
the promise as fulfilled.
So, as successful, right?
And that is exactly what we want here, okay?
So let's call the resolve function here.
Resolve, now that function actually takes an argument here
which is for the result of the promise,
and so this is basically how we return our result
from the promise if it was successful
and what do we want to return here?
That's right, that's just this array of these IDs.
So let me just copy it from up here and put it down here.
All right, so again, what we do here is that after
this timer finishes after one and a half second
we say that the promise is successful and that we want
to return this data here and that's why we used
the resolve function and since set time out is impossible
to fail, so, this timer will always finish.
There's no way it is not going to finish
and so we don't need to call the reject function
in this case, but we would need it in some other cases,
like when we were dealing with data coming back
from a server where there can happen all kinds of errors.
But in this case we don't need to call the reject
function ever.
All we do here is to call the resolve function
which marks the promise as fulfilled
and returns this data here.
All right, and so we produced our very first very simple
promise here which is stored now in this get IDs variable.
Okay, and so now it is actually time that we consume
this promise, and in order to do that we can use two methods
on all of the promise objects and these methods are
the then and the catch methods, okay?
So all of the promise objects here inherit these two methods
and so we can now go ahead and use them.
So get IDs dot then and this then method allows us
to add an event handler for the case that the promise
is fulfilled.
So which means that there is a result.
So now all we have to do here is to pass in a call back
function which will handle what we do in case the promise
was successful which will actually always be the case
here in this promise, right?
So as I mentioned before, this cannot fail
and so it will always be successful
and so we can always call the then method on our promise.
So, let's add a call back function here
and this call back here actually can take an argument.
I'm gonna explain to you in a second how this works.
Okay, so I'm simply calling this one here IDs
and then an arrow function again and then just like this.
So here's the function body.
Now, this argument here that we pass into
our call back function which is this entire thing here,
right, so this argument here will be the result
of the successful promise.
So in this case it will be this entire array, okay?
And so let's now log this one to the console.
So console dot log IDs and that's actually it already.
So let's check it out and wait for it here and here we go.
So after one and a half second again it then printed
these four IDs in this array here to our console.
So let's recap what happened here.
So we created this promise here with this executor
function in it and inside that function we usually have
some asynchronous code which is in this case here
our set time out which after one and a half seconds
returns this data here in case it is successful
and that's why we use the resolve function
and the set time out function will always be successful
and so all we need to do is to simply always use
the resolve function here and then pass in the data
that we want to return from the successful promise,
so from the fulfilled promise and we can then handle
that successful, so that fulfilled, state down here
with the then methods that is available on all
our promise objects, all right?
So we have our promise object and then on it we use
the then methods which is used to handle the fulfilled
promise and into this then method we pass in a call back
function which will always have as an argument the result
of the successful promise and that's why if we use console
log of the IDs we will basically console log
this array here, okay?
Does that make sense?
Now, as I mentioned, we also have access to another method
which is the catch method which allows us to add
a handler for the case that a promise gets rejected.
So then an error happened.
So let's try that one as well.
And so, I'm going to change these here in different lines.
So just like this.
And finally a catch.
And here we usually call the argument
of the call back function the error
and then we can log this error to the console.
Okay, so again right now it's impossible that this catch
here is actually called because this promise here
is impossible to fail.
So we never called this reject call back function in here.
But just to try it let me actually use reject here
instead of resolve.
Just so you see what happens here.
And so you see the result is actually the same
because we still return this data here
but this time as an error basically.
So let's say we didn't have this catch here
and so now it shouldn't have any result.
Okay, and so now we actually get this error here.
Okay, but right now since we're actually catching
this error here and we can also instead say something
else here, just say error
and then let's try it out again.
And so now it says we have an error
and again we have an error because by using
this reject function here we basically mark this promise
as rejected and so therefore JavaScript assumes
that there was an error and so this part here
is the method that gets called, okay?
But now let's put it back of course to resolve
because our promise is always successful.
Okay, so now we translated this first part here
of our previous example and now let's write the two
promises to handle the part where we get a recipe
based on an ID and then where we get another recipe
based on the same publisher like we did here in this part.
Okay, so let's now add these two promises
and then we can put it all together down here
when we consume our promises.
Okay, so let's remember how this worked up here.
So we passed in an ID into the call back function
of this timer which came from the recipe IDs
that we got from the first step and so let's do something
similar here.
Okay, and so what we're gonna do is to actually create
a function which receives this ID
and which will then return a promise.
So let's say const get recipe which will be a function
that receives the recipe ID
and with this function body here.
So again an arrow function expression here
with the new ES six syntax.
Then in here we can return our new promise.
So we return and we say new promise
just like we did up here, right?
And now we need to do our executor function.
And this part here is always the same.
We always say resolve, reject
and then pass in the function body.
Okay, and now what happens inside of this promise is
actually pretty similar to what we have in here.
So let's just get it from this part.
Okay, but instead here of course of the console log
we will resolve this value that we have here.
Okay, so just like in our other promise we actually
want to resolve the value and actually I'm seeing
that this is not quite correct because we're still missing
our set time out here of course.
So let's add that very quickly.
So set time out, then in here we want to pass in an ID
and then this code of course.
Then 1500 seconds and then we pass in rec ID.
So, does that make sense?
So, into this get recipe function we pass in rec ID,
so the recipe ID, which we then pass in into the set time
out and so then it will become ID and then inside
the call back function we use ID here.
So this need to be ID in uppercase.
Okay, now how do we actually consume this promise here?
So let me show it to you.
So what we could do here of course is to simply call
the get recipe function here and then add a then to it.
So, just like this, like get recipe,
with IDs two, just like we did before,
and then this would return a promise, right?
And then we could add the then and so on and so forth.
But this would not really solve the problem
of the call back hell, right?
Because then again we would have this call back here
just then inside of another then and that's not ideal
at all, and so there's another way of doing this,
and that's actually the beauty of promises
which is something called chaining.
So we can return a promise from this then method here
and then simply add the next then method after it.
So let me show that to you so that it makes more sense.
So I do it like this, return, get recipe IDs two
and then I can add the next then after this.
This get recipe here will return a promise, okay?
So after this here runs we will have a promise here
and what we do then is to return that promise
and therefore we can then add the next then
to handle the result of that resolved promise okay?
So we now have this promise here and now want to
handle the fulfillment of this new promise, okay?
And what will happen when this promise is fulfilled
is that it will basically return this data here, okay?
And that is our recipe.
So let's call this parameter of this
call back function here the recipe
and then simply log it to the console.
And let's try it out here.
So the IDs and then finally the recipe.
So it's working just fine without any of the call back
hell that we had up here, right?
So now our code is all nicely separated here
into these different functions and then down here
we consume this promises.
We could of course put this one here into a function
as well but that's not really necessary
because we don't need to pass any data into this.
So, here this get recipe we did it as a function
because we needed to pass in this recipe ID
into this promise here, okay?
And so that's the second one and now finally let's work
on the last one.
So let's add another function here called get related.
And remember that this one here is basically to fetch
a recipe which is related to the author of the first recipe
and so of course this needs to happen in a sequence
because we can only load a related recipe if we already
know the publisher that we're looking for
and so we need to do this in these three steps
just like we did before.
So again return a new promise.
Then resolve and reject and again this is always
the same here and oh I forgot to put the parentheses here
for the arrow function.
And then again here the set time out.
Let's just say pub here.
After 1.5 seconds, so 1500 milliseconds
and then the publisher.
So, just the same logic as before.
So this publisher is the one that we get here
as an input to this function which will then be passed
into the call back function of the set time out
and will then be called pub, okay?
And now I'm just going to copy the code that we have
up here again.
And simply paste it here.
And again, we don't want to log it to the console
but we want to resolve this promise
and return this recipe here.
So recipe and let's call the recipe here as well
and let's actually also use this pub argument.
I think up there we didn't actually use it
but let's do that down here.
So let's use a template literal here again.
And say pub and then show the recipe here.
So the Italian pizza.
Okay, and so that should get the job done,
and so now in order to consume this promise
I'm going to do it in the exact same way as before.
So we receive our recipe here and now I will return
the result of calling get related.
So get related and what we pass in here then is
the recipe dot publisher, right?
So recipe dot publisher because that's
in this case here Jonas, right?
So we get Jonas from our recipe and then pass it into
the get related function and so all of this here will
return a new promise and so just like before
we can then use the then method on it.
And again, we receive a recipe and so I can call it
recipe again because it's a different function
than this one here and so I can simply reuse this name.
And so let's again log this recipe to the console.
Give it a save and let's try it out.
So after one and a half second this one
then the first recipe and then the related recipe
and now we get an error here saying undefined
and then this weird object object
and I think I know why this is happening
and that's because our recipe here
actually returns a string, okay?
So you see here that we actually resolve this entire
string here and not just the recipe object
and so therefore we can then not read
a recipe dot publisher, okay?
And so instead I'm simply going to add Jonas here.
Okay, so in this case it doesn't really matter
and so that solves this first part here.
Then after that I have object object
and that's because here I put this entire object here
into a string but instead I just want recipe dot title
just like we have here in this line of code as well.
Okay, and if I do this now then it should work
and now finally, yeah, so Jonas, Italian pizza.
I could of course pass something else here
just like my entire name.
And so one and a half and one and a half
and finally another one and a half later
we get this entire result, okay?
So this is how we do the same thing that we did before
with the call back hell with promises.
So instead of having this really messy
and unmanageable code which is actually a lot less code
here but it still is really unmanageable especially
if we had more code and deeper nested call backs.
Then it would become really like a nightmare to maintain
and instead we have very nicely separated code here
like this where we produced all of our promises
and then in the end we simply consume them here
with this chain of thens and finally then a catch
to catch if there is any error.
And that's it about promises.
Woo, this was a lot to take in and I actually I find
promises to be one of the hardest if not the hardest
concept to grasp in JavaScript.
And so, please don't worry if you didn't really understand
it 100% on this point, okay?
Because what really matters is actually how we consume
promises and not so much how we produce,
because that is oftentimes already included in some
kind of libraries that we're gonna use later in the course.