nonopolarity
nonopolarity

Reputation: 151206

What is a proper way to create a JS promise that is rejected?

If just for experimenting with JS Promises or Promise.race(), etc, a promise that is rejected needs to be created:

p = Promise.reject(Error("some unknown reason"));

but this way, an exception will be thrown. And if you silent the error:

p = Promise.reject(Error("some unknown reason")).catch(err => {});

Then p actually will be a resolved promise. Why is that? Is a rejected promise supposed to be created this way, or what should be done? A resolved promise is much simpler if by using Promise.resolve(1).

Upvotes: 1

Views: 81

Answers (1)

jfriend00
jfriend00

Reputation: 708026

A proper way to create a rejected promise would just be this:

let p = Promise.reject(new Error("some unknown reason"));

Somewhere, before you return back to the event loop, you will NEED to have a .catch() handler on any rejected promise. If you don't, the system will warn you of an unhandled rejection. That is the state of the Javascript language implementations. The designers feel like it is extremely important to not let unhandled promise rejections slide by unnoticed (I agree - just like unhandled synchronous exceptions don't slide by unnoticed). And, with the current state of the JS engines, their detection of an unhandled rejection is slightly imperfect. It's possible you may do something with that rejected promise that LATER adds a .catch() handler, but the JS engine doesn't know you're doing to do it later. It just knows that you've gotten back to the event loop and you didn't do it yet. That seems like trouble so it warns you.

If you put a .catch() on it, then the promise takes on whatever you return or throw from the .catch() handler (just like a try/catch in the synchronous world). So, if you do this:

let p = Promise.reject(new Error("some unknown reason")).catch(err => {});

Then, you catch the rejection in your .catch() and return or throw nothing from it, so the promise chain is now resolved with an undefined resolved value. There is no requirement for a .catch() there, but if you have a specific reason to add a .catch() (such as logging or cleanup code or inspecting the error to determine proper course of action), but you want the promise chain to stay rejected with the same reason, then you must either return a new rejected promise or throw the original error like this:

let p = Promise.reject(new Error("some unknown reason")).catch(err => {
    // do whatever you want here
    console.log(err);
    throw err;                 // rethrow to keep the promise chain rejected
});

or like this:

let p = Promise.reject(new Error("some unknown reason")).catch(err => {
    // do whatever you want here
    console.log(err);
    return Promise.reject(err);  // return rejected promise to keep the promise chain rejected
});

but, you need to DO something with that promise to use it in some sort of promise chain context that will eventually have a real .catch() handler.

For example (you mentioned Promise.race), you can do this just fine:

function delay(t,v) {
    return new Promise(resolve => {
        setTimeout(resolve, t, v);
    });
}

let p1 = Promise.reject(new Error("You got rejected!!"));
let p2 = delay(100, "Got a good result");

Promise.race([p1, p2]).then(result => {
    console.log(result);
}).catch(err => {
    console.log(err.message);
});

Here there would be no warnings about an unhandled promise rejection because we inserted the p1 promise (that is rejected) into a Promise.race() and that has a .catch() handler on it.

Then p actually will be a resolved promise. Why is that?

Because you had an empty .catch() that didn't return a rejected promise from the handler or didn't throw an exception. So, the reject was "caught" and "handled" and the promise chain is now back to resolved. Since there was no return value from the .catch() handler, the resolved value for the promise chain is now undefined.

Is a rejected promise supposed to be created this way, or what should be done?

Yes, that is the proper way to create a rejected promise. But, you need to DO something with it, not just let it sit there on its own. And what ultimately gets done with it (returning it from some promise chain or linking other handlers onto it) needs to happen soon (like before the interpreter gets back to the event loop). We could advise much better how to handle this if you presented a real world use case with real code.

Upvotes: 2

Related Questions