1

I'm new to Node.JS and I started to read several topics about Node.js event handling and concurrency.

The bit of code following fires 1000 times an event, which is handled by incrementing a global variable. The first bit of code successfully counts to 1000, but the second bit doesn't even increment to 2. Is there any atomicity I don't get ?

I'm still wondering why the following code gives a coherent output (it counts successfully to 1000 without any concurrency:

// Import events module
var events = require('events');

// Event emitter
var eventEmitter = new events.EventEmitter();

var max = 1000;

var count = 0;

eventEmitter.on('event', () => {
    let time = Math.trunc(Math.random() * 1000) +1;
    setTimeout(() => {  
        var c = count;
        c++;
        count = c;
        console.log(c);  
    }, time);
});

// Fire
for (let i = 0; i < max; i++) {
    eventEmitter.emit('event');
}

console.log('Program Ended.');

Is there any atomicity I don't get ?

But when I move the var creation & assignation, the output is radically different (the final result is 1).

// Import events module
var events = require('events');

// Event emitter
var eventEmitter = new events.EventEmitter();

var max = 1000;

var count = 0;

eventEmitter.on('event', () => {
    let time = Math.trunc(Math.random() * 1000) +1;
    var c = count;
    setTimeout(() => {  

        c++;
        count = c;
        console.log(c);  
    }, time);
});

// Fire
for (let i = 0; i < max; i++) {
    eventEmitter.emit('event');
}

console.log('Program Ended.');

Any advice ? Any reading ?

Have a nice day !

Cwellan
  • 305
  • 1
  • 3
  • 12
  • `setTimeout` is *not* atomic. – Bergi Aug 21 '17 at 14:29
  • Why, in the first case, does that successfully count to 1000 ? If it's not atomic, something like "read; read; write; write;" should happen, shouldn't it ? – Cwellan Aug 21 '17 at 15:24
  • The callback *inside* the `setTimeout` is atomic again. Have a look at [this](https://stackoverflow.com/a/7238663/1048572), [this](https://stackoverflow.com/a/29713827/1048572) and [that](https://stackoverflow.com/a/8611469/1048572). The point is that *between* scheduling the timeout and *asynchronously* executing the timeout callback anything else can happen. – Bergi Aug 21 '17 at 15:26
  • Wow, that's exactly what I needed ! Thank you **a lot**. – Cwellan Aug 21 '17 at 15:33

2 Answers2

1

From Bergi's provided external resources:

All Javascript event handler scripts are handled from one master event queue. This means that event handlers run one at a time and one runs until completion before the next one that's ready to go starts running. As such, there are no race conditions in Javascript. Any individual thread of execution in javascript will run to completion before the next one starts.

From this.

And:

One event will run to completion before the next event is triggered. So, if a second click occurs while the first is still processing, the second click will be queued and will not run until the code processing the first one is done.

From that

Cwellan
  • 305
  • 1
  • 3
  • 12
0

The eventemitter emits all events in one go initially when the code starts . So on 'event' is triggered immediately after starting the programm. All the timeout callbacks are only called after all the event handler registration is done. Put a console.log in your on event callback (not in the settimeout callback) and see the output.

In the second code snippet, all of the settimeout callbacks captures count = 0 because

var c = count;

is outside the settimeout callback and hence callback increments it to 1.

But in the first code snippet count is read within callback

var c = count; 

so each of the callbacks have the updated values.

Malice
  • 1,457
  • 16
  • 32
  • Hello Malice, and thank you for your answer ! Do you mean that the anonymous callback function in setTimeout doesn't imply any concurrency between different event handling ? I mean, why is it impossible to have a read; read; write; write; ? – Cwellan Aug 21 '17 at 15:10
  • Does the code always executes this way: 1/ The javascripts executes and queues events in a list. 2/ Events called "event" are sequentially handled and queues "timeout" events. 3/ The timeout callback is executed for every event queued – Cwellan Aug 21 '17 at 15:13
  • The event emission is not a async call ( meaning it will not give up event loop), so your event emission loops will complete in one go, your `settimeout` calls never get called in between the event emission. You might want to split up your event emitting loop into many pieces, say 4 each of length `max/4` , and then call these loops in a callback for another `settimeout` , with random timing. You will run run into different results each time is my guess – Malice Aug 21 '17 at 16:51
  • An important takeaway is that your event emission loop never gives up the `event loop`(this is nodejs internal loop), so it can never check whether a timeout has occured or not for one of the callbacks... – Malice Aug 21 '17 at 16:56