Aditya DS
Aditya DS

Reputation: 103

Resolving a promise after some time t or if an event fires, whichever occurs first

I have a function called wait which accepts time in milliseconds and returns a promise. The promise should get resolved if myEvent is fired or after some time ms whichever occurs first. Currently I am doing something like this which I don't think is the right way to do it.

async function wait(ms) {
    return new Promise((res) => {
        myEmitter.on('myEvent', res);
        setTimeout(res, ms);
    });
}

Is there a better way to do this?

Upvotes: 4

Views: 1813

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1075209

What you're doing is fine.¹ Once the promise is settled (by the first call to the resolve function or reject function, to use their usual names), calling the resolve/reject functions again doesn't do anything at all. So what you have is the simple way to write it.

If you were doing something with a side effect like res(doSomethingAndReturnValue()) in the event handler or timeout handler, that wouldn't be a good idea because the doSomethingAndReturnValue() part still executes and has its side effects. But in your example you aren't doing that.


¹ Three fairly minor notes:

  1. I assume that when your event handler is called, some information is passed to the handler. Since you're using res directly as the handler, it will receive that information. So you have it set up to fulfill the promise with the value the event handler gets (if the event fires first), or with undefined (if the timer fires first), which means the code using it sees varying fulfillment values.

  2. Typically I'd expect a timeout to be a rejection rather than a fulfillment, but it depends on your use case.

  3. Your function doesn't need the async modifier, because it never uses await. A function doesn't need async just because it returns a promise. As of a normative change to the spec a year or so back, it no longer makes a difference if you return a native promise from an async function as you're doing (it used to delay things very briefly), but there's no reason for it.

Upvotes: 4

VLAZ
VLAZ

Reputation: 29087

Your code is correct. A promise can only be resolved once - if you resolve it twice, the second one is effectively a no-op.

With that said, as an alternative you can use Promise.race which has the same idea. It takes promises as input and will produce a promise that settles when the first of the input of promises settles.

For example, clicking the button here settles a promise but it also gets settled in 10 seconds:

let buttonResolve;
let buttonPromise = new Promise(res => buttonResolve = res);

document.querySelector("button")
  .addEventListener("click", buttonResolve)

let timedPromise = new Promise(res => setTimeout(res, 10000));
let start = Date.now();
Promise.race([buttonPromise, timedPromise])
  .then(() => console.log(`finished in ${(Date.now() - start) / 1000}s`))
<button>Click me</button>

Upvotes: 4

Related Questions