This chapter is a brief roadmap for asynchronous programming.
The next chapters explain asynchronous programming in JavaScript:
The remainder of this chapter gives you some first ideas of what all of that means.
Don’t worry about the details!
Don’t worry if you don’t understand everything yet. This is just a quick peek at what’s coming up. Everything is explained in much more detail in the next chapters.
Normal functions are synchronous: the caller waits until the callee is finished with its computation. divideSync()
in line A is a synchronous function call:
function main() {
try {
const result = divideSync(12, 3); // (A)
assert.equal(result, 4);
} catch (err) {
assert.fail(err);
}
}
By default, JavaScript tasks are functions that are executed sequentially in a single process. That looks like this:
while (true) {
const task = taskQueue.dequeue();
task(); // run task
}
This loop is also called the event loop because events, such as clicking a mouse, add tasks to the queue.
Due to this style of cooperative multitasking, we don’t want a task to block other tasks from being executed while, for example, it waits for results coming from a server. The next subsection explores how to handle this case.
What if divide()
needs a server to compute its result? Then the result should be delivered in a different manner: The caller shouldn’t have to wait (synchronously) until the result is ready; it should be notified (asynchronously) when it is. One way of delivering the result asynchronously is by giving divide()
a callback function that it uses to notify the caller.
function main() {
divideCallback(12, 3,
(err, result) => {
if (err) {
assert.fail(err);
} else {
assert.equal(result, 4);
}
});
}
When there is an asynchronous function call:
divideCallback(x, y, callback)
Then the following steps happen:
divideCallback()
sends a request to a server.
main()
is finished and other tasks can be executed.
An error err
: Then the following task is added to the queue.
taskQueue.enqueue(() => callback(err));
A result
value: Then the following task is added to the queue.
taskQueue.enqueue(() => callback(null, result));
Promises are two things:
Invoking a Promise-based function looks as follows.
function main() {
dividePromise(12, 3)
.then(result => assert.equal(result, 4))
.catch(err => assert.fail(err));
}
One way of looking at async functions is as better syntax for Promise-based code:
async function main() {
try {
const result = await dividePromise(12, 3); // (A)
assert.equal(result, 4);
} catch (err) {
assert.fail(err);
}
}
The dividePromise()
we are calling in line A is the same Promise-based function as in the previous section. But we now have synchronous-looking syntax for handling the call. await
can only be used inside a special kind of function, an async function (note the keyword async
in front of the keyword function
). await
pauses the current async function and returns from it. Once the awaited result is ready, the execution of the function continues where it left off.