Reputation: 103
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
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:
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.
Typically I'd expect a timeout to be a rejection rather than a fulfillment, but it depends on your use case.
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
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