ECMAScript: State of the Asynchronism
A synchronous operation blocks a process till the operation completes. An asynchronous operation is non-blocking and only initiates the…
A synchronous operation blocks a process till the operation completes. An asynchronous operation is non-blocking and only initiates the operation. Completion could be discovered by some other methods.
To understand the underlying workings of asynchronism in Javascript, let's talk first about how promises uncover this completion method.
Introduced in ES(2015).
A Promise you say?
Exactly what it means, in simple words the Promise
object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.var promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});promise1.then(function(value) {
console.log(value);
// expected output: "foo"
});console.log(promise1);
// expected output: [object Promise]
Explained in more detail from the docs
A Promise
is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.
A Promise
is in one of these states:
- pending: initial state, neither fulfilled nor rejected.
- fulfilled: meaning that the operation completed successfully.
- rejected: meaning that the operation failed.
A pending promise can either be fulfilled with a value or rejected with a reason (error). When either of these options happens, the associated handlers queued up by a promise’s then
method are called. (If the promise has already been fulfilled or rejected when a corresponding handler is attached, the handler will be called, so there is no race condition between an asynchronous operation completing and its handlers being attached.)
As the Promise.prototype.then()
and Promise.prototype.catch()
methods return promises, they can be chained
Async / Await
Introduced in ES2017, the Async/Await
function declaration defines an asynchronous function, which returns an AsyncFunction object. An asynchronous function is a function which operates asynchronously via the event loop, using an implicit Promise to return its result.
But the syntax and structure of your code using async functions are much more like using standard synchronous functions.function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}async function asyncCall() {
console.log('calling');
var result = await resolveAfter2Seconds();
console.log(result);
// expected output: 'resolved'
}asyncCall();
Explained in more detail from the docs
An async
function can contain an await
expression that pauses the execution of the async function and waits for the passed Promise
's resolution, and then resumes the async
function's execution and returns the resolved value.
Remember, the await
keyword is only valid inside async
functions. If you use it outside of an async
function's body, you will get a SyntaxError
.
Try / Catch
The try...catch
statement marks a block of statements to try, and specifies a response, should an exception be thrown.
function getProcessedData(url) {
return downloadData(url) // returns a promise
.catch(e => {
return downloadFallbackData(url) // returns a promise
})
.then(v => {
return processDataInWorker(v); // returns a promise
});
}
This can be rewritten as
async function getProcessedData(url) {
let v;
try {
v = await downloadData(url);
} catch(e) {
v = await downloadFallbackData(url);
}
return processDataInWorker(v);
}
Footnote: ES(2019) introduced the optional catch binding, which enables to simply omit the variable including the parenthesis after catch
Explained in more detail from the docs
The try
statement consists of a try
block, which contains one or more statements. {}
must always be used, even for single statements. At least one catch
clause, or a finally
clause, must be present. This gives us three forms for the try
statement:
try...catch
try...finally
try...catch...finally
A catch
clause contains statements that specify what to do if an exception is thrown in the try
block. That is, you want the try
block to succeed, and if it does not succeed, you want to pass control to the catch
block. If any statement within the try
block (or in a function called from within the try
block) throws an exception, control is immediately shifted to the catch
clause. If no exception is thrown in the try
block, the catch
clause is skipped.
The finally
clause executes after the try
block and catch
clause(s) execute but before the statements following the try
statement. It always executes, regardless of whether an exception was thrown or caught.
You can nest one or more try
statements. If an inner try
statement does not have a catch
clause, the enclosing try
statement's catch
clause is entered.
In conclusion
Handling asynchronous code can be a pain sometimes, but if you really want to master this important skill, practicing and understanding these key concepts will help you achieve that.