Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at JavaScript async programming.
Sync and Async
The synchronous code always runs before the async code.
So if we have:
setTimeout(function() {
console.log(2);
}, 0);
console.log(1);
Then we get:
1
2
The console log outside is synchronous, so it runs first.
The callback’s console log runs after the synchronous code, since it’s async.
The event loop may be blocked if we run something that’s synchronous since the whole app is managed by a single process.
Both the user interface and other computations are all in one thread.
So if one thing runs, then whatever comes after it can’t run until that thing is done.
For instance, if we have a synchronous sleep
function, then that’ll pause the execution of the whole program.
If we have:
function sleep(ms) {
const start = Date.now();
while ((Date.now() - start) < ms);
}
console.log('start');
sleep(2000);
console.log('end');
Then we see 'start'
logged first, then wait 2 seconds, then 'end'
is logged.
The sleep
function uses a while
loop, so it’s synchronous.
This means it’ll hold up the whole program from running.
Avoiding Blocking
To avoid blocking the UI thread, we can use different kinds of async code like web workers, or setTimeout
or Promises.
One example of async code is the Fetch API.
We can get data by making HTTP requests without blocking the whole thread.
For instance, we can write:
fetch('https://api.agify.io/?name=michael')
.then(res => res.json())
.then(res => {
console.log(res);
})
to use the fetch
function to get the data.
It returns a promise so it’s async.
Then then
callbacks are called only when the results are ready.
When it’s not the promise is paused until a result is obtained.
Asynchronous Results
Other examples, include Node.js async callbacks.
For example, the readFile
method takes a callback that’s run asynchronously when the file is read.
We can write:
fs.readFile('foo.txt', {
encoding: 'utf8'
},
function(error, text) {
if (error) {
// ...
}
console.log(text);
});
Then we read file.txt
from with readFile
.
Callbacks
Callbacks are problematic because error handling is complicated.
We’ve to handle errors at each callback.
The signatures are also less elegant because there’s no separation of concerns between inputs and outputs.
Async functions use callback functions, which can’t return anything.
Composition is also more complicated.
Node style callbacks some problems.
We’ve to check for errors with if
statements.
Reusing error handlers is harder.
And providing a default handler is also harder.
Promises
Promises are a pattern of async programming where a single result is returned asynchronously.
They’re better than callbacks since they can be chained.
Promises are returned by various functions and serve as the placeholder for the final result.
For instance, a promise chain may be written by writing:
asyncFunction(arg)
.then(result => {
console.log(result);
});
We can have more than one then
call if the then
callback returns a promise.
For instance, we can write:
asyncFunction(arg)
.then(result1 => {
console.log(result1);
return asyncFunction2(x, y);
})
.then(result2 => {
console.log(result2);
});
Conclusion
Promises are a better way to write async code in JavaScript.