Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
18 views

Materi Basic Programming #11

The document discusses promises in JavaScript. A promise represents the eventual completion or failure of an asynchronous operation. Promises can be constructed using the Promise constructor, which takes a function that resolves or rejects the promise. Asynchronous functions may return promises. Promises can be consumed using the .then() method, which takes success and failure callback functions. The success callback runs if the promise resolves, and the failure callback runs if the promise rejects.

Uploaded by

Aji Pradipto
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views

Materi Basic Programming #11

The document discusses promises in JavaScript. A promise represents the eventual completion or failure of an asynchronous operation. Promises can be constructed using the Promise constructor, which takes a function that resolves or rejects the promise. Asynchronous functions may return promises. Promises can be consumed using the .then() method, which takes success and failure callback functions. The success callback runs if the promise resolves, and the failure callback runs if the promise rejects.

Uploaded by

Aji Pradipto
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 25

Basic Programming (with Javascript) #11

Enggar Damar Saraswata


Catur Bagaskara
Mahdi Widianto
Review Callback
In JavaScript, a callback function is a
function that is passed into another
function as an argument. This function
can then be invoked during the
execution of that higher order function
(that it is an argument of).

Since, in JavaScript, functions are


objects, functions can be passed as
arguments.
What we will learn?
● Introduction
Promises ● What is a Promise?
● Constructing a Promise Object
● The Node setTimeout() Function
● Consuming Promises
● Success and Failure Callback Functions
● Using catch() with Promises
● Avoiding Commong Mistakes
● Using Promise.all()
Introduction
An asynchronous operation is one that allows the
computer to “move on” to other tasks while
waiting for the asynchronous operation to
complete. Asynchronous programming means
that time-consuming operations don’t have to
bring everything else in our programs to a halt.

There are countless examples of asynchronicity in


our everyday lives. Cleaning our house, for
example, involves asynchronous operations such
as a dishwasher washing our dishes or a washing
machine washing our clothes. While we wait on
the completion of those operations, we’re free to
do other chores.

Similarly, web development makes use of


asynchronous operations. Operations like making
a network request or querying a database can be
time-consuming, but JavaScript allows us to
execute other tasks while awaiting their
completion.
What is Promises?
Promises are objects that represent the eventual outcome of an
asynchronous operation. A Promise object can be in one of three states:
● Pending: The initial state— the operation has not completed yet.
● Fulfilled: The operation has completed successfully and the promise
now has a resolved value. For example, a request’s promise might
resolve with a JSON object as its value.
● Rejected: The operation has failed and the promise has a reason for
the failure. This reason is usually an Error of some kind.

We refer to a promise as settled if it is no longer pending— it is either


fulfilled or rejected. Let’s think of a dishwasher as having the states of a
promise:
● Pending: The dishwasher is running but has not completed the
washing cycle.
● Fulfilled: The dishwasher has completed the washing cycle and is full
of clean dishes.
● Rejected: The dishwasher encountered a problem (it didn’t receive
soap!) and returns unclean dishes.

If our dishwashing promise is fulfilled, we’ll be able to perform related


tasks, such as unloading the clean dishes from the dishwasher. If it’s
rejected, we can take alternate steps, such as running it again with soap or
washing the dishes by hand.

All promises eventually settle, enabling us to write logic for what to do if


the promise fulfills or if it rejects.
Constructing a Promise Object

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:

● We declare a variable myFirstPromise


● myFirstPromise is constructed using new Promise() which is the Promise constructor method.
● executorFunction() is passed to the constructor and has two functions as parameters: resolve and reject.
● If someCondition evaluates to true, we invoke resolve() with the string 'I resolved!'
● If not, we invoke reject() with the string 'I rejected!'

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?

This delay is performed asynchronously—the rest of our


program won’t stop executing during the delay.
Asynchronous JavaScript uses something called the event-
loop. After two seconds, delayedHello() is added to a line of
code waiting to be run. Before it can run, any synchronous
code from the program will run. Next, any code in front of it
in the line will run. This means it might be more than two
seconds before delayedHello() is actually executed.
In the example code, we invoked returnPromiseFunction() which returned a
promise. We assigned that promise to the variable prom. Similar to the
asynchronous promises you may encounter in production, prom will initially have
a status of pending.

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.

