Last active
September 13, 2022 22:16
-
-
Save tjconcept/69994c78c8f5b8008258353292787519 to your computer and use it in GitHub Desktop.
"join" with deterministic behavior for rejections
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
class Rejection { | |
constructor(reason) { | |
this.reason = reason | |
} | |
} | |
export default function using(...args) { | |
const lastIdx = args.length - 1 | |
const fn = args[lastIdx] | |
if (typeof fn !== 'function') { | |
throw new Error('Missing expected function argument') | |
} | |
if (lastIdx === 0) { | |
throw new Error('At least two arguments must be passed') | |
} | |
const wrapped = Array(lastIdx) | |
for (let i = 0; i < lastIdx; i++) { | |
wrapped[i] = args[i].catch((err) => new Rejection(err)) | |
} | |
return Promise.all(wrapped).then((results) => { | |
const err = results.find((r) => r instanceof Rejection) | |
if (err !== undefined) { | |
return Promise.reject(err.reason) | |
} else { | |
return fn(...results) | |
} | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I fixed it, I think.
The only deficiency, that could be fixed, would be to "reject earlier". That is, if a value rejects, and all values to the left of it has resolved, reject straight away.
However, if the primary use case is servers, the "common case" would be for all values to resolve, and then this is as optimal as it gets. I think it would be way more complicated to "reject early" too.
Just like the original
join
(from Bluebird) and the nativePromise.all
, there's the trade-off that you must accept silencing all other rejections. Only in super edge cases is that an issue, but imagine a bug that occurs in your second parameter only when the first one also rejects, e.g. a syntax error. You'd never find it and it could lead to a memory leak or data corruption:You'll never see that
FallbackPlan
is not defined, or worse if it fails half-way and doesn't clean up and leaks.So, if you're the pedantic type, you'd probably not use
join
orPromise.all
if exceptions are part of your flow control for operational loads. All code can do that, but it may be too cumbersome.