-1

Why is it exactly that the a resolveing promise correctly waits for the someOtherPromise to complete, but the reject does not? Run the following code sample and check the console.log output. I expected the "myFailingPromise rejected" message to show 2000 ms later, just as the "myPromise resolved" did.

let someOtherPromise = (previousPromise) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(previousPromise + ' => someOtherPromise after 2000ms');
      resolve('someOtherPromise');
    }, 2000);
  });
}

let myPromise = () => {
  return new Promise((resolve, reject) => {
    resolve(someOtherPromise('myPromise'));
  });
};

let myFailingPromise = () => {
  return new Promise((resolve, reject) => {
    reject(someOtherPromise('myFailingPromise'));
  });
};

myPromise().then((val) => {
  // this is executed after `someOtherPromise` resolves.
  console.log('myPromise resolved');
}).catch((err) => {
  console.log('myPromise rejected');
});

myFailingPromise().then((val) => {
  // this is executed after `someOtherPromise` resolves.
  console.log('myFailingPromise resolved');
}).catch((err) => {
  console.log('myFailingPromise rejected');
});

I know the intended behaviour can be achieved by using someOtherPromise.then(reject) in the second example, but my question is why the promise as an argument to reject is not possible, since it works for resolve.

Flame
  • 6,663
  • 3
  • 33
  • 53
  • 2
    That's simply how [resolve and reject](https://stackoverflow.com/a/29269515/1048572) work. [`resolve` is not `fulfill`](https://stackoverflow.com/q/32168194/1048572) (unfortunately). – Bergi May 25 '18 at 10:36
  • Your new Promise in myPromise resolves right away with the `someOtherPromise`. So `myPromise().then()` is actually only the success handler for `someOtherPromise`. What are you trying to achieve here? – k0pernikus May 25 '18 at 10:40
  • ive updated the question with a proper sample – Flame May 25 '18 at 11:17

3 Answers3

3

When you're resolving a promise A, if the value with which you're resolving it is a promise B, then your promise A will match the state of the promise B.

However, when you're rejecting a promise, your only giving a reason, whether the reason looks or not like a promise doesn't matter.

What you need to do, is to resolve with someOtherPromise in both cases.

If you want to wait for the first promise and reject anyway, you can do this:

