If I have the following code
const sleep = (ms) => new Promise((resolve) => {
setTimeout(resolve, ms);
});
// myFunction is some kind of network operation, a database fetch etc.
const myFunction = async (label) => {
console.log("enter " + label);
await sleep(500);
console.log("leave " + label);
}
// somewhere in the code, in some event handler
myFunction("a");
// somewhere ELSE in the code, in some event handler
myFunction("b");
The logging output will almost certainly be
enter a
enter b
leave a
leave b
So the second function call will be executed before the first finishes. I think I do understand why. await
is only syntactic sugar and not actually blocking.
Question
How can I guarantee that the nth call to myFunction
is being executed completely before the (n+1)th call starts? The logging output should be
enter a
leave a
enter b
leave b
- Is there a pattern?
- Is there any library I could use? (this node package seems to be unmaintained.)
Note: The function myFunction
might be a library function I cannot change.
Why await myFunction
does not work
The calls to myFunction
could be anywhere else, e.g. in a click event handler, so we cannot just await myFunction
. Which ever call enters first, should also leave the function body first (FIFO). As per this note, this is also NOT A DUPLICATE OF How can I use async/await at the top level?
If you use myFunction
inside a click handler like this
<button onClick="() => myFunction('button a')">hit me</button>
you cannot simply await myFunction
because if the user clicks several times myFunction
will be called several times before the first call returns.
3
Answers
Solution (Wrapper Function)
My take on a wrapper function
await
s the currently running function before another call to that function is being processed.When the first call is being processed and the wrapped function (read "Promise") is in pending state, any subsequent call will end up in the while loop and the until the first function returns. Then, the next wrapper function call on the stack leaves the while loop and calls the wrapped function. When the wrapped function is pending, the while loop runs one iteration for each message waiting on the stack.
General Usage
You would use it like this:
However, I can't say whether this is actually strictly FIFO. Is it guaranteed that the (n+1)-th wrapper function call will actually call the wrapped function as the (n+1)-th in line? I think that requires a deep understanding of the event loop.
Class Methods
What I also don't like about this: Class methods can only be defined in the constructor (or is there another way?):
However, each method is being executed completely before the method is being called again (on the same object - which makes sense).
Library Functions
Also, this works with library functions as well:
One idea like mentioned is create a promise queue.
example.
If you have multiple things being triggered separately you want to build a queue type of system. There are a few ways to write that type of way, but basic idea is to register functions into an array and call them after the previous one finishes.