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

JavaScript Promises

ECMAScript (ES), developed by ECMA International, has evolved since its first edition in 1997, with significant updates including ES5 in 2009 and ES6 in 2015. JavaScript handles asynchronous operations through callbacks, promises, and async/await, with promises providing a structured way to manage asynchronous tasks and ensuring that code execution waits for prior tasks to complete. The document also discusses the importance of promise chaining and the async/await syntax for more readable asynchronous code.

Uploaded by

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

JavaScript Promises

ECMAScript (ES), developed by ECMA International, has evolved since its first edition in 1997, with significant updates including ES5 in 2009 and ES6 in 2015. JavaScript handles asynchronous operations through callbacks, promises, and async/await, with promises providing a structured way to manage asynchronous tasks and ensuring that code execution waits for prior tasks to complete. The document also discusses the importance of promise chaining and the async/await syntax for more readable asynchronous code.

Uploaded by

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

JavaScript / ECMAScript

• ECMAScript (ES) was developed by ECMA (European Computer


Manufacturers Association)International after the organization
adopted JavaScript.
• The first edition of ECMAScript was released in 1997.
• The ES standard version is often updated to include new features.
• Versions of ES and the release year include: -
• -ES5 in 2009 or ECMAScript 2009
• ▪ES6 in 2015
• ▪ES7 in 2016
• ▪ES8 in 2017
• ▪ES9 in 2018
• Async in JavaScript -
• In the browser, JavaScript is single-threaded, so only one
task can run at a time.
• •If tasks are run synchronously, the UI is blocked and the
website becomes unresponsive.
• •JavaScript I/O functions (write to disk and network calls)
are non-blocking because they run asynchronously.
• •There are three common patterns to consume
asynchronous functions in JavaScript:
• ▪Callbacks
• ▪Promises
• ▪Async/await
• These common patterns allow asynchronous functions to
run in a deterministic manner.
• Callbacks -
• A callback function is passed as an argument into another function, which is invoked inside the other function to complete an
action.
• •A callback is a language feature of ES5 (2009).
• Example –
<!DOCTYPE html>
<html>
<body>
<script>
function done()
{
console.log('Begin messing things up again');
}
function cleanRoom(listOfTasks, doneCallback)
{
for(let i = 0; i<listOfTasks.length; i++)
{
console.log(listOfTasks[i]);
}
doneCallback();
}
cleanRoom(['1. Collect things off the floor', '2. Bin Trash'], done);
</script>
</body>
</html>

O/p_ -
1. Collect things off the floor
2. Bin Trash
3. Begin messing things up again
• The code example shows the function cleanRoom
receiving an array of strings as the first argument and a
function as the second argument. The cleanRoom
function returns only after the done function returns.

• The callback signature convention -


• Callbacks are simple to use and are supported widely
across browsers.
• •The Node.js community uses an error-first callback
signature, which means the following:
• ▪The first argument of a callback is reserved for an error
object.
• ▪The second argument of a callback is reserved for any
valid response data.
• Callback hell -
• Nesting callbacks within one another can lead
to callback hell.
• •Callback hell is required in cases where you
want to ensure a specific sequence.
• •Callback hell, also known as the Pyramid of
Doom, is an anti-pattern that makes code hard
to read and debug.
function logAfterDelay(message, cb) {
setTimeout(function()
{
console.log(message);
cb();
}, 500)
}

logAfterDelay('walk to the fish pond', function(){


logAfterDelay('Feed the fish', function(){
logAfterDelay('Match fish eat their food', function(){
logAfterDelay('Stand there and do nothing', function(){

});
});
});
});

O/P –
walk to the fish pond
Feed the fish
Match fish eat their food
Stand there and do nothing
• The example shows the logAfterDelay
function, which receives a string of “Walk to
the fish pond” as the first argument and a new
instance of the logAfterDelay function as the
second argument. As the number of nested
functions grows, the code becomes harder to
read.
• The programmer is faced with the challenge of
splitting code into separate functions to obey
the single responsibility principle and for
reusability, and balancing these items against
the readability of the code.
JavaScript: Learn Promises
Why do we need Promises?

• Promises (like callbacks) allow us to wait on certain


code to finish execution prior to running the next bit of
code.
• Why is this important? Think about a website that
loads data from an API then processes and formats the
data to display to the user. If we try to process and
format our data before the API has even fetched the
information, we’re either going to get an error or a
blank website. By using a Promise, we can ensure that
the API data isn’t processed/formatted until after our
API call has succeeded.
• The constructor syntax for a promise object
is:-
let promise = new Promise(function(resolve,
reject) {
// executor (success , error)
});
• The function passed to new Promise is called
the executor. When the promise is created,
this executor function runs automatically. It
contains functions that runs depending upon
whether a promise is successful or not.
• The resulting promise object has internal properties:
• state — initially “pending”, then changes to either
“fulfilled” or “rejected”,
• result — an arbitrary value, initially undefined.
• When the executor finishes the job, it should call one of
the functions that it gets as arguments:
• resolve(value) — to indicate that the job finished
successfully:
– sets state to "fulfilled",
– sets result to value.
• reject(error) — to indicate that an error occurred:
– sets state to "rejected",
– sets result to error.
• Here’s an example of a Promise constructor and a simple executor
function with its “producing code” (the setTimeout):

