关于事件注册和事件循环的javascript混淆

时间:2013-07-18 09:04:19

标签: node.js javascript-events event-loop

我试了很长时间才弄清楚事件和事件循环是如何工作的。我在网上阅读了很多关于事件注册和事件循环的文章,但我仍然无法想象它是如何工作的。

我正在阅读Prgmatic Bookshelft关于事件队列的Async JavaScript一书,你可以在书中看到一篇文章。

> When we call setTimeout, a timeout event is queued. Then execution
> continues, the code after the setTimeout call runs and so on, until no
> code more left. After then it will start the event loop and detect,
> which event get fired and it will call the handler.

在这篇文章之后,我对javascript中的事件和异步有了一点想象力。然后我自己尝试以下代码:

setTimeout(function(){alert("Hello")},0);

for (var i = 0; i <= 100000; i++) {
    console.log('I am loop');
}

我已将时间设为0,因为我想确认这句话

  

the code after the setTimeout call runs and so on..

  

After then it will start the event loop and...

直到这里,一切都很好javascript它的工作原理。然后我继续阅读Async JavaScript书籍,并阅读了以下内容:

  

输入事件的工作方式相同:当用户点击DOM元素时   附加了一个点击处理程序,一个点击事件排队。但是处理程序   在所有当前运行的代码完成之前不会执行。

第一句话对我来说有点混乱。例如:

//Some codes
#("element").click(function(e){
    //Do something
});
//Some codes

我在这里不明白的是,什么时候编译器会在事件队列中保存click事件?它会在注册后保存在队列中(在此代码中)或者当我点击元素时?

在节点中,我尝试使用此代码,以了解事件的工作原理。请考虑以下代码。

var events = require("events");
var event = new events.EventEmitter();

event.on('ev1', function () {
    console.log('Into ev1');

});

event.emit('ev1');
for (var i = 0; i <= 5; i++) {
    console.log('I am in the loop');
}

console.log('I am finish with loop');

我期待的是:

I am in the loop    
I am in the loop    
I am in the loop    
I am in the loop    
I am in the loop    
I am in the loop    
I am finish with loop    
Into ev1    

但我得到的是:

Into ev1
I am in the loop
I am in the loop
I am in the loop
I am in the loop
I am in the loop
I am in the loop
I am finish with loop

在这个输出之后,它打破了关于事件(异步)执行和事件循环的所有想象,我之前阅读过书中的文章和文章。请考虑书中的这句话:

  

setTimeout调用之后的代码运行,因此,直到没有更多的代码   剩下。之后它将启动事件循环并检测哪个事件   被解雇,它会调用处理程序。

但为什么上面与EventEmitter的代码相反?我认为事件循环将在结束时执行(在完成其他代码之后)。

我在互联网上读过关于数据库(例如mongoose)查询与节点是异步的。我不知道异步数据库查询是如何确定的,但我的成像是这样的:
Database request and response

请解释一下,javascript中的事件是如何运作的?

1 个答案:

答案 0 :(得分:1)

setTimeoutEventEmitter略有不同,因为您获得了这些结果。首先,我会请你先运行这些例子。

将其保存在async.html中并在浏览器中运行。检查控制台以查看结果。

<div id="element">
</div>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
setTimeout(function(){console.log("Hello")},0);
$("#element").click(function(e){
    console.log("Inside");
});
$("#element").click();
for (var i = 0; i < 5; i++) {
    console.log('I am loop');
}
</script>

输出

Inside
I am loop
I am loop
I am loop
I am loop
I am loop
Hello

现在将其保存在async.js中并在node.js中运行

setTimeout(function(){console.log("Hello")},0);

for (var i = 0; i < 5; i++) {
    console.log('I am loop');
}

输出

I am loop
I am loop
I am loop
I am loop
I am loop
Hello

您会发现setTimeout的工作方式与EventEmitter略有不同。 EventEmitter立即执行处理程序,而setTimeout尝试立即执行它。直到下一个滴答时才执行操作。阅读它here

不要将setTimeout与EventEmitter混合使用。

修改

您的示例并未充分展示异步。您只是在事件处理程序中运行循环。这是一个更清晰的例子:

var events = require("events");
var event = new events.EventEmitter();

event.on("work", function (i,cb) {
    console.log('Started '+i );
    setTimeout(function(){event.emit("done",i,cb);},Math.random()*1000);
});

event.on("done", function (i,cb) {
    cb(i);
});

var async = function (cb) {
    for (var i = 0; i <= 100; i++) {
        emitclosure(i,cb);
    }
}

function emitclosure(i,cb){
        setTimeout(function(){event.emit("work",i,cb)},Math.random()*1000);
}

async(function (i) {
    console.log("I have done "+i);
});

我想指出,我只使用setTimeout和事件来感受工作到达和处理延迟的延迟,这与他们在问题中的同义用法不同。 emitclosure只是循环变量的闭包,因此它们的范围是安全的。