0

I have this situation :

import processAsyncCall from "../helpers/processAsyncCall";

const promiseWrapper = () => new Promise((res, rej) => { /* [1] */ });
const onResolve = () => { /* [2] */ };

dispatch(processAsyncCall(promiseWrapper, onResolve));
  1. The promiseWrapper contains a promise that will resolve when a certain action is made (ex: fetch a file, update content, etc.).
  2. This method will be called when the promise inside the promiseWrapper has resolved.

Let's take a look at processAsyncCall.

export default (
  promiseWrapper,
  onResolve = null,
) => (dispatch, getState) => {
  dispatch(showLoading());
  promiseWrapper()
    .then(result => {
      if (onResolve !== null && typeof onResolve === "function") {
        onResolve(dispatch, getState)(result);
      }
    })
    .catch(() => console.log("ERROR_IN_ASYNC"))
    .finally(() => dispatch(hideLoading()));
  });
};

The important part is promiseWrapper().then(result => { /* ... */}).

I execute the function promiseWrapper to create the Promise on the spot.


My question :

Is it necessary to wrap a promise in a function to ensure that it is not resolved before it is processed?

I had this problem where the then callback wouldn't work if I passed my Promise directly as a parameter.

dispatch(processAsyncCall(new Promise(/* ... */), /* ... */));

My understanding is that when a promise resolves, the then callback is called. But if the function resolves and there is no then callback at that time, the Promise is done/completed/dead.

Keep in mind that the example above is pretty straightforward, but in some cases the Promise is created elsewhere and it is passed down until it reaches the processAsyncCall function.

Bird
  • 572
  • 5
  • 15
  • "*I execute the function `promiseWrapper` to create the Promise on the spot.*" - don't you want to do the action that creates the promise *after* `dispatch(showLoading())`? – Bergi Aug 27 '18 at 13:46
  • 1
    "*if the function resolves and there is no then callback at that time, the Promise is dead*" - nope, not at all. Promises don't die. They enter the resolved state when you resolve them. You can install any amount of `then` callbacks to them, they *always* get called when the promise is fulfilled - even when the promise had been resolved *before* the `then` call. – Bergi Aug 27 '18 at 13:48
  • @Bergi It is a good suggestion! In my case I don't mind in the loading is not exacly before the action. I thought of chaining them `dispatch(showLoading()).then(/* ... */)`. I can still improve it later :) – Bird Aug 27 '18 at 13:52
  • 2
    The reason you'd wrap the promise like this is so you can choose when the code should begin executing, rather than having it execute immediately after define-time. Also, consider rewriting this using `async`/`await`, I think you'll see it becomes a lot easier to read – Paul S. Aug 27 '18 at 13:55
  • @PaulS. This is absolutely right : "The reason you'd wrap the promise like this is so you can choose when the code should begin executing". I wrote this code 3 months ago and so I forgot why I needed it to **resolve** in `processAsyncCall`. I will consider rewriting with the `async/await` later on. – Bird Aug 27 '18 at 14:04

2 Answers2

2

My understanding is that when a promise resolves, the then callback is called. But if the function resolves and there is no then callback at that time, the Promise is done/completed/dead.

That is incorrect. By definition, code cannot know when a promise will resolve, and requiring code to attach a callback before it resolves would put considerable constraints on promises and make them much less useful. No, you can attach a new callback at any time and it will be called:

const p = new Promise(r => {
    r('yay');
    setTimeout(() => p.then(console.log));
});

p.then(console.log);

The difference in your case is probably simply whether processAsyncCall expects a promise as argument or a function that returns a promise. Those are different types and must be handled differently, obviously.

deceze
  • 510,633
  • 85
  • 743
  • 889
2

I feel that Promise behaviour remains a little mysterious in your mind.

You can imagine a Promise as an object having 4 properties :

  • a state,
  • a value,
  • a list of callback that will be called in case of success,
  • a list of callback to be called if it failed.

The state always start at pending, and the code inside your promise is stacked to be called in the next execution loop as if you had called it with setImmediate.

The promise can only resolve or reject once and will remain in that state forever after that.

When a promise is resolved/rejected, its state changes to fulfilled or rejected, and the corresponding callbacks are called in order.

When you call then or catch on a Promise, if the state of the Promise is pending, the callback is added to the corresponding list. If the promise is already resolved (respectively rejected), the callback will be called in the next execution loop.

I hope it can help you understand what happens here.

Sharcoux
  • 5,546
  • 7
  • 45
  • 78
  • 1
    Nitpick: [the states are a bit more complicated](https://stackoverflow.com/a/29269515/1048572) regarding `resolve`, and "*If the promise is already fulfilled, the callback is immediately called.*" should be "…is immediately scheduled" (not called - it's still asynchronous). – Bergi Aug 27 '18 at 14:04
  • Try this: `a = Promise.resolve();` and later: `a.then(console.log('1')); console.log("2");`. If the promise is resolved it is not asynchronous. But you're right about the states. I really simplified. – Sharcoux Aug 27 '18 at 14:30
  • 1
    That's because you need to use `a.then(() => { console.log('1'); })` – Bergi Aug 27 '18 at 14:37
  • Thanks alot for the explanation! I understood alright with @deceze's answer, but your answer is more about the *WHY* is the `then` callback called each time. I understand that the answer is not exact, but it is enough for me to understand the mecanism behind! I will try to code a **Promise** object this week and see how it can be done :) – Bird Aug 27 '18 at 17:07
  • @Bergi OMG! You're right. I was not sure so I made this quick check but I did it too quickly apparently... – Sharcoux Aug 27 '18 at 20:32