Catching async errors with context referenced to originating sync function

In the code below the error is never caught:

const fn = () => {
  try {
    setTimeout(() => { throw new Error('An exception is raised') }, 10)
  } catch (error) {
    console.error({ error })
  }
}
fn()

The following is a solution:

const fn2 = () => {
  const errorFn = (error) => console.error({ error })
  setTimeout(() => {
    try {
      throw new Error('An exception is raised')
    } catch (e) { errorFn(e) }
  }, 10)
}
fn2()

The downside to this solutions is that it has to be implement in the function within setTimeout. That’s fine if one controls the code, but if users are supplying the code and calling setTimeout and don’t implement appropriate error handling, it could bring down one’s server!

Another solution is process.on('uncaughtException,... but that loses the context of the originating sync call that initiated the async function. Unless there is some clever way to supply that context?

Are there any other ways to catch async errors with context to the originating sync code?

Could one set a default error handler for a particular async branch – that’s catches all unhandled errors that may occur in that async branch?