Promise.try, and a synchronous throw in an async chain

A function that throws synchronously before returning a promise, and a caller whose .catch() never runs.

// the bug
function load(id) {
  if (!id) throw new Error('id required')   // synchronous
  return fetch(`/api/orders/${id}`)
}

load(undefined).catch(handle)   // TypeError: cannot read
                                 // .catch of undefined

// ES2025
Promise.try(() => load(undefined)).catch(handle)   // works

A function that sometimes throws and sometimes returns a promise has two error channels, and a caller can only reasonably handle one. Promise.try normalises it at the call site, which is the right place when the function is somebody else’s — for our own code the fix is to make the function async so every failure is a rejection.