16

I am new in node and try to use async and event behavior advantages in node. I used to understand from node, everything that handle with Event object, it gonna be async execution.
Then i had try this, consider following code:

var events = require("events");

var event = new events.EventEmitter();


event.on("work", function () {

    for (var i = 0; i <= 10; i++) {
        console.log("I do my work " + i);
    }

    event.emit("done");
});

var async = function (cb) {

    event.on("done", cb);
    event.emit("work");
    for (var i = 0; i <= 10; i++) {
        console.log("Async " + i);
    }
}


async(function () {
    console.log("I am done callback!");
});  

This is async execution? In my opinion no! Why, because i had read this sentence many:

An event is fired, so go and do something and then when you have finished it, come back and tell me, but in meanwhile i will do something else.

Like a fast food restaurant scenario. But the code above, when the event work gonna fired, following sequence will happen:

  1. go into the callback
  2. let through the loop
  3. output I do my work n
  4. fired the done event
  5. output I am done callback!
  6. output Async n

why I am done callback! gonna output before Async n? Why is here not like fast food restaurant scenario, like

the work event is fired, go and do you stuff and come back when you done, in meanwhile i will output Async n

This is i used to understand about event driven behavior and async in node. But now i am very confused. I know, javascript works on single thread. How can i write an async function with event emitter? But i think is not possible, because when i emit an event, it will immediately execute the handler.

softshipper
  • 32,463
  • 51
  • 192
  • 400

1 Answers1

23

I used to understand from node, everything that handle with Event object, it gonna be async execution.

This is incorrect. Events are synchronous. When you add a listener, you're just saving the callback in an object:

this._events[type].push(listener);

When you emit an event, you're just iterating an array and calling each listener:

for (i = 0; i < len; i++)
      listeners[i].apply(this, args);

Source code: https://github.com/joyent/node/blob/master/lib/events.js

This is async execution? In my opinion no!

You are correct. It's async if you call any I/O function or use setImmediate, nextTick or a timer—otherwise, it's synchronous. A synchronous code being written asynchrously doesn't convert it to an asynchrous code.

why I am done callback! gonna output before Async n?

Because when you receive the "done" callback you call to "cb":

event.on("done", cb);

When cb returns, the "Async n" loop is executed.

How can i write an async function with event emitter?

Using setImmediate or process.nextTick.

If you want to postpone the "I do my work" loop execution, you can wrap the line events.emit ("work") with nextTick.

var events = require("events");

var event = new events.EventEmitter();


event.on("work", function () {

    for (var i = 0; i <= 10; i++) {
        console.log("I do my work " + i);
    }

    event.emit("done");
});

var async = function (cb) {

    event.on("done", cb);
    process.nextTick (function () {         //<-----
        event.emit("work");
    });                                     //<-----
    for (var i = 0; i <= 10; i++) {
        console.log("Async " + i);
    }
}


async(function () {
    console.log("I am done callback!");
});  

This will print:

Async 0
Async 1
Async 2
Async 3
Async 4
Async 5
Async 6
Async 7
Async 8
Async 9
Async 10
I do my work 0
I do my work 1
I do my work 2
I do my work 3
I do my work 4
I do my work 5
I do my work 6
I do my work 7
I do my work 8
I do my work 9
I do my work 10
I am done callback!
Raxvan
  • 6,257
  • 2
  • 25
  • 46
Gabriel Llamas
  • 18,244
  • 26
  • 87
  • 112
  • The event loop will be execute again, after the codes in the stack has been completed. For example, i have 3 lines of code. The first is process.nextTick(dosomething) and other just console.log("something");. Then the event loop will be execute right? And at the beginning of this loop, the callback that registered in process.nextTick() will be execute. Right? – softshipper Jul 19 '13 at 12:49
  • look at the following code, when i call /test1 and in other browser /test2, it need a long time to get response on /test2, for me it is like /test1 block /test2 var express = require('express'); var app = express(); app.get('/', function(req, res){ res.send('Hello World'); }); app.get('/test1', function(req, res){ for(var i = 0; i<= 100000000000; i++){ } res.send('The loop is finished'); }); app.get('/test2', function(req, res){ res.send('No loop'); }); app.listen(3000); console.log('Express started on port 3000'); – softshipper Jul 19 '13 at 18:39
  • 2
    /test1 is "blocking" the event loop. That's why node is not for cpu intensive tasks. You could spawn a new process and do there these tasks. Read this article to fully understand concurrency in node: http://neilk.net/blog/2013/04/30/why-you-should-use-nodejs-for-CPU-bound-tasks/ – Gabriel Llamas Jul 20 '13 at 09:12