let promise = new Promise(function(resolve, reject) {


// the function is executed automatically when the promise is constructed

// after 1 second signal that the job is done with the result "done"
setTimeout(() => resolve("done"), 1000);
});
• We can see two things by running the code above:
• The executor is called automatically and immediately (by
the new Promise).
• The executor receives two arguments: resolve and reject —
these functions are pre-defined by the JavaScript engine. So
we don’t need to create them. We only should call one of
them when ready.
• After one second of “processing” the executor
calls resolve("done") to produce the result:
• That was an example of a successful job completion, a
“fulfilled promise”.
• And now an example of the executor rejecting the promise
with an error:
let promise = new Promise(function(resolve, reject) {
// after 1 second signal that the job is finished with an error
setTimeout(() => reject(new Error("Whoops!")), 1000);
});

To summarize, the executor should do a job (something that


takes time usually) and then call resolve or reject to change
the state of the corresponding Promise object.
• The Promise that is either resolved or rejected is called
“settled”, as opposed to a initially “pending” Promise.
• There can be only a single result or an error -
• The executor should call only one resolve or one reject. The
promise’s state change is final.
• All further calls of resolve and reject are ignored:-
let promise = new Promise(function(resolve, reject) {
resolve("done");

reject(new Error("…")); // ignored


setTimeout(() => resolve("…")); // ignored
});
• The idea is that a job done by the executor may have only one
result or an error.
• Also, resolve/reject expect only one argument (or none) and
will ignore additional arguments.
• Reject with Error objects
• In case something goes wrong, we can call reject with any type of
argument (just like resolve). But it is recommended to
use Error objects (or objects that inherit from Error).
• Immediately calling resolve/reject
• In practice, an executor usually does something asynchronously
and calls resolve/reject after some time, but it doesn’t have to.
We also can call resolve or reject immediately, like this:
let promise = new Promise(function(resolve, reject) {
// not taking our time to do the job
resolve(123); // immediately give the result: 123
});
• The state and result are internal
• The properties state and result of the Promise object are internal.
We can’t directly access them from our “consuming code”. We
can use the methods .then/.catch/.finally for that. They are
described below.
Consumers: then, catch, finally

• A Promise object serves as a link between the executor and


the consuming functions , which will receive the result or
error. Consuming functions can be registered (subscribed)
using methods .then, .catch and .finally.
• Then -
• The most important, fundamental one is .then.
• The syntax is:-
promise.then(
function(result) { /* handle a successful result */ },
function(error) { /* handle an error */ }
);
• The first argument of .then is a function that: runs when the
promise is resolved, and receives the result.
• The second argument of .then is a function that: runs when the
promise is rejected, and receives the error.
• For instance, here’s a reaction to a successfully resolved
promise:-

let promise = new Promise(function(resolve, reject) {


setTimeout(() => resolve("done!"), 1000);
});

// resolve runs the first function in .then


