Last active
September 18, 2021 09:19
-
-
Save mjackson/fafef431758f45ba3b830ad9cbeb2328 to your computer and use it in GitHub Desktop.
A workaround for the lack of a promise cancelation API
This file contains hidden or 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
| /** | |
| * Registers the given callback to be called in the node.js callback | |
| * style, with error as the first argument, when the promise resolves. | |
| * | |
| * Also, returns a function that may be used to prevent the callback | |
| * from ever being called. Calling the returned function is synonymous | |
| * with saying "I no longer care about the resolution of this promise, | |
| * even if it fails." | |
| * | |
| * Since there is no provision in the promise spec for cancel/abort | |
| * behavior, this may be used as a workaround. | |
| * | |
| * In React components, you can use this function as a workaround for | |
| * the deprecation of the isMounted() feature. | |
| * | |
| * const AsyncInput = React.createClass({ | |
| * handleResponse(error, value) { | |
| * this.setState(...) | |
| * }, | |
| * handleChange(event) { | |
| * this.ignoreResponse = createBinding( | |
| * makeRequest(event.target.value), | |
| * this.handleResponse | |
| * ) | |
| * }, | |
| * componentWillUnmount() { | |
| * // Ignore any requests that are currently in progress. | |
| * if (this.ignoreResponse) | |
| * this.ignoreResponse() | |
| * }, | |
| * render() { | |
| * return <input onChange={this.handleChange}/> | |
| * } | |
| * }) | |
| */ | |
| export const createBinding = (promise, callback) => { | |
| let isIgnored = false | |
| promise.then( | |
| value => !isIgnored && callback(null, value), | |
| error => !isIgnored && callback(error) | |
| ) | |
| return () => | |
| isIgnored = true | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I disagree, @rnicholus. A lot of people share this idea (I've heard it a few times before) so please allow me to expand a little on why I disagree.
tl;dr: Our job here is to handle errors thrown by the promise, not the callback.
The whole promise paradigm is designed to model JavaScript's native
try/catchbehavior with an asynchronous call "stack". For purposes of illustration, let's assume thatpromiseis a synchronous operation, modeled with a function that eitherreturns orthrows. If this were the case, my version ofcreateBindingwould look something like this:This is fairly straightforward:
tryto callcallbackwith the return value of the operation, butcatchtheerrorand use it as the first argument if the operation fails. One key characteristic of this code is that it makes no attempt tocatchany errors thatcallbackitself mightthrow. That's not its job. It's only supposed to resolve thepromised value.In the version you propose, with the second
.catchchained onto the end of the first.then, the sync equivalent would be:The
.catchin your example essentially says "catch everything, both errors in promise() and in callback()". We now have the following problems:catchblock, how do you know where theerrorcame from? Was it thrown bypromise()? Or somewhere insidecallback()?callbackis the culprit:throwagain when we call it in thecatchblock?callbacktwice? We're calling it for the 2nd time inside thecatch.These are semantics that we need to communicate to consumers if we're going to
catcherrors thatcallbackthrows.