Reputation: 1869
I'm trying to throttle the execution of a function in Svelte. However, using throttle within an auto-subscription seems to break it. Consider the following example (REPL):
<script>
import throttle from 'lodash.throttle';
import { writable } from 'svelte/store';
const arr = writable(['A', 'B', 'C']);
function foo(val) {
console.log('foo: ', val);
}
</script>
{#each $arr as a}
<button on:click={throttle(() => { foo(a); }, 1000)}>
Button {a}
</button>
{/each}
The execution of foo(a)
is not throttled at all. If you remove the {#each $arr as a}
block and just pass a string to foo, you'll see that it works as intended.
I'm assuming this has to do with the event loop and how Svelte auto-subscriptions work but don't know the exact reason. Wondering if anyone knows a) why this is happening and b) what a possible solution could look like?
Upvotes: 0
Views: 648
Reputation: 5482
If you look at the code Svelte generates for this, you can see that it is regenerating the throttled function on every click when you pass a store value. This resets the throttle timer on every click.
dispose = listen(button, "click", function () {
if (is_function(throttle(click_handler, 1000)))
throttle(click_handler, 1000).apply(this, arguments);
});
For whatever reason, this does not happen when you pass a regular string.
dispose = listen(button, "click", throttle(click_handler, 1000));
This may be a bug in Svelte, but I'm not sure. It might be worth opening an issue on the GitHub repo.
I was able to work around it by generating the throttled functions ahead of time:
<script>
import throttle from 'lodash.throttle';
import { writable } from 'svelte/store';
const arr = writable(['A', 'B', 'C']);
function foo(val) {
console.log('foo: ', val);
}
$: throttledFns = $arr.map(val => getThrottled(val));
function getThrottled(val) {
console.log('getting throttled');
return throttle(() => { foo(val); }, 1000);
}
</script>
{#each $arr as a, idx}
<button on:click={throttledFns[idx]}>
Button {a}
</button>
{/each}
This will regenerate the throttled functions when the store array changes, but not on every click.
You can also generate a throttled version of foo once and use that, but that will throttle all clicks to the buttons (e.g. if you click A and then click B, the click to B will be throttled).
<script>
// as before
const throttledFoo = throttle(foo, 1000);
</script>
{#each $arr as a, idx}
<button on:click={() => throttledFoo(a)}, 1000)}>
Button {a}
</button>
{/each}
Upvotes: 1