The difference between APIs like this and rAF is the frequency of use. There may be 100-1000 timers in a single frame, leading to 100-1000 callbacks. rAF is 1/frame. Apples and oranges different.
I can understand how most _javascript_ developers (and even browser implementers are oblivious to the overhead imposed by the '_javascript_y' ways of doing things - most pages don't require (and shouldn't require) anyone to know. But when you're pushing the bounds of the platform you notice very fast and it's something that's often impossible to work around.
In the case of Chrome, a single callback from the browser into user _javascript_ can have anywhere from .05ms to .5ms of overhead -- not much if you've got a few a frame for things like network responses/etc - but if you've got 1000 coming back you've just blown your entire frame time. Not to mention the risk of additional allocations required to make the callbacks and the GC time this could impose.
We requested this extension to target specific use cases around high-performance applications. If the API is designed in such a way to prevent the use in high-performance applications, it's not going to be worth it.
So I'm suggesting that you all step back from the callback cliff and instead look at what the problem is and how it can be solved without making the whole endeavor useless.
The way I see it, the query API would work like this in a browser such as Chrome where rendering occurs in another thread (though it can be done similarly for other implementations):
- user js runs init:
- createQuery()
- added to command buffer to send over to the gpu process
- stashed in a 'query map' on the renderer side
- user js runs frame:
- beginQuery()
- drawElements()
- endQuery()
- commands added to buffer, sent to gpu process
- queryCounter()
- returns the value of the renderer-side query object immediately - no blocking
- gpu process:
- run command buffer, see active timers, schedule them for processing
- for each scheduled counter: query, if available then queue for sending back to renderer in a batch
- renderer message from gpu:
- for each updated query:
- find in query map, set value
- user js runs frame:
- queryCounter()
- returns the new value that was just set
There's no way to spin lock here, as the query results are never made available in the same tick that requested them nor will they ever change value inside of a tick. As I said before, any loop trying to test them will be reduced to a while(true); and not work anywhere. The spec can be revised to say that counter values cannot change within a js tick.
The difference here vs. user callbacks is that the application can schedule when the counters are queried and - more importantly - if they are queried at all. There's no more overhead spent doing more native-to-js transitions than the application wants, and it happens when the application allows it. This fact *must not be overlooked* - it's already hard enough to build a jank-free application in the browser.
For the use cases this API is designed for - which are those that we intend to use it for - this design is acceptable. It allows us to do things that would be impossible with callbacks, such as aggressively timing things but only reading the timers back if we decide post-hoc that the frame was slow.
So again, I propose keeping the API as is, with a spec note talking about when query values are allowed to change.
For those providing feedback who have never needed an API like this please understand that there are scenarios that exist that are beyond the trivial examples in the spec, and 'webifying' it for the sake of trying to match the convention used by other APIs with completely different usage and semantics is not doing anyone any favors.