
Security News
Crates.io Users Targeted by Phishing Emails
The Rust Security Response WG is warning of phishing emails from rustfoundation.dev targeting crates.io users.
Resolves promises concurrently with deterministic rejection order. Somewhere between Promise.all and Promise.allSettled.
Promise.all
and Promise.allSettled
.
Promise.allSettled
for the same input$ npm i dpa
Deterministic promise all. I promise (pun intended!) it's not as esoteric as it sounds!
Suppose you have some asynchronous authorization functions, each resolving to
undefined
or rejecting, that you'd like to run on all requests to an endpoint.
You care about performance! So you run the authorization checks concurrently
using Promise.all
and display an error to the user on rejection.
Using Remix it might look something like this:
import { useCatch } from 'remix'
export const loader = async ({ request }) => {
await Promise.all([
checkThing1(request),
checkThing2(request),
checkThing3(request),
])
// Do authorized stuff...
}
// Rendered when an error response is thrown in `loader`
export const CatchBoundary = () => {
const caught = useCatch()
// Return some JSX...
}
Everything seems to work great, but what if the promises returned by
checkThing1
and checkThing2
both reject? What does the user see? The answer
is it depends on which one rejects first!
That's right. Your error page is nondeterministic. The user can visit the same URL with the same authorization state and receive a different page purely based on how quickly each authorization check completes.
Another problematic case in a framework like Remix is redirects. In Remix you
can redirect by throwing redirect responses. If you use Promise.all
to
concurrently run a bunch of functions that may throw redirects, then your
redirects are nondeterministic too.
You could use
Promise.allSettled
to wait for all the promises to either resolve or reject, then if any reject you
could reject with the first rejected promise in your list of promises. This way
you reject with the same promise regardless of which one rejects first
(time-wise). This is what
Remix was doing to keep concurrent loader execution deterministic
(and maybe still does, but I couldn't figure where that's done at the latest
commit).
But can we do better? What if the first promise in your list is the first
promise to reject? We could reject right away and still be deterministic! But
with Promise.allSettled
we're stuck waiting for every promise to resolve in
all cases...
More generally, if a promise in your list rejects and every promise before it in
the list resolved, then we can immediately reject with that promise. That's what
dpa
does. It's deterministic while being strictly as fast as or faster than
Promise.allSettled
!
import { setTimeout } from 'node:timers/promises'
import dpa from 'dpa'
const test = async fn => {
const start = Date.now()
try {
console.log(await fn())
} catch (error) {
console.log(`${error} thrown`)
} finally {
const elapsed = Date.now() - start
console.log(`${Math.trunc(elapsed / 1000)}s elapsed`)
}
console.log()
}
await test(() =>
dpa([
setTimeout(1000).then(() => 1),
setTimeout(4000).then(() => 2),
setTimeout(5000).then(() => 3),
]),
)
// => [1, 2, 3]
// => 5s elapsed
await test(() =>
dpa([
setTimeout(1000).then(() => 1),
setTimeout(4000).then(() => {
throw 2
}),
setTimeout(6000).then(() => {
throw 3
}),
]),
)
// => 2 thrown
// => 4s elapsed
await test(() =>
dpa([
dpa([
setTimeout(1000).then(() => 1),
setTimeout(6000).then(() => {
throw 2
}),
setTimeout(2000).then(() => {
throw 3
}),
]),
]),
)
// => 2 thrown
// => 6s elapsed
Stars are always welcome!
For bugs and feature requests, please create an issue.
FAQs
Resolves promises concurrently with deterministic rejection order. Somewhere between Promise.all and Promise.allSettled.
We found that dpa demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
The Rust Security Response WG is warning of phishing emails from rustfoundation.dev targeting crates.io users.
Product
Socket now lets you customize pull request alert headers, helping security teams share clear guidance right in PRs to speed reviews and reduce back-and-forth.
Product
Socket's Rust support is moving to Beta: all users can scan Cargo projects and generate SBOMs, including Cargo.toml-only crates, with Rust-aware supply chain checks.