let myFailingPromise = () => {
  return new Promise((resolve, reject) => {
    someOtherPromise.then(reject);
  });
};
Axnyff
  • 9,213
  • 4
  • 33
  • 37
  • `resolve`ing both cases is not the correct way when there is a `catch` waiting for a failed case. If i want to reject that promise with a promise, I'd have to call `someOtherPromise.then(reject)` (which works). My question is: why is `reject(someOtherPromise)` not supported – Flame May 25 '18 at 11:27
  • @Flame Update your usa-case in your question. This sounds like a XY-problem to me. Why do you want to reject a promise with another promise (which isn't supported by design)? – k0pernikus May 25 '18 at 11:28
  • @k0pernikus i simply want to reject a promise which waits for the passed promise to fulfill. If the answer is "its not supported by design" or "a reject is not supposed to contain a promise" then that doesnt answer the question since you're basically saying "you cant because you cant". – Flame May 25 '18 at 11:37
  • `resolve(someOtherPromise.then(() => throw new Error("reject")))` – Axnyff May 25 '18 at 11:45
  • your updated answer calls `then(reject())` but it should be `then(reject)` otherwise it immediately executes i believe – Flame May 25 '18 at 12:05
  • @Flame It's impossible to reject with a Promise. That's a fact of how Promises work. You have to deal with that. Which is why I am asking you to phrase your actual problem outside of your attempted solution. Take a step back, and describe your use-case. Not your idealized attempted solution on rejecting promises with promises. I am sure that the solution for what you are trying to achieve is out there, yet it's hard to determine by your example what you are actually trying to do. – k0pernikus May 25 '18 at 13:03
  • use case: when a particular promise is about to reject, i want to ouput some voice feedback, which simply speaks a few words. This voice feedback handler is a promise, which resolves when it is done speaking. So the solution I have is using `speak('my text').then(reject)` instead of `reject(speak('my text'))`. The whole question is about why `resolve` accepts both theses syntaxes, but `reject` does not – Flame May 25 '18 at 14:27
  • @Flame `resolve` changes the pending state of the promise and returns the success value which can be anything developer-defined. Anything you want. `reject` changes the pending state to failed and it provides a `reason` so you know why something failed. resolve and reject do very different things. One handles the success case, the other the error case. Only in the success case should a value be passed down, whereas in the error case you only pass down a reason. – k0pernikus May 25 '18 at 16:09
1

The reject only takes in a reason to highlight the error. Not another promise. You can resolve a promise with another promise of the same type, yet only the last promise's success case you will see.

Have a look at this adapted implementation:

const someOtherPromise = new Promise((resolve, _) => {
    resolve("I am a success");
});
const failingPromise = new Promise((_, reject) => {
    reject("I failed for a reason");
});

someOtherPromise
    .then((result) => {
            console.log("some other promise resolves", result);
            failingPromise
                .then((success) => {
                    console.log("never called");
                })
                .catch((reason) => {
                    console.error("failing promise rejects", reason);
                });
        }
    )
    .catch((error) => {
        console.error("also never called", error);
    });

This is the then-able approach to wait for other promises leading to a callback hell. This is why you can also use async / await syntax:

const app = async () => {
        try {
            const success1 = await someOtherPromise; // will succeed
            console.log(success1);
            const success2 = await failingPromise; // never succceds
            console.log(success2); // never be reached
        } catch (e) {
            return Promise.reject(e); // catches the error of failing promise and rethrows it, redundant but here showcases error handling
        }
    }
;

app()
    .then(() => {
        console.log("never be reached because of failing promise");
    })
    .catch(console.error);
k0pernikus
  • 60,309
  • 67
  • 216
  • 347
  • does something go inherently wrong when promises will support a `reject`ion with a promise as its argument? I am wondering why it doesnt follow the `resolve` spec. – Flame May 25 '18 at 11:24
  • You can `resolve` with any type. It can be a string, number, object, and also a promise or a collection of a promise -- whatever you fancy as a success case for your Promise. Rejections only handle a reason, that will be either a string message or an `Error`. Rejections are not supposed to contain other Promises. – k0pernikus May 25 '18 at 11:27
  • @Flame If `reject` did assimilate promises, then how would it be different from `resolve`? (In fact, `reject` isn't necessary at all. We could do only with `resolve`, and use `resolve(Promise.fulfill(value))` or `resolve(Promise.reject(reason))` to distinguish between success and error cases.) – Bergi May 25 '18 at 11:27
  • i am envisioning `resolve(myPromise)` will result in a `then()` or `catch` being called afterwards (depending on `myPromise` result), but a `reject(myPromise)` will result in a `catch()` being called afterwards. Note that in both cases it should wait for `myPromise` to succeed/fail – Flame May 25 '18 at 11:32
0

In regards to your updated question with the timeout, here is what you could do in order to always await another promise:

const otherPromise = async (willBeSuccesful: boolean) => {
    console.log("started timer for case", willBeSuccesful);

    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("resolve timer for case", willBeSuccesful);

            const successValue = "Fnord"; // whatever you want

            return willBeSuccesful
                ? resolve(successValue)
                : reject("this other promise failed because of reasons"); // only provide a reason, not another promise
        });
    };
};

const alwaysWaitForOtherPromiseThenRejectAnyway = async (otherPromise) => {
    try {
        const success = await otherPromise; // always waits 2 seconds, not matter
        console.log("other promises succeeded with value", success);
    } catch (e) {
        return Promise.reject(e); // passing through reason, redundant, only to showcase
    }

    return Promise.reject("reason why this promise failed"); // only happens after otherPromise was resolved, you could swallow that error and fail here or resolve here as well
};

const succeedingPromise = otherPromise(true);
const failingPromise = otherPromise(false);

alwaysWaitForOtherPromiseThenRejectAnyway(succeedingPromise)
    .catch((reason) => console.error(reason)); // fail after waiting for success of other promise

alwaysWaitForOtherPromiseThenRejectAnyway(failingPromise)
    .catch((reason) => console.error(reason)); // will fail as soon as otherPromise fails

It will always wait for the timeout before rejection happens. Rejection will have different reasons. Output will be:

started timer for case true
started timer for case false
resolve timer for case true
resolve timer for case false
other promises succeeded with value Fnord
reason why this promise failed
this other promise failed because of reasons
k0pernikus
  • 60,309
  • 67
  • 216
  • 347