Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

One thing to watch out for when using debounce/throttle is the poor interaction with async functions. Debounced/throttled async functions can easily lead to unexpected behavior because they typically return the last result they have when the function is called, which would be a previous Promise for an async function. You can get a result that appears to violate causality, because the result of the promise returned by the debounce/throttle will (in a typical implementation) be from a prior invocation that happened before your debounce/throttle call.

There are async-safe variants but the typical lodash-style implementations are not. If you want the semantics of "return a promise when the function is actually invoked and resolve it when the underlying async function resolves", you'll have to carefully vet if the implementation actually does that.



Another thing to watch for is whether you actually need debouncing.

For example, debouncing is often recommended for handlers of the resize event, but, in most cases, it is not needed for handlers of observations coming from ResizeObserver.

I think this is the case for other modern APIs as well. I know that, for example, you don’t need debouncing for the relatively new scrollend event (it does the debouncing on its own).


Sad that the `scrollend` event isn't supported by Safari and doesn't look to be a part of their release this fall either.


Yep. Apple is a weird organization. scrollend is in Chrome since May 2023 and in Firefox since January 2023.


Reactive programming (such as with RxJS [0]) can be a good solution to this, since they provide primitives that understand time-based dependencies.

[0]: https://rxjs.dev/api/index/function/switchMap


Debouncing correctly is still super hard, even with rxjs.

There are always countless edge cases that behave incorrectly - it might not be important and can be ignored, but while the general idea of debouncing sounds easy - and adding it to an rxjs observable is indeed straightforward...

Actually getting the desired behavior done via rxjs gets complicated super fast if you're required to be correct/spec compliant


can you recommend some reading about the topic? also maybe point to some specs that require (and describe?) fully correct debouncing? thanks!


Not really, but I feel like you might've misunderstood me.

The debouncing of rxjs just takes an observation and debounces, which is essentially throttle with inverted output (it outputs last instead of first).

That's almost never what the product owner actually wants, at least IME.

If they give you any kind of soec, you'll quickly realize that limit.

I.e. debouncing after the request happened is impossible, just like cleanly abortion requests on exit or similar.

There are also often a ton of signals you need to add to the observale for all the events the PO wants to respond to, such as opening dialogues, interacting with elements outside of the context of the debounces event chain etc pp

It just keeps getting more complicated with every additional thing they come up with. But if they're fine with just living with the technical limitations, all is fine.


That doesn't sound correct. An async function ought to return a _new_ Promise on each invocation, and each of those returned Promises are independent. Are you conflating memoization? Memoized functions will have these problems with denouncing, but not your standard async function.


If I understand it correctly, they're saying the debounce function itself usually implements memoization in a way that will return you stale promises.


this sounds interesting but it's a bit too early here for me. by any chance can we (not simply a royal we :D) ask you to provide a code example (of a correct implementation), or a link to one? many thanks!


I would probably look at the lodash implementation (for JS/TS) as a complete implementation example.

https://github.com/lodash/lodash/blob/8a26eb42adb303f4adc7ef...


Damn, there was another thread not too long ago claiming that a sync does not mean concurrent - this would have been a great example to bring up.


So - events / handlers may need to be tagged as "for human interaction"? (to avoid over-filtering signals?)


Such stuff has first-class support in Kotlin: Structured concurrency simplifies multi-threaded programming pretty effectively.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: