JavaScript EventEmitter链式调用解析:emit先于on执行仍生效的原因
为什么EventEmitter的
on()在emit()代码之后调用依然能生效? 先看你提供的代码示例:
var EventEmitter = require('events').EventEmitter; var fs = require('fs'); function findPattern(files, regex) { var emitter = new EventEmitter(); files.forEach(function(file) { fs.readFile(file, 'utf8', function(err, content) { if(err) return emitter.emit('error', err); emitter.emit('fileread', file); var match = null; if(match = content.match(regex)) match.forEach(function(elem) { emitter.emit('found', file, elem); }); }); }); return emitter; } findPattern( ['fileA.txt', 'fileB.json'], /hello \w+/g ) .on('fileread', function(file) { console.log(file + ' was read'); }) .on('found', function(file, match) { console.log('Matched "' + match + '" in file ' + file); }) .on('error', function(err) { console.log('Error emitted: ' + err.message); });
咱们拆解成两个问题来解释:
一、链式调用的原理
这个链式写法的核心逻辑非常直白:EventEmitter的on()方法(包括addListener()、once()这类事件绑定方法)执行后,会返回当前的EventEmitter实例本身。
你可以这么理解:每次调用emitter.on(...)时,方法内部完成事件监听器的绑定后,会把this(也就是当前的emitter对象)返回出去。这样一来,你就可以在调用完on()之后,直接用.衔接调用下一个on()或者其他EventEmitter方法,形成连续的链式写法。这种设计在JS生态里很常见,比如jQuery的链式调用也是同一个思路。
二、为什么代码里emit()写在on()前面,结果却不受影响?
这其实是Node.js异步I/O机制导致的,和链式调用本身关联不大,但链式调用让这个写法看起来更“反直觉”。
仔细看findPattern里的逻辑:
- 我们创建emitter实例后,立刻遍历文件调用
fs.readFile(),但readFile是异步操作——它不会立刻执行回调里的代码,而是把文件读取的任务交给系统内核处理,自己先退出主线程,继续执行后续代码。 - 主线程接下来会执行
findPattern(...)后面的所有.on()调用,把fileread、found、error这三个事件的监听器全部绑定到emitter上。 - 等到所有同步代码(也就是那些
.on()绑定操作)都执行完毕,Node.js的事件循环才会去处理之前readFile完成后的回调函数,这时候emitter.emit(...)才会真正触发事件。
换句话说,代码里emit()的位置虽然在on()前面,但实际执行顺序是on()先完成绑定,emit()才被触发,所以监听器能正常接收到事件并输出结果。
如果把readFile换成同步的fs.readFileSync,结果就完全不同了——同步读取会立刻执行后续的emit(),这时候on()还没完成绑定,事件触发后不会有任何监听器响应。
内容的提问来源于stack exchange,提问作者arvin
相关产品推荐
相关产品推荐

