I want to lock an async function from simultaneous execution. The function can be only executed by one caller and it should get true as output after execution where others get false as the return without executing the logical content of the function. Others should not wait for the current caller to complete.
This is what I tried so far:
class Lock {
constructor() {
this.locked = false;
}
lock(resolve) {
if (this.locked) {
resolve(false)
}
this.locked = true
return
}
release(resolve) {
this.locked = false;
resolve(true)
}
}
let lock = new Lock();
function myFunction() {
return new Promise(resolve => {
lock.lock(resolve);
//do something - this takes time includes some api calls and db operations
lock.release(resolve);
})
}
async function callSite() {
const executed = await myFunction();
if(executed){
//do something
}
else{
//do another thing
}
}
But seems like it doesn't work as expected. Can anyone help me to improve this?
Promise execution is not always guaranteed to stop immediately after resolving or rejecting; you'll have to return a boolean after you check the lock and exit or continue the promise accordingly.
Run the below snippet; it's an example having 3 buttons with the same promise and a 3 seconds delay. When one button is running all the others can not execute.
class Lock {
constructor() {
this.locked = false;
}
lock(resolve) {
if (this.locked) {
resolve(false);
return false;
}
this.locked = true
return true;
}
release(resolve) {
this.locked = false;
resolve(true)
}
}
let lock = new Lock();
function myFunction({ resultEl }) {
resultEl.textContent = "Is running";
return new Promise(resolve => {
// Check if it's locked
if (!lock.lock(resolve)) {
// If it is, return and exit the function even if the promise is already resolved
return;
}
// do something - this takes time includes some api calls and db operations
// just wait 3 seconds for demontration
setTimeout(() => {
lock.release(resolve);
}, 3000);
})
}
async function callSite() {
this.disabled = true;
const executed = await myFunction({ resultEl : this.nextSibling });
this.disabled = false;
if (executed) {
this.nextSibling.textContent = `Finished at ${(new Date).getTime()}`;
}
else {
this.nextSibling.textContent = `Was NOT executed at ${(new Date).getTime()}`;
}
}
document.getElementById('a-button').addEventListener('click', callSite);
document.getElementById('b-button').addEventListener('click', callSite);
document.getElementById('c-button').addEventListener('click', callSite);
div {
margin-bottom: 10px;
}
button {
margin-right: 5px;
}
<div>
<button id="a-button">Run A</button><span></span>
</div>
<div>
<button id="b-button">Run B</button><span></span>
</div>
<div>
<button id="c-button">Run C</button><span></span>
</div>