EventEmitter是否能避免回调地狱
发布于 5 年前 作者 zouzhenxing 5256 次浏览 来自 分享

今在我有个学生去面试,面试官问:如何避免回调地狱? 学生回答:使用EventEmitter对象可以避免 面试官:错,不能。要使用Promise。

我的学生回来问我:兴哥EventEmitter真的能解决回调地狱吗? 我不知道怎么回答,因为我一直用EventEmitter来编写异步回调的。node4.X版本中的文件操作不都是支持EventEmitter吗?

exports.copyStream = function() {
	var et = new events.EventEmitter();
	
	et.on("cpstart",function() {
		fs.open("home.js","r",function( err,fd ) {
			if( err ) {
				return console.log(err);
			}
			
			console.log("打开home.js文件成功");
			et.emit("cpread",fd);
		});
	}).on("cpread",function( fd ) {
		var buf = new Buffer(1024);
		fs.read(fd,buf,0,buf.length,0,function( err, bytes ){
			if( err ) {
				return console.log(err);
			}
			
			console.log("一共读取了" + bytes + "个字节");
			et.emit("cpwrite","home_stream_1.js",buf,parseInt(bytes));
			et.emit("cpclose",fd,"home.js文件");
		});
	}).on("cpclose",function( fd,msg ){
		fs.close(fd,function( err ) {
			if( err ) {
				return console.log(err);
			}
			console.log(msg + "关闭成功");
		});		
	}).on("cpwrite",function(filename, buf, bytes ) {
		fs.open(filename,"w",function( err,fd ) {
			if( err ) {
				return console.log(err);
			}
			console.log("打开" + filename + "成功");
			fs.write(fd,buf,0,bytes,function( err,written ) {
				if( err ) {
					return console.log(err);
				}
				
				console.log("一共写入了" + written + "个字节");
				et.emit("cpclose",fd,filename + "文件");
			});
		});
	}).emit("cpstart");
}

这样算不算解决了回调地狱的问题,大家教教我。

17 回复

EventEmitter只能较初级地解决callback hell。

唉,这让我想起在我写Java那会,Hibernate大行其道,所有人都去使用HQL时,好多人忘了JDBC如何去写。在Spring大行其道时,又有好多人回归到JDBC的怀抱。 说实在的,我并没有觉得使用了Promise之后,回调就晰了。我个人认为,Promise只有二种状态,其实并不能完美解决好多问题。反而EventEmitter给了我们编写代码时的灵活性。 其实说白了,就是如何组织回调链的问题,想清楚,用什么方法实现不了呢。

不过,尽管 PubSub 模式如此多 才多艺,但它不是适用于所有任务的万能工具。PubSub 模式尤其不 适用于一次性事件,一次性事件要求对异步函数执行的一次性任务的 两种结果(完成任务或任务失败)做不同的处理。(Ajax 请求就是常 见的一次性事件实例。)用于解决一次性事件问题的工具叫做 Promise,这正是下一章的主题。 ——《Javascript异步编程》

推荐读一下

@sleelily 谢谢分享 我并不排斥Promise,它的存在必然是解决某些问题的。

这不是很优美吗?那个面试的水平有限。

@JacksonTian 我现在绕了一圈,还是喜欢用 eventproxy

@alsotang 避免回调地狱的方案其实挺多的。event emitter无疑还是最底层的一种,但很多场景还是离不开它。

@JacksonTian promise话太多,async/co 很多时候还是要用 promise 包一层 api。

反正所有 api 都支持 callback,就干脆继续 eventproxy 了。

@JacksonTian 而且 promise 当 chain 起来的时候,要保证自己不会漏了错误处理。要按 promise 的最佳实践把各个 promise 合理地串起来,心智负担也挺重的。

EventEmitter和Promise都可以解决callback hell,个人觉得Promise从形式上来说更加像阻塞的代码,更加容易读。 如果你的业务涉及多个异步调用,并且它们之间有先后依赖关系的话(例如先请求网络,然后写数据库,最后写本地文件),用EventEmitter实现会让逻辑不连贯。但是用promise的话就很好看了,它会显示成这样

sendRequest()
	.then(writeDB)
	.then(writeFile)
	catch(error)

如果用EventEmitter的话就或者会变成这样:

sendRequest.on("end", ()=>e.emit("writeDB"))
writeDB.on("writeDB", ()=>e.emit("writeFile"))
writeFile.on("writeFile", ()=>{})

比较一下明显promise更加易读。

如果一个异步调用返回了EventEmitter,我会觉得神烦,如果返回了一个promise我会觉得很爽,可能是因为我在项目中大量使用了promise的缘故,已经习惯了promise的开发方式。

那个面试的水平有限。

@JacksonTian 主要代码写起来比较简单,不用去理解promise这种概念…就是有时候有些冗长~

我感觉promise比较好的地方在于,作为变量可以被传递。

暂时用Co+Promise+Generator这样写起来很清晰,像同步的写法,到ES7就Async/Await

回到顶部