2

Let's say I have a button with an event handler:

<button onclick="btnClicked()"> CLICK ME </button>

and this is the js file:

var num = 0; // global variable

function btnClicked() { 
   num++;
   console.log(num);
}

Let's say that I clicked twice on the same button. I expect to see in console 1 and then 2.

But, it's not necessary that this is what I will get. Each button click invokes the function btnClicked asynchronously, so there is a possible scenario that the second call will end before the first call ends. e.g. The first invocation calls btnClicked, passes the line num++ and then holds (for some reason). Meanwhile, second invocation enters the function that num = 1, increments it to 2, prints 2 in console, and then the first invocation continues its execution with num=2 and therefore also prints 2.

Do you know any way to make sure that second invocation will occur only after first invocation ends ?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
CrazySynthax
  • 13,662
  • 34
  • 99
  • 183
  • 2
    `so there is a possible scenario that the second call will end before the first call ends` - erm, not with that code – Jaromanda X Sep 05 '17 at 06:01
  • 2
    JavaScript has the concept of "running to completion". I.e. code that's currently running cannot be interrupted. https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop *"Each button click invokes the function btnClicked asynchronously"* Clicking on the button will add the handler to the message queue. Only if the JavaScript engine doesn't have anything to do will it process the next message in the queue. – Felix Kling Sep 05 '17 at 06:03
  • Who interrupts the running code in this scenario? – CrazySynthax Sep 05 '17 at 06:05
  • Not sure I understand the question. The execution cannot be interrupted. – Felix Kling Sep 05 '17 at 06:08
  • It's not interrupted. Each execution is a thread that is executed in different pace – CrazySynthax Sep 05 '17 at 06:12
  • 1
    JavaScript is single threaded (in browsers at least). There can always only be one thing that is executed, and that thing is executed *to completion*. Again, have a look at the link I posted. – Felix Kling Sep 05 '17 at 06:13

4 Answers4

1

After reading the responses I understood that the scenario that I describes (console will print "2" twice) is not possible.

Event handlers are added to event queue. Event Queues are not V8 threads. Therefore, each task in event queue will execute synchronously, i.e. only after the first invocation ends the second one will start.

However, if the function were:

function btnClicked() { 
   num++;
   console.log(num);
   **AJAX call** 
}

then the execution order would be: print: 1 AJAX call sent for first invocation print 2 AJAX call sent for second invocation

and then in this case both AJAX calls take place in different threads and we cannot know for sure which one of them will ends first.

CrazySynthax
  • 13,662
  • 34
  • 99
  • 183
0

On the button click, just disable the button and at end of the function enable the button. Can you try this. I think it will work for you.

0

While it may not address your problem directly, it is recommended to use eventListener instead of onclick.

Also, to make a variable global you would call it without 'var' -- perhaps not exactly something you'd want to do with something generic like 'num', but that would make it global (more on that here).

This would give you something like:

<button id='click-me'> CLICK ME </button>

<script>
    num = 0; // global variable
    document.getElementById('click-me').addEventListener('click', function(){
        // function here
    });
</script>
Frish
  • 1,371
  • 10
  • 20
0

You just need a promise for solving the order issue. Events are going each by each. But if you have an ajax request, and you need to send ajax requests each by each after you got a response from a server. And you send ajax request by a button click (asynchronous execution of functions by certain order, each by each). So here is a working example with fake execution function.

var num = 0;

var executing = new Promise(function(resolve, reject){
     resolve();
});

var btnHandle = function(){
  executing.then(createJobInstanse());
}

var createJobInstanse = function(){
 return function(){
      return new Promise(function(resolve, reject){
        // fake asynchronous operation from 10ms to 2000ms, with random execution time
        window.setTimeout(function(){
          console.log(num++);
          resolve();
        }, Math.floor(Math.random() * 2000) + 10);
      });
 };
}

  

var btn = document.querySelector("#btn");
btn.addEventListener("click",btnHandle);
<button id="btn" onclick="btnHandle">click</button>
Alex Nikulin
  • 8,194
  • 4
  • 35
  • 37