.then() is a higher-order function— it takes two callback functions as


arguments. We refer to these callbacks as handlers. When the promise
settles, the appropriate handler will be invoked with that settled value.
● The first handler, sometimes called onFulfilled, is a success handler, and
it should contain the logic for the promise resolving.
● The second handler, sometimes called onRejected, is a failure handler,
and it should contain the logic for the promise rejecting.

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

To handle a “successful” promise, or a promise that resolved, we


invoke .then() on the promise, passing in a success handler
callback function:

Let’s break down what’s happening in the example code:

● prom is a promise which will resolve to 'Yay!'.


● We define a function, handleSuccess(), which prints the
argument passed to it.
● We invoke prom‘s .then() function passing in our
handleSuccess() function.
● Since prom resolves, handleSuccess() is invoked with prom‘s
resolved value, 'Yay', so 'Yay' is logged to the console.
With typical promise consumption, we won’t know whether a
promise will resolve or reject, so we’ll need to provide the logic
for either case. We can pass both a success callback and a
failure callback to .then().

Let’s break down what’s happening in the example code:

● prom is a promise which will randomly either resolve


with 'Yay!' or reject with 'Ohhh noooo!'.
● We pass two handler functions to .then(). The first will be
invoked with 'Yay!' if the promise resolves, and the
second will be invoked with 'Ohhh noooo!' if the promise
rejects.
Success and Failure
Callback Functions
Note: The success callback is sometimes called the “success handler function”
or the onFulfilled function. The failure callback is sometimes called the “failure
handler function” or the onRejected function.
Using catch() with Promises

One way to write cleaner code is to follow a principle called


separation of concerns. Separation of concerns means
organizing code into distinct sections each handling a specific
task. It enables us to quickly navigate our code and know where
to look if something isn’t working.

Remember, .then() will return a promise with the same settled


value as the promise it was called on if no appropriate handler
was provided. This implementation allows us to separate our
resolved logic from our rejected logic. Instead of passing both
handlers into one .then(), we can chain a second .then() with a
failure handler to a first .then() with a success handler and both
cases will be handled.
Since JavaScript doesn’t mind whitespace, we follow a common convention of
putting each part of this chain on a new line to make it easier to read. To create
even more readable code, we can use a different promise function: .catch().

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:

● We invoke a function firstPromiseFunction() which returns a promise.


● We invoke .then() with an anonymous function as the success handler.
● Inside the success handler we return a new promise— the result of
invoking a second function, secondPromiseFunction() with the first
promise’s resolved value.
● We invoke a second .then() to handle the logic for the second promise
settling.
● Inside that .then(), we have a success handler which will log the second
promise’s resolved value to the console.

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.

Let’s break down what’s happening in the above


code:

● We invoke returnsFirstPromise() which


returns a promise.
● We invoke .then() with a success handler.
● Inside the success handler, we invoke
returnsSecondValue() with firstResolveVal
which will return a new promise.
● We invoke a second .then() to handle the
logic for the second promise settling all
inside the first then()!
● Inside that second .then(), we have a
success handler which will log the second
promise’s resolved value to the console.

Instead of having a clean chain of promises, we’ve


nested the logic for one inside the logic of the
other. Imagine if we were handling five or ten
promises!
Mistake 2:
Forgetting to return a
promise.

Let’s break down what’s happening in the


example:

● We invoke returnsFirstPromise() which


returns a promise.
● We invoke .then() with a success handler.
● Inside the success handler, we create our
second promise, but we forget to return it!
● We invoke a second .then(). It’s supposed
to handle the logic for the second promise,
but since we didn’t return, this .then() is
invoked on a promise with the same
settled value as the original promise!

Since forgetting to return our promise won’t throw


an error, this can be a really tricky thing to debug!
Using Promise.all()

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().

You might also like