promise.then(
Or – function(result){alert
result => alert(result), // shows "done!" after 1 second
function(error){alert
error => alert(error) // doesn't run
);
• The first function was executed.
• And in the case of a rejection – the second one:-
let promise = new Promise(function(resolve, reject) {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// reject runs the second function in .then


promise.then(
result => alert(result), // doesn't run
error => alert(error) // shows "Error: Whoops!" after 1 second
);
• If we’re interested only in successful completions, then we
can provide only one function argument to .then:-
let promise = new Promise(resolve => {
setTimeout(() => resolve("done!"), 1000);
});

promise.then(alert); // shows "done!" after 1 second

• Catch -
• If we’re interested only in errors, then we can use null as the first
argument: .then(null, errorHandlingFunction). Or we can
use .catch(errorHandlingFunction), which is exactly the same:-

let promise = new Promise((resolve, reject) => {


setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// .catch(f) is the same as promise.then(null, f)


promise.catch(alert); // shows "Error: Whoops!" after 1 second

• The call .catch(f) is a complete analog of .then(null, f), it’s just a shorthand.
• Finally -
• Just like there’s a finally clause in a regular try {...} catch {...},
there’s finally in promises.
• The call .finally(f) is similar to .then(f, f) in the sense that it
always runs when the promise is settled: be it resolve or reject.
• finally is a good handler for performing cleanup, e.g. stopping
our loading indicators, as they are not needed anymore, no
matter what the outcome is.
• Like this: -
new Promise((resolve, reject) => {
/* do something that takes time, and then call resolve/reject */
})
// runs when the promise is settled, doesn't matter successfully
or not
.finally(() => stop loading indicator)
.then(result => show result, err => show error)
Promises chaining

• We have a sequence of asynchronous tasks to


be done one after another. For instance,
loading scripts. How can we code it well?
• Promises provide a couple of recipes to do
that - promise chaining.
• It looks like this:-
new Promise(function(resolve, reject) {

setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

alert(result); // 1
return result * 2;

}).then(function(result) { // (***)

alert(result); // 2
return result * 2;

}).then(function(result) {

alert(result); // 4
return result * 2;

});
• The idea is that the result is passed through
the chain of .then handlers.
• Here the flow is:
• The initial promise resolves in 1 second (*),
• Then the .then handler is called (**).
• The value that it returns is passed to the
next .then handler (***)
• …and so on.
• As the result is passed along the chain of
handlers, we can see a sequence
of alert calls: 1 → 2 → 4.
• The whole thing works, because a call to promise.then returns a promise, so that we
can call the next .then on it.
• When a handler returns a value, it becomes the result of that promise, so the
next .then is called with it.
• To make these words more clear, here’s the start of the chain:
new Promise(function(resolve, reject) {

setTimeout(() => resolve(1), 1000);

}).then(function(result) {

alert(result);
return result * 2; // <-- (1)

}) // <-- (2)
// .then…

• The value returned by .then is a promise, that’s why we are able to add
another .then at (2). When the value is returned in (1), that promise becomes
resolved, so the next handler runs with the value.
• A classic newbie error: technically we can also add many .then to a single promise.
This is not chaining.
• For example:
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000);
});

promise.then(function(result) {
alert(result); // 1
return result * 2;
});

promise.then(function(result) {
alert(result); // 1
return result * 2;
});

promise.then(function(result) {
alert(result); // 1
return result * 2;
});
• What we did here is just several handlers to one promise.
They don’t pass the result to each other, instead they process
it independently.
• Here’s the picture (compare it with the chaining above):

• All .then on the same promise get the same result – the result
of that promise. So in the code above all alert show the
same: 1.
• In practice we rarely need multiple handlers for one promise.
Chaining is used much more often.
• Returning promises
• Normally, a value returned by a .then handler is immediately passed to the
next handler. But there’s an exception.
• If the returned value is a promise, then the further execution is suspended
until it settles. After that, the result of that promise is given to the
next .then handler.
• For instance:
new Promise(function(resolve, reject) {

setTimeout(() => resolve(1), 1000);

}).then(function(result) {

alert(result); // 1

return new Promise((resolve, reject) => { // (*)


setTimeout(() => resolve(result * 2), 1000);
});

}).then(function(result) { // (**)

alert(result); // 2

return new Promise((resolve, reject) => {


setTimeout(() => resolve(result * 2), 1000);
});

}).then(function(result) {

alert(result); // 4

});
• Here the first .then shows 1 and returns new
Promise(…) in the line (*). After one second it
resolves, and the result (the argument
of resolve, here it’s result*2) is passed on to
handler of the second .then in the line (**). It
shows 2 and does the same thing.
• So the output is again 1 → 2 → 4, but now
with 1 second delay between alert calls.
• Returning promises allows us to build chains
of asynchronous actions.
Async/await

• There’s a special syntax to work with promises in a more comfortable


fashion, called “async/await”. It’s surprisingly easy to understand and use.
• Async functions -
• Let’s start with the async keyword. It can be placed before a function, like
this:

async function f() {


return 1;
}

• The word “async” before a function means one simple thing: a function
always returns a promise. Even If a function actually returns a non-promise
value, prepending the function definition with the “async” keyword directs
JavaScript to automatically wrap that value in a resolved promise.
• For instance, the code above returns a resolved promise with
the result of 1, let’s test it:-

async function f() {


return 1;
}

f().then(alert); // 1
• We could explicitly return a promise, that would be the same
as:-

async function f() {


return Promise.resolve(1);
}

f().then(alert); // 1
• So, async ensures that the function returns a
promise, and wraps non-promises in it. Simple
enough, right? But not only that. There’s another
keyword, await, that works only
inside async functions, and it’s pretty cool.
• Await -
• The syntax:-
// works only inside async functions
let value = await promise;
• The keyword await makes JavaScript wait until
that promise settles and returns its result.
• Here’s an example with a promise that resolves in
1 second:
async function f() {

let promise = new Promise((resolve, reject) => {


setTimeout(() => resolve("done!"), 1000)
});

let result = await promise; // wait till the promise


resolves (*)

alert(result); // "done!"
}

f();
• The function execution “pauses” at the
line (*) and resumes when the promise settles,
with result becoming its result. So the code
above shows “done!” in one second.
• Let’s emphasize: await literally makes
JavaScript wait until the promise settles, and
then go on with the result. That doesn’t cost
any CPU resources, because the engine can do
other jobs meanwhile: execute other scripts,
handle events etc.
• It’s just a more elegant syntax of getting the
promise result than promise.then, easier to
read and write.
• Can’t use await in regular functions
• If we try to use await in non-async function,
there would be a syntax error:
function f() {
let promise = Promise.resolve(1);
let result = await promise; // Syntax error
}

• We will get this error if we do not


put async before a function. As
said, await only works inside an async
function.

You might also like