Skip to content

Instantly share code, notes, and snippets.

@cowboyd
Last active April 11, 2023 10:54
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cowboyd/74c0fd9e7aa3cccaeda0b84604c0136a to your computer and use it in GitHub Desktop.
Save cowboyd/74c0fd9e7aa3cccaeda0b84604c0136a to your computer and use it in GitHub Desktop.
Handle a stream of clicks by canceling the latest one
/**
* Implements an operation to fetch the data as described in https://twitter.com/BenLesh/status/1455597411385098242
* The "winner" is unambiguous. It is the request corresponding to the latest click, guaranteed.
* Other important things to note:
*
* 1. The HTTP request is cancelled every time along with its containing task
* 2. click listener is removed automatically at the end of the `clickToLoad` operation
* 3. The cancellation logic is explicit here to demonstrate the concept, but it can easily be abstracted into a separate
*. function. See the `click-to-load-HOF` example below
*/
import { on, fetch, spawn } from 'effection';
function* clickToLoad() {
let current;
yield on(button, 'click').forEach(function*() {
if (current) {
yield current.halt();
}
current = yield spawn(function*() {
const resp = yield fetch('/sometimes/veryfast/sometimes/veryslow.data');
if (resp.ok) {
updateView(yield resp.json());
}
})
});
}
/**
* Using a higher order `cancelPrior` function, we can make the Effection example look almost exactly like the
* example in the original https://twitter.com/BenLesh/status/1455597411385098242
* The "meat" of the operation is plain: load the data and update the view, and the "async correctness" aspect is explicit,
* but also secondary as it should be.
*/
function* clickToLoad() {
yield on(button, 'click').forEach(cancelPrior(function*() {
const resp = yield fetch('/sometimes/veryfast/sometimes/veryslow.data');
if (resp.ok) {
updateView(yield resp.json());
}
}));
}
/**
* Returns an operation that has an implicit reference to its previous invocation. Whenever it is started
* it first cancels that previous task.
*/
function cancelPrior(operation) {
let current;
return function*() {
if (current) {
yield current.halt();
}
current = yield spawn(operation);
}
}
@getify
Copy link

getify commented Nov 15, 2021

@cowboyd This was a really interesting gist/example... thanks for posting!

For my own learning and posterity, I expressed it with CAF + helpers: https://gist.github.com/getify/572779ee72421c500416946b5afed5f9 There were things CAF didn't have that it should have had, so this exercise helped me improve my library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment