-
-
Notifications
You must be signed in to change notification settings - Fork 11
Differentiating Programmer Errors from Operational Errors #10
Description
This primarily concerns the error symposium participants, but may also concern the post mortem WG.
The Problem
- Promise users expect all Promise-returning API usage to return a Promise object, success or fail.
- This means that invalid API use is returned as a rejection, alongside other errors.
- This necessitates the use of
try / catch
inasync/await
code. - This also necessitates error type checking in promise-based code.
- Were a "abort on unhandled rejection" flag to land, this would reduce such a flags efficacy: given that such a flag would operate by attempting to immediately abort the promise at top of stack assuming no user-installed handlers were present, and given that in type-checking users would usually add user-installed handlers.
Proposed Solutions
Recovery Object
Add an optional object parameter to all promise-returning APIs. This will be known as the recovery
object. It does not change the behavior of Promises/A+; it is purely a Node API-level pattern. It allows users to intercept errors before settling the returned promise, and change the outcome of the lower-level API call:
// recovery object
await fs.readFilePromise('some/file', {
ENOENT() {
return null
}
})
// normal use:
fs.readFilePromise('some/file')
Pros:
- Promise API users get the API they expect. Error symposium users get the API they expect.
- Simple to implement.
Cons:
- It's a new pattern.
- It doesn't solve problems in the ecosystem.
--abort-on-sync-rejection
Add a flag to abort the process on synchronous new Promise
rejection. This does not extend to synchronous throw
within handlers
. Potentially patch Promise.reject
so that it returns a pending promise that rejects on next tick.
For example, the following usage would abort under this flag:
new Promise(() => { throw new Error() })
new Promise((_, reject) => { reject(new Error()) })
The following usage would not abort under this flag:
new Promise((_, reject) => { setTimeout(() => reject(new Error())) })
attainPromise().then(() => { throw new Error() })
The net effect being that the promise would immediately exit on invalid Node API use under this flag. Without the flag, the process keeps running "as expected" for the majority of promise users.
Pros:
- Solves problem in ecosystem for native promise users.
- Does not introduce a new pattern.
Cons:
- Unclear on how common synchronous rejection is in
Promise
constructors. Believed to be uncommon, could be proven wrong? - Flagged and unflagged behavior diverge.
This Discussion
Please pose problems with or benefits of the listed solutions, or propose alternate avenues of inquiry which will be added to the issue text. Clarifying comments, like "how are you using this?" are in-scope. Comments that suggest that the desire to separate operational errors from programmer errors is invalid will be moderated in the interest of keeping this discussion productive. Thank you for participating!