5 Asynchronous Functions
5.1 Introduction
Modern languages now have interesting forms of control that go beyond traditional call-and-return, such as pausing the computation and being able to resume it. They make programs much more readable, elegant, and even performant.
Generators are one such control operator that is common in modern programming languages. In this assignment, we want you to learn another common one: async/await. We want you to learn enough about these to compare and contrast them, so you have them in your toolbox when you program in the future.
5.2 Exam Mode
For this homework, you are expected to do the work entirely on your own: you may consult with the course staff only for essential information (clarification of typos/errors, names of library functions, etc.: if in doubt you can always ask). In other words, you will do this in “exam mode”. Though some parts will be more challenging than others, we do believe you can do all the problems in this assignment.
5.3 Part 1: Understanding async/await
Your task is to understand asynchronous functions in JavaScript, particularly why the following two seemingly equivalent JavaScript programs below produce different outputs. Both programs record the starting time t0, execute two tasks, log the results, and print the time spent on the tasks. One task takes three seconds to complete, while the other takes five. The key detail is that taskThatTakesXSeconds is not an ordinary JavaScript function; it’s asynchronous. Calling an asynchronous function doesn’t immediately return the result. Instead, it returns a special value that can be resolved to the actual result using await. The only difference between the two programs lies in where the await is placed.
The first program:
// program 1 |
async function main() { |
let t0 = Date.now(); |
let task1 = await taskThatTakesXSeconds(3); |
let task2 = await taskThatTakesXSeconds(5); |
console.log(task1) |
pause() |
console.log(task2) |
console.log(Date.now() - t0); |
} |
|
main() |
The second program:
// program 2 |
async function main() { |
let t0 = Date.now(); |
let task1 = taskThatTakesXSeconds(3); |
let task2 = taskThatTakesXSeconds(5); |
console.log(await task1) |
pause() |
console.log(await task2) |
console.log(Date.now() - t0); |
} |
|
main() |
First, notice that these two programs look very similar—
To do so, we need to define pause and taskThatTakesXSeconds. The pause function simply returns 0; it becomes relevant in a later part of the assignment. The taskThatTakesXSeconds function simulates asynchronous tasks using setTimeout. In real-world programming, this function could represent operations like downloading files, waiting for user input, and so on.
function pause() { |
return 0; |
} |
|
async function taskThatTakesXSeconds(seconds) { |
// simulated with setTimeout so that you can run the program |
return new Promise((resume) => { |
setTimeout(() => resume("done"), seconds * 1000) |
}) |
} |
describe how each program behaves, and
explain why the programs behave differently.
5.4 Part 2: Implementation State
We want you to demonstrate your understanding of async/await by drawing the stack + heap configurations, in the style of the Stacker, for the two JavaScript programs. Below are templates for the stacker at the point of calling pause for each program above. You need to fill in each of the “Todo”s.
Program 1 template (click for enlarged image):
Program 2 template (click for enlarged image):
5.5 What To Hand In
A written program comparison (Part 1).
Eight responses for the holes in the Stacker templates (Part 2), four per template.