Home > Net >  Can one promise be resolved twice?
Can one promise be resolved twice?

Time:12-19

I have such function:

async howLongToMyBirthdayDate(date) {
    return await new Promise((resolve, reject) => {
      let bday;
      if (!(date instanceof Date)) {
        if (Number.isInteger(date) && date > 0) {
          bday = new Date(date);
        } else {
          setTimeout(() => reject(new Error('Wrong argument!')), 1000);
          return;
        }
      } else {
        bday = date;
      }

      const today = new Date();
      today.setHours(0, 0, 0, 0);

      bday.setFullYear(today.getFullYear());
      bday.setHours(0, 0, 0, 0);

      if (bday - today === 0) {
        setTimeout(() => resolve(this.congratulateWithBirthday()), 1000);
        
      }

      setTimeout(
        () =>
          resolve(this.notifyWaitingTime(Math.ceil((bday - today) / 86400000))),
        1000
      );
    });
  }

The promise resolves twice. I see the result of function congratulateWithBirthday and notifyWaitingTime. Is it normal? I thought that promise can be resolved or rejected only once - by the first invokation of resolve or reject callback. Maybe setTimeout changes the behaviour of it? Can anyone explain me, please?

CodePudding user response:

Your promise doesn't need to resolve twice to get the behavior you're describing. You're invoking both of those functions directly, so of course they get invoked. Whether the second call to resolve does anything isn't related.

This line

resolve(this.notifyWaitingTime(Math.ceil((bday - today) / 86400000))),

does not say "resolve and then run this function", it says "invoke this function, and then call resolve with the return value of it".

A more appropriate way to write this code might be something like this:

async howLongToMyBirthdayDate(date) {
    const result = await new Promise((resolve, reject) => {
      let bday;
      if (!(date instanceof Date)) {
        if (Number.isInteger(date) && date > 0) {
          bday = new Date(date);
        } else {
          setTimeout(() => reject(new Error('Wrong argument!')), 1000);
          return;
        }
      } else {
        bday = date;
      }

      const today = new Date();
      today.setHours(0, 0, 0, 0);

      bday.setFullYear(today.getFullYear());
      bday.setHours(0, 0, 0, 0);

      // resolve with how long until bday
      if (bday - today === 0) {
        setTimeout(() => resolve(0), 1000);
        
      }

      // we just resolve with how long until bday
      setTimeout(
        () =>
          resolve(Math.ceil((bday - today) / 86400000)),
        1000
      );
    });

  if (result === 0) { this.congratulateWithBirthday(); }
  else if (result > 0) { this.notifyWaitingTime(result); }
  }

Not 100% sure this addresses your question since I can't see what the functions you're calling do, but my guess from how you were (ab)using resolve is that you want to essentially call one of two functions after calculations depending on if bday is now or not.

CodePudding user response:

Short answer, yes, given your code it will resolve twice. The reason for that is because congratulateWithBirthday and notifyWaitingTime are not conditioned on each other so they will both be called on the same run.

If you want to avoid that you should have something like

if (bday - today === 0) {
  setTimeout(() => resolve(this.congratulateWithBirthday()), 1000);
} else {
  setTimeout(() => resolve(this.notifyWaitingTime(Math.ceil((bday - today) / 86400000))), 1000);
}

Also, as a side note, try to decide between using async/await or Promise. There's no reason for using both and it makes the code look weird.

  • Related