phold v1.0.0
phold
Hold promise rejection until requested
Usage
phold(promise)
Assigns an empty error handler on the promise
to avoid unhandledRejection
and returns a function that returns a promise which resolves with the original
value or rejects with the original error.
Example
const { phold } = require('phold');
async function example() {
// capture any rejections so you don't get the unhandledRejection warning/crash
const giveItBack = phold(somethingThatCouldReject());
// do more work
await someOtherAsyncStuff();
try {
// resolve or reject with original result, if any
await giveItBack();
}
catch (err) {
// will be caught if somethingThatCouldReject() rejects
}
}
Use case
Suppose you have an asynchronous subscription channel, where you are waiting for a single message or a single error notification to arrive (e.g. notifications about jobs in a queue). The notification is triggered by a request. This means that you need to subscribe for the message before you make the request (otherwise there is a chance you'll miss the notification if the job completes really really fast).
The subscription channel is probably an event emitter, but since we're only waiting for a single message, i.e. we have a concrete final state ("job completed" or "job failed") - a promise is a nice abstraction to use to hold the value until we need it. Your code might look something like this:
async function queueExample() {
const myJob = await createJob();
const subscription = new Promise((resolve, reject) => {
jobQueue
.on('completed', (job) => {
if (job.id === myJob.id) {
resolve(job);
}
})
.on('error', (err) => {
if (err.job.id === myJob.id) {
reject(err);
}
});
});
const started = await startJob(myJob);
try {
return await subscription;
}
catch (err) {
return retry(myJob)
}
}
Note how startJob
is an async function - this means we're breaking the event loop without assigning a catch()
handler for the subscription
promise. This means that if error
event arrives before startJob
completes (or if you have some other async steps before you actually need the subscription
) - you will get this very nice warning in your console (node 6.6.0+):
(node:9260) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:9260) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
The scary part is the DeprecationWarning
- in the future such code would crash your process - and this is absolutely not what you want, because you're perfectly able to handle the rejection (e.g. by retrying the operation).
phold
fixes this by assigning a catch()
handler to your promise to hold the rejection until you're ready to use it.