9

I have an issue with preventing double (multiple) eventListener handling in code:

var locked;

button.addEventListener("click", function() {
    if (locked) return;
    locked = true;
    calculateSomethingHeavy();
    locked = false;
}

Second immediate button click triggers another event, despite locked == true. Things like button.disabled = true or setTimeout(function() {locked = true;}, 0) have no effect because (I guess) second call is stacked and will be invoked only after first is fully handled. I think I'm missing some whole technology of asynchronous event handling. How to do this in pure js?

3
  • 1
    Is calculateSomethingHeavy heavy or asynchronous (or both)? Those are very distinct concepts Commented Jul 8, 2018 at 20:22
  • 1
    If calculateSomethingHeavy is meant to be asynchronous and you need locked to be set to false after it completes, then you need to return a Promise from calculateSomethingHeavy and set locked to false in its success handler. As written, this is synchronous code, so locked will be set to false immediately after calculateSomethingHeavy is called. Commented Jul 8, 2018 at 20:40
  • Actually, calculateSomethingHeavy is a bit tricky - the function fills a big canvas with pixels and after its first call it timeouts itself 99 more times to give some time for refreshing progress bars on the page (so each function call does 1% of all calculations). But the thing is, eventListener behaviour doesn't depend on existence of those timeouts, so I put simplified version here (I hope solution also doesn' depend on that) Commented Jul 9, 2018 at 21:50

1 Answer 1

8

The correct answer here depends on the definition of calculateSomethingHeavy. Presumably it's asynchronous based on the question title, but that could be implemented using callbacks, or events, or promises or async/await.

Regardless of which of those is at play here, what you need to do is ensure that locked is not set to false until after calculateSomethingHeavy has finished. That might look like the following in each case...

Callbacks

var locked;

button.addEventListener("click", function() {
    if (locked) return;
    locked = true;
    calculateSomethingHeavy(() => {
        locked = false;
    });
});

Events

var locked;

button.addEventListener("click", function() {
    if (locked) return;
    locked = true;
    calculateSomethingHeavy().on('finish', () => {
        locked = false;
    });
});

Promises

var locked;

button.addEventListener("click", function() {
    if (locked) return;
    locked = true;
    calculateSomethingHeavy()
        .then(() => {
            locked = false;
        });
});

async/await

var locked;

button.addEventListener("click", async function() {
    if (locked) return;
    locked = true;
    await calculateSomethingHeavy();
    locked = false;
});
Sign up to request clarification or add additional context in comments.

2 Comments

I just want to pose a question, what value does setting the locked = false in an asynchronous call? Even if you wanted to access locked in the outside global scope, it would still have it's original value...even though the event (and async call) has fired. Out of curiosity, is there any way to set globals in async calls and read thew new values in the outside scope?
@JabariDash, the value is in the if test at the top of the event handler. It is there to prevent re-entrancy, so if calculateSomethingHeavy is already calculating, the function becomes a NOP instead of calling it again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.