Promise.reject(reason) is the shortcut for "I already know this failed — hand me back a promise that's rejected and skip the executor." It's the mirror image of Promise.resolve, and you'll see it everywhere from input-validation guards to tests that need a pre-rejected promise to assert against.
Implement promiseReject(reason) as a standalone function. It returns a brand-new promise that is already in the rejected state, carrying reason as its rejection value. The reason is passed through verbatim — no wrapping, no conversion to Error, no special handling if it happens to be a promise.
function promiseReject(reason) {
// returns a Promise that is already rejected with `reason`.
}
promiseReject(new Error('boom')).catch((err) => {
console.log(err.message); // 'boom'
});
promiseReject('nope').catch((reason) => {
console.log(reason); // 'nope'
});
// The reason can be ANY value — not just Errors.
promiseReject(42).catch((r) => console.log(r)); // 42
promiseReject(undefined).catch((r) => console.log(r)); // undefined
// Passing a promise as the reason does NOT chain. The reason IS that promise.
const inner = Promise.resolve('hi');
promiseReject(inner).catch((r) => console.log(r === inner)); // true
.catch receives. Don't wrap strings in new Error(...), don't unwrap promises, don't coerce undefined to anything..catch handler still runs asynchronously (microtask), but the promise's internal state is settled before you return it.promiseReject(somePromise) rejects with somePromise itself as the reason, not with whatever that promise resolves or rejects to. This is the subtle difference from Promise.resolve, which does unwrap thenables.Promise constructor. The whole exercise is about wiring it up correctly with the executor function — don't reach for Promise.reject itself, that's cheating.