Skip to content
This repository was archived by the owner on Mar 31, 2018. It is now read-only.
This repository was archived by the owner on Mar 31, 2018. It is now read-only.

Differentiating Programmer Errors from Operational Errors #10

@chrisdickinson

Description

@chrisdickinson

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 in async/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!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions