Skip to content

Instantly share code, notes, and snippets.

@amerryma
Created December 10, 2024 14:50
Show Gist options
  • Save amerryma/1b661382697bcf3632612fa07c756695 to your computer and use it in GitHub Desktop.
Save amerryma/1b661382697bcf3632612fa07c756695 to your computer and use it in GitHub Desktop.
Return Await Gotchas

In JavaScript, the interaction between return, await, and finally can cause subtle issues that developers should be aware of. Here are some key gotchas:

1. finally and return interaction

  • The finally block runs regardless of whether a return, throw, or an exception occurs in try or catch.
  • If finally has a return, it overrides any return value or thrown error from try or catch.
function test() {
  try {
    return "from try";
  } catch (e) {
    return "from catch";
  } finally {
    return "from finally"; // Overrides the previous return
  }
}

console.log(test()); // "from finally"

2. await in finally

  • If await is used inside finally, the function execution is paused, and the result is returned only after the awaited promise resolves.
async function test() {
  try {
    return "from try";
  } finally {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    console.log("finally completed");
  }
}

test().then(console.log);
// Logs "finally completed" after 1 second
// Then logs "from try"

3. Rejections in finally

  • If finally throws an error or returns a rejected promise, it overrides any previous return or throw.
async function test() {
  try {
    return "from try";
  } finally {
    throw new Error("from finally");
  }
}

test().catch(console.error); 
// Logs: Error: from finally

4. Missing await in return

  • Forgetting await when returning a promise from try can cause unintended behavior if finally modifies the control flow.
async function test() {
  try {
    return Promise.resolve("from try"); // Missing await!
  } finally {
    console.log("finally executed");
  }
}

test().then(console.log); 
// Logs: "finally executed"
// Logs: "from try"

5. Async Functions in finally

  • If the finally block contains an await but you forget await in the try block’s return, the finally block may delay the completion of the function.
async function test() {
  try {
    return Promise.resolve("from try"); // Not awaited
  } finally {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    console.log("finally done");
  }
}

test().then(console.log); 
// Logs: "finally done" after 1 second
// Logs: "from try"

Best Practices

  1. Always await promises in try if you intend to wait for them.
  2. Avoid returning values from finally unless explicitly needed.
  3. Be aware that errors thrown in finally override anything from try or catch.
  4. Use finally for cleanup logic, not for altering return values or error states unless intentional.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment