Materi Basic Programming #11
Materi Basic Programming #11
Let’s construct a promise! To create a new Promise object, we use the new keyword and the Promise constructor method:
The Promise constructor method takes a function parameter called the executor function which runs automatically when the
constructor is called. The executor function generally starts an asynchronous operation and dictates how the promise should be
settled.
The executor function has two function parameters, usually referred to as the resolve() and reject() functions. The resolve() and
reject() functions aren’t defined by the programmer. When the Promise constructor runs, JavaScript will pass its own resolve() and
reject() functions into the executor function.
● resolve is a function with one argument. Under the hood, if invoked, resolve() will change the promise’s status from pending to
fulfilled, and the promise’s resolved value will be set to the argument passed into resolve().
● reject is a function that takes a reason or error as an argument. Under the hood, if invoked, reject() will change the promise’s
status from pending to rejected, and the promise’s rejection reason will be set to the argument passed into reject().
Let’s look at an example executor function in a Promise constructor:
In our example, myFirstPromise resolves or rejects based on a simple condition, but, in practice, promises settle
based on the results of asynchronous operations. For example, a database request may fulfill with the data from a
query or reject with an error thrown. In this exercise, we’ll construct promises which resolve synchronously to more
easily understand how they work.
Example
The Node setTimeout() Function
Knowing how to construct a promise is useful, but most of the time, knowing how to consume, or use,
promises will be key. Rather than constructing promises, you’ll be handling Promise objects returned to
you as the result of an asynchronous operation. These promises will start off pending but settle
eventually.
Moving forward, we’ll be simulating this by providing you with functions that return promises which
settle after some time. To accomplish this, we’ll be using setTimeout(). setTimeout() is a Node API (a
comparable API is provided by web browsers) that uses callback functions to schedule tasks to be
performed after a delay. setTimeout() has two parameters: a callback function and a delay in
milliseconds.
Here, we invoke setTimeout() with the callback function
delayedHello() and 2000. In at least two seconds
delayedHello() will be invoked. But why is it “at least” two
seconds and not exactly two seconds?
Example
Consuming Promises
The initial state of an asynchronous promise is pending, but we have a
guarantee that it will settle. How do we tell the computer what should
happen then? Promise objects come with an aptly named .then() method. It
allows us to say, “I have a promise, when it settles, then here’s what I want
to happen…”
In the case of our dishwasher promise, the dishwasher will run then:
● If our promise rejects, this means we have dirty dishes, and we’ll add
soap and run the dishwasher again.
● If our promise fulfills, this means we have clean dishes, and we’ll put the
dishes away.
We can invoke .then() with one, both, or neither handler! This allows for
flexibility, but it can also make for tricky debugging. If the appropriate
handler is not provided, instead of throwing an error, .then() will just return a
promise with the same settled value as the promise it was called on. One
important feature of .then() is that it always returns a promise. We’ll return
to this in more detail in a later exercise and explore why it’s so important.
Success and Failure Callback Functions
The .catch() function takes only one argument, onRejected. In the case of a
rejected promise, this failure handler will be invoked with the reason for rejection.
Using .catch() accomplishes the same thing as using a .then() with only a failure
handler.
Let’s break down what’s happening in the example code:
● prom is a promise which randomly either resolves with 'Yay!' or rejects with 'Ohhh
noooo!'.
● We pass a success handler to .then() and a failure handler to .catch().
● If the promise resolves, .then()‘s success handler will be invoked with 'Yay!'.
● If the promise rejects, .then() will return a promise with the same rejection reason
as the original promise and .catch()‘s failure handler will be invoked with that
rejection reason.
Example
Chaining Multiple Promises
One common pattern we’ll see with asynchronous programming is multiple operations which
depend on each other to execute or that must be executed in a certain order. We might make
one request to a database and use the data returned to us to make another request and so on!
Let’s illustrate this with another cleaning example, washing clothes:
We take our dirty clothes and put them in the washing machine. If the clothes are cleaned,
then we’ll want to put them in the dryer. After the dryer runs, if the clothes are dry, then we can
fold them and put them away.
This process of chaining promises together is called composition. Promises are designed
with composition in mind!
Let’s break down what’s happening in the example:
In order for our chain to work properly, we had to return the promise
secondPromiseFunction(firstResolveVal). This ensured that the return value
of the first .then() was our second promise rather than the default return of a
new promise with the same settled value as the initial.
Avoiding Common Mistakes
Promise composition allows for much more readable code than the nested
callback syntax that preceded it. However, it can still be easy to make mistakes.
In this exercise, we’ll go over two common mistakes with promise composition.
Mistake 1:
Nesting promises instead
of chaining them.
When done correctly, promise composition is a great way to handle situations where asynchronous operations depend on each other or
execution order matters. What if we’re dealing with multiple promises, but we don’t care about the order? Let’s think in terms of
cleaning again.
For us to consider our house clean, we need our clothes to dry, our trash bins emptied, and the dishwasher to run. We need all of these
tasks to complete but not in any particular order. Furthermore, since they’re all getting done asynchronously, they should really all be
happening at the same time!
To maximize efficiency we should use concurrency, multiple asynchronous operations happening together. With promises, we can do
this with the function Promise.all().
Promise.all() accepts an array of promises as its argument and returns a single promise. That single promise will settle in one of two
ways:
● If every promise in the argument array resolves, the single promise returned from Promise.all() will resolve with an array containing
the resolve value from each promise in the argument array.
● If any promise from the argument array rejects, the single promise returned from Promise.all() will immediately reject with the
reason that promise rejected. This behavior is sometimes referred to as failing fast.
Let’s break down what’s happening:
● We declare myPromises assigned to invoking Promise.all().
● We invoke Promise.all() with an array of three promises— the returned
values from functions.
● We invoke .then() with a success handler which will print the array of
resolved values if each promise resolves successfully.
● We invoke .catch() with a failure handler which will print the first rejection
message if any promise rejects.
Example
● Promises are JavaScript objects that represent the eventual result of
an asynchronous operation.
Review ●
●
Promises can be in one of three states: pending, resolved, or rejected.
A promise is settled if it is either resolved or rejected.
● We construct a promise by using the new keyword and passing an
Promises executor function to the Promise constructor method.
● setTimeout() is a Node function which delays the execution of a
callback function using the event-loop.
● We use .then() with a success handler callback containing the logic for
what should happen if a promise resolves.
● We use .catch() with a failure handler callback containing the logic for
what should happen if a promise rejects.
● Promise composition enables us to write complex, asynchronous code
that’s still readable. We do this by chaining multiple .then()‘s
and .catch()‘s.
● To use promise composition correctly, we have to remember to return
promises constructed within a .then().
● We should chain multiple promises rather than nesting them.
● To take advantage of concurrency, we can use Promise.all().