Functions scheduled to run with setTimeout are executed in the main loop, outside the body of code that originated them.
To handle errors, put the try-catch inside the setTimeout handler:
setTimeout(function () {
  try {
    throw new Error('error!');
  } catch (e) {
    console.error(e);
  }
}, 300)
If you need to access the Error object from block that called setTimeout, use Promises:
const promise = new Promise((resolve, reject) => {
  setTimeout(function () {
    try {
      throw new Error('error!');
      resolve(); // if the previous line didn't always throw
    } catch (e) {
      reject(e)
    }
  }, 300)
})
promise
  .then(result => console.log("Ok " + result))
  .catch(error => console.error("Ouch " + error))
This example above is not the most elegant way of handling the case with a Promise. Instead, implement a delay(ms) function like this:
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}
Then call
delay(300).then(myFunction).catch(handleError)