Promise solved or rejected multiple times by "blocking" execution of the rest of the code

Asked

Viewed 41 times

2

My goal is to get the user’s email and then send it to the database. For this, I put a "change" event in the input that captures the email. My idea is that every time the input goes out of focus, the value within the input is captured.

Filing cabinet index.js:

const GetEmail = require("./classes/getEmail.js");

(async function () {
  const email = await new GetEmail().userEmail;

  console.log('email retornado no index.js');
})();

Filing cabinet getEmail.js:

get userEmail() {
  return new Promise((resolve, reject) => {
    const input = document.querySelector(".email-form input");

    function handleInputChange({ target: { value: email } }) {
      console.log('evento change disparado');
      console.log('email: ' + email);
      if (this.validateEmail(email)) {
        console.log('email correto, resolve deve ser retornado');
        return resolve(email);
      } else {
        console.log('email incorreto, reject deve ser retornado');
        return reject("Invalid e-mail");
      }
    }

    handleInputChange = handleInputChange.bind(this);

    input.addEventListener("change", handleInputChange);
  });
}

The problem is this: if I type the email incorrectly and take the focus of the input, the promise within the userEmail method returns the Reject and an error message is sent to the browser console (this behavior is expected). But if I turn the focus to input and correct the email, the Promise Does NOT return the resolve with the email and function console.log within the index.js IS NOT EXECUTED.

Follow a Gif demonstrating this behavior: https://imgur.com/gb6d040

Conversely, if I type the email correctly and take the focus of the input, the promise within the method userEmail returns the solves with the email and the function console.log within the index.js is executed correctly. But if I change the email to an incorrect one, the Promise should return the reject that should throw an error in the browser console, but that’s not what happens.

Follow another gif demonstrating the above behavior: https://imgur.com/nstL9Aq

  • 2

    You’re using a promise like a observable. A promise can only pass to the state settled (resolve or reject) only once. Can the promise leave the final state several times? No. Once it passes to the final state, there it will stay. It is a state machine which does not allow a step backwards from that final stage.

1 answer

2


Note that from the getter userEmail, you are creating a promise, which is returned. In the index.js, you call that getter:

const email = await new GetEmail().userEmail;

I mean, you’re creating a only promise, since the getter userEmail, who creates the promise, is only called once.

The mistake comes from the fact that you think that a single promise can be solved or rejected several times, which is not the case.

What is a promise?

From the standpoint of the appropriate states for a promise, we can think of it as a state machine. When you create a new promise, it is initiated in the state pending, that will be active until it is resolved or rejected.

Once you call the function resolve (first parameter of the constructor) or reject (second constructor parameter), the promise transitions to one of these two states:

  • fulfilled, in case it has been resolved;
  • rejected, if it has been rejected.

It is said that the promise was settled when in the state fulfilled or rejected. Once settled, the promise nay can go back to the state pending, since fulfilled and rejected sane final states. In summary, this diagram can be represented:

Gráfico de estados da promessa.

The problem of the question

Reiterating what I put at the beginning of this answer, it is worth noting that you are creating a single promise.

The problem is that you are trying to solve it (from your own constructor) several times, which is an error from the point of view of states passable by a promise.

As we have seen above, a promise nay can be resolved or rejected several times.

Therefore, instead of, for each event change, try to resolve or reject the only promise multiple times, creates a promise for each event. Thus, you will be able to resolve or reject multiple promises.

It could be something like this:

main();

// Pense como `index.js`:
function main() {
  async function handleInputChange(event) {
    try {
      const message = await validateEmail(event.target.value);
      console.log('[SUCESSO] Do índice:', message);
    } catch (error) {
      console.log('[ERRO] Do índice:', error);
    }
  }
  const input = document.querySelector('.email-form-input');
  input.addEventListener('change', handleInputChange);
}

// Pense como método que valida o e-mail:
function validateEmail(email) {
  return new Promise((resolve, reject) => {
    // Omiti a função de validação para simplificar...
    if (email.includes('@')) {
      resolve(`E-mail ${email} é válido.`);
    } else {
      reject(`E-mail ${email} é inválido.`);
    }
  });
}
<input type="text" class="email-form-input" />

But note that it makes no sense to use promises in this type of situation, since an email validation tends to be synchronous.

Of course, if it is an email existence validation that needs some asynchronous processing (such as server query), promises can be seen as necessary.

In short, you were using promises as if they could be solved or rejected several times, which is not the case. However, it may be the case to use Observables (see Rxjs, one of the implementations for Javascript).

  • Actually, my mistake was to think that, at each "change" event triggered, a new promise was created. Anyway, thank you very much for the explanation.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.