Async synchronize
If you have a Java background, you might already be familiar with the synchronized
keyword. It makes sure a function can only be executed by one of the threads at any single time.
Javascript is single-threaded, so this kind of synchronization is a non-issue here. But the same concept can be applied to asynchronous blocks. That is, when you want to make sure the execution of an async block can only start when the previous one is finished.
Why would you need it?
You might need to send a request to a target that does not support concurrent connections. That might happen when a web worker maintains internal state.
Or you have an error case that affects concurrent requests and you don't want to implement complex logic to handle it. As an example, a token that you need to refresh from time to time.
The solution
First, you need a function that maintains its internal state. This can be achieved with a closure:
const synchronize = (() => {
let chain = ...;
...
return () => {}
})();
This creates a function that has access (and can also update) the chain
variable.
Then you need to maintain the chain of promises and run them sequentially:
const synchronize = (() => {
let chain = Promise.resolve();
return async (promise) => {
return chain = chain.then(promise);
}
})()
Now when you call this functions, subsequent invocations will wait for an earlier to finish:
synchronize(async () => {
log("Block 1 start");
await wait(500);
log("Block 1 end");
})
synchronize(async () => {
log("Block 2 start");
await wait(500);
log("Block 2 end");
})
Prints:
Block 1 start
Block 1 end
Block 2 start
Block 2 end
The result value is also handled correctly:
const result = await synchronize(async () => {
return 6
})
log(result) // 6