Subscribe to my email list now at http://jauyeung.net/subscribe/
Follow me on Twitter at https://twitter.com/AuMayeung
Many more articles at https://medium.com/@hohanga
Even more articles at http://thewebdev.info/
Concurrency is an important part of most modern programs. To achieve this in JavaScript, we have to use asynchronous code, which is non-blocking.
In this article, we’ll look at how to write asynchronous code in a way that’s clean and easy to read and change.
Use Promises Instead of Callbacks
Promises have been a standard object since ES6, so the previous asynchronous callbacks should all be replaced with promises.
Using callbacks is a real pain if we have any sequential code since we have to nest them on multiple levels.
For example, if we want to run multiple setTimeout
callbacks without promises, then we have to nest them as follows:
setTimeout(() => {
console.log('foo');
setTimeout(() => {
console.log('bar');
setTimeout(() => {
console.log('baz');
}, 200)
}, 200)
}, 200)
As we can see, we only have three callbacks and the nesting is already very ugly. We have to clean this up so that this is more pleasant to look at and easier to understand.
We can do this with promises as follows:
const timeOutPromise = (str) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(str);
}, 200)
})
}
timeOutPromise('foo')
.then((val) => {
console.log(val);
return timeOutPromise('bar');
})
.then((val) => {
console.log(val);
return timeOutPromise('baz');
})
.then((val) => {
console.log(val);
})
As we can see, with promises, we can chain them with the then
method with a callback passed into it. We don’t have to nest callbacks except in the timeoutPromise
function, and it’s only two levels of nesting instead of three or more.
We get the resolves value of a promise in the parameter of the callback that we pass into the then
method.
To catch errors, we can use the catch
method with a callback passed in as follows:
const timeOutPromise = (str) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(str);
}, 200)
})
}
timeOutPromise('foo')
.then((val) => {
console.log(val);
return timeOutPromise('bar');
})
.then((val) => {
console.log(val);
return timeOutPromise('baz');
})
.then((val) => {
console.log(val);
})
.catch((err) => console.error(err))
Async/Await Is a Cleaner Syntax for Chaining Promises
ES2017 introduced the async
and await
syntax, which is a cleaner way of chaining promises.
We can rewrite what we had above by writing:
const timeOutPromise = (str) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(str);
}, 200)
})
}
(async () => {
let val;
val = await timeOutPromise('foo');
console.log(val);
val = await timeOutPromise('bar');
console.log(val);
val = await timeOutPromise('baz');
console.log(val);
})();
It’s exactly the same as:
const timeOutPromise = (str) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(str);
}, 200)
})
}
timeOutPromise('foo')
.then((val) => {
console.log(val);
return timeOutPromise('bar');
})
.then((val) => {
console.log(val);
return timeOutPromise('baz');
})
.then((val) => {
console.log(val);
})
The one difference is that the resolved value is assigned to val
via the assignment operator. This assignment works as long as we have await
before our promises.
To handle rejected promises, we can use the try...catch
clause as we do with synchronous code:
const timeOutPromise = (str) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(str);
}, 200)
})
}
(async () => {
try {
let val;
val = await timeOutPromise('foo');
console.log(val);
val = await timeOutPromise('bar');
console.log(val);
val = await timeOutPromise('baz');
console.log(val);
} catch (err) {
console.error(err);
}
})();
async
functions only return promises, so we can’t use them as general-purpose functions. They’re syntactic sugar for promises and not a replacement of it.
Conclusion
To write asynchronous code, promises are the way to go. They let us chain multiple of them together without nesting callbacks.
We should convert asynchronous code to promises if they aren’t returned as promises already. To chain them, we can use the then
method.
To catch errors from rejected promises, we can use the catch
method.
async
and await
are syntactic sugar for promises. They’re the same thing but async
and await
is shorter.
We can use try...catch
to catch errors from rejected promises with async
and await
.
The post JavaScript Clean Code: Concurrency appeared first on The Web Dev.