Created
June 9, 2022 13:44
-
-
Save mmocny/50c6584269c2418acf9b9cdffea2485b to your computer and use it in GitHub Desktop.
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
/* Usage: | |
await Promise.firstOnly([ | |
(signal) => { | |
return new Promise((resolve, reject) => { | |
// ...do work and resolve(); | |
signal.addEventListener("abort", () => { | |
// ...cancel and reject(); | |
}); | |
}); | |
}, | |
]); | |
*/ | |
/* export default */ async function firstOnly(promiseConstructors, { signal } = { signal: new AbortController().signal }) { | |
signal?.throwIfAborted(); | |
// Create controllers that fork signal | |
const controllers = promiseConstructors.map(() => { | |
const controller = new AbortController(); | |
signal?.addEventListener("abort", () => controller.abort()); | |
return controller; | |
}); | |
// Call each promise constructor, passing in the signal | |
const promises = promiseConstructors.map((pc, i) => | |
Promise.resolve(pc(controllers[i].signal)) | |
); | |
return new Promise((resolve, reject) => { | |
let firstFulfilled = false; | |
// Reject if the outer signal is aborted | |
signal?.addEventListener("abort", () => | |
reject(new DOMException("Aborted", "AbortError")) | |
); | |
for (const [i, promise] of promises.entries()) { | |
// Resolve with whatever fulfills first | |
promise.then(resolve).then(() => { | |
if (firstFulfilled) return; | |
firstFulfilled = true; | |
// Abort the others | |
const otherControllers = controllers.slice(); | |
otherControllers.splice(i, 1); | |
for (const controller of otherControllers) { | |
controller.abort(); | |
} | |
}); | |
} | |
// Handle the case where all the promises reject. | |
Promise.allSettled(promises).then((results) => { | |
if (results.every((r) => r.status === "rejected")) { | |
// You could do something better here? | |
reject(Error("All rejected")); | |
} | |
}); | |
}); | |
} | |
// Abortable PROmise Constructor Callback | |
function aprocc(cb) { | |
return (signal) => { | |
return new Promise((resolve, reject) => { | |
cb(resolve, reject, signal); | |
}); | |
} | |
} | |
Promise.firstOnly = firstOnly; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment