Open n8schloss opened 7 years ago
The problem described here is just one case of aliasing of the HTTP representation that can occur by only relying on the "name" (aka URL). HTTP Content Negotiation describes some other methods that this could occur with.
One example that I am directly aware of is making multiple range requests (Using the Range
header), it is impossible to differentiate. Inferring from the content length is unfeasible because range requests could request the same range size but vary by byte index.
I'm not sure what the existing spec says when you don't consume the body, but it seems like there should be valuable timing state immediately on Response construction? Thus, a persistent reference to (what I assume is an underlying mutable) perf entry is probably equally acceptable vs needing a method to resolve. In other words, the instant a Response is created, perf entries are likely already existent. I assume that such entries shall be updated contingent on additional response behavior, such as reading the stream. I rarely think about the case where:
const res = await fetch(...)
await doSlowWork()
await res.json()
...and how that may/may-not impact the perf entries API, such as this. gotta hit the spec :glasses: 📖
Nonetheless, some link (direct ref, method to get immutable copy, whatever) seems warranted. Today I wrote a server-timing
in parser in JS to chain to my fetch calls, as we do not have such a linkage natively.
I'm not sure if this is explicitly specified behavior, but at least Firefox and Chromium keep the URL hash in PerformanceResourceTiming.name
. That should work for making the URL unique for multiple requests to the same resource. I've tested this on Firefox 114, Chrome 114, and Edge 114, all on desktop. You should do your own testing if you plan on using this trick.
This doesn't obviate the need for this issue. Exposing an easy way to get the associated PerformanceResourceTiming
entry from a fetch
call would be more robust and ergonomic. Giving access to a partial PerformanceResourceTiming
entry before the body is consumed may also be useful, but is currently impossible.
It's not perfect but reading the spec, it looks like one imperfect approach you can do now is to bound the .startTime of the PerformanceResourceTiming with
const a = performance.now()
const f = fetch(...) // no await
const b = performance.now()
await f
// later, look for performance entries where a <= prt.startTime <= b (and prt.name === requestedURL)
This should disambiguate pretty well even between concurrent requests to the same resource, as long as the performance.now() resolution is finer than the difference in start times between adjacent fetches.
Spec notes for why this works:
(copy and pasting from https://github.com/whatwg/fetch/issues/491)
Problem
Currently the process of linking a fetch request to a specific resource timing object is imperfect. If there are two fetch api requests for the same resource that happen within a short time it's impossible to tell exactly which fetch call initiated which request, leading to some errors in performance logging. Orthogonally, if all a developer cares about is the perf result for a single fetch, having to use the whole performance api could be cumbersome.
Solution?
The ideal solution here (imo) is for there to be a way to get a resource timing object for a specific response. This could be accomplished by adding a method to response objects that give you a promise which will eventually resolve to the PerformanceResourceTiming object once it's ready.
Thoughs?