-
-
Save getify/572779ee72421c500416946b5afed5f9 to your computer and use it in GitHub Desktop.
Handle a stream of clicks by canceling the latest one
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// FORKED FROM: https://gist.github.com/cowboyd/74c0fd9e7aa3cccaeda0b84604c0136a | |
// | |
// using CAF instead of Effection. | |
/** | |
* 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 event iteration 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 | |
*/ | |
// to use: | |
// CAF(clickToLoad)(new cancelToken()); | |
import CAF, { cancelToken, signalRace, tokenCycle } from "caf/caf"; | |
import { onEvent, onceEvent } from "caf/cag"; | |
function *clickToLoad(signal) { | |
var getNextToken = tokenCycle(); | |
var clicks = onEvent(signal, button, "click"); | |
// Note: can't use `for await..of` here, even though | |
// `clicks` is an ES2018 async iterable, because we want | |
// `clickToLoad(..)` to itself be fully cancelable, | |
// so we just emulate with a `while..true` loop | |
// | |
// e.g., | |
// for await (let click of clicks) { | |
// | |
while (true) { | |
// part of emulating the `for await..of` loop | |
if ((yield clicks.next()).done) break; | |
CAF(function*(signal){ | |
const resp = yield fetch("/sometimes/veryfast/sometimes/veryslow.data", { signal, }); | |
if (resp.ok) { | |
updateView(yield resp.json()); | |
} | |
})( | |
signalRace([ signal, getNextToken().signal ]) | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// similar to the file forked from, this version doesn't uses a HOF | |
// to define an abstraction helper, `forEach()`, to clean up | |
// the boilerplate around the original tweet's code | |
import CAF, { cancelToken, signalRace, tokenCycle } from "caf/caf"; | |
import { onEvent, onceEvent } from "caf/cag"; | |
function *clickToLoad(signal) { | |
yield forEach(signal, onEvent(signal, button, "click"), function*(signal){ | |
const resp = yield fetch("/sometimes/veryfast/sometimes/veryslow.data", { signal, }); | |
if (resp.ok) { | |
updateView(yield resp.json()); | |
} | |
}); | |
} | |
// `forEach()` HOF helper, wrapped with CAF behavior | |
const forEach = CAF(function *forEach(signal,events,spawn) { | |
var getNextToken = tokenCycle(); | |
while (true) { | |
if ((yield events.next()).done) break; | |
CAF(spawn)( signalRace([ signal, getNextToken().signal ]) ); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment