1. A simple test method that uses fetch() to retrieve a URL with a sleep delay (discards the response) and print a debug message.
    async function delay(ms, msg) {
      const res = await fetch(`https://httpstat.us/200?sleep=${ms}`);
      return msg;
    }
    
  2. Print with a 2 second delay:
    console.log(await delay(2000, 'One'));
    // One   (2 seconds have passed)
    
  3. Printing three lines results in 4 seconds execution time:
    console.log(await delay(2000, 'One'));
    console.log(await delay(1000, 'Two'));
    console.log(await delay(1000, 'Three'));
    // One   (2 seconds have passed)
    // Two   (3 seconds have passed)
    // Three (4 seconds have passed)
    
  4. Printing all three lines in parallel results in 2 seconds execution time but the output is not ordered:
    delay(2000, 'One').then(function(r) { console.log(r) });
    delay(1000, 'Two').then(function(r) { console.log(r) });
    delay(1000, 'Three').then(function(r) { console.log(r) });
    // Two
    // Three
    // One
    // (2 seconds have passed)
    

    Or the same result using async/await:

    (async() => console.log(await delay(2000, 'One')))();
    (async() => console.log(await delay(1000, 'Two')))();
    (async() => console.log(await delay(1000, 'Three')))();
    
  5. Printing all three lines in parallel results in 2 seconds execution time, but the output is ordered:
    await Promise.all([
      delay(2000, 'One'),
      delay(1000, 'Two'),
      delay(1000, 'Three')
    ]);
    // ["One, Two, Three"]
    // 2 seconds have passed
    

Notes on async/await

The async pattern is viral. That means if you have a child function that’s async, the parent function and all parent functions above it end up being async:

async function a() { return await delay(2000, 'One') }
async function b() { return await a() }
async function c() { return await b() }
console.log(await c());
// One   (2 seconds have passed)

However, when removing async from c():

async function a() { return await delay(2000, 'One') }
async function b() { return await a() }
function c() { return b() }
console.log(await c());
// Promise<pending>

The above doesn’t work. However, there are certain cases where this is fine for example if you don’t care to grab a return value:

async function a() { return await delay(2000, 'One') }
async function b() { console.log(await a()) }
await b();
// One   (2 seconds have passed)

It may be useful for fire-and-forget tasks like updateDatabase() or running a daemon but generally you’ll end up with async functions all the way up the call stack. There is no way pragmatic way to break out of this pattern.