Why the Hints API Returns a Promise
Script can read client hints as well as receive them in headers. The high-entropy values arrive through an asynchronous call, and that choice encodes something about how the browser answers.
Low-entropy values are plain properties
The coarse hints are available synchronously as properties on a browser object. Reading them costs nothing and always succeeds, because the values are already determined.
Synchronous access is appropriate for values sent by default anyway. Nothing is being decided at the moment of the read.
The API shape therefore mirrors the header behaviour. What is broadcast is cheap to read; what is negotiated is not.
High-entropy values are requested by name
Detailed hints require a call naming the fields wanted, which resolves later with whichever of them the browser chose to provide.
Naming fields keeps the interface parallel to the header mechanism. A site states its requirement in both channels rather than receiving everything and filtering.
The returned object may omit requested fields. Consumers must check for presence rather than assuming the request was fully honoured.
Asynchrony leaves room for a decision
An asynchronous interface allows the browser to consult policy, apply user settings, or in principle prompt before answering. A synchronous property read forecloses all of that.
Whether a given browser does any of this today is beside the point. The interface was shaped so that doing it later would not require breaking every consumer.
This is a common pattern for capabilities that may become gated. The asynchronous shape is a reservation of future latitude.
It also discourages casual use
An asynchronous call inside a synchronous rendering path is inconvenient, and the inconvenience is not accidental. Code that genuinely needs the value will restructure around it.
Code reaching for detailed hints out of habit tends not to, because the cost of doing so is visible at the call site rather than hidden in a header.
Friction is a weak mechanism on its own, but combined with explicit field naming it shifts the default from collecting everything toward asking for something.
What consumers should do with the result
Treat the resolved object as partial data. Handle each field's absence as an ordinary branch, and never let a missing value throw or produce a blank in rendered output.
Where the value drives a visible decision, decide what the unknown case renders before writing the known cases. That ordering produces better fallbacks.
The pattern is the same one that serves detection well generally: ask narrowly, expect refusal, and make the refusal path the one that works.