async Functions and await

An async function always returns a promise: return x fulfills it and throw rejects it. await p suspends the function and returns control to the caller. When p settles, the function resumes as a microtask: await yields the value or throws the reason, so try...catch works. Arrows (async x => ...) and methods can be async too.

Sequential versus concurrent awaits, and try...catch around awaitJavaScriptLive
const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value));
async function time(label, fn) {
  const start = performance.now(), result = await fn();
  console.log(label, result, `~${Math.round((performance.now() - start) / 100) * 100} ms`);
}
await time("sequential", async () => (await delay(100, "a")) + (await delay(100, "b")));
const both = () => Promise.all([delay(100, "a"), delay(100, "b")]);   // both timers start now
await time("concurrent", async () => (await both()).join(""));
async function load() {
  try { await Promise.reject(new Error("offline")); }
  catch (err) { return `fallback: ${err.message}`; }
}
console.log(await load());

Start independent requests together instead of awaiting them one by one. array.forEach(async ...) waits for nothing; use for...of with await, or Promise.all(array.map(...)). Since ECMAScript 2022, modules may await at the top level.