身为重度Node.js用户,我也来说说我的理解吧。
计算机语言和技术平台很多时候不是谁更好谁更差的问题,而是谁更合适的问题,这里我们从语言和生态系统两个维度来坐下简要的对比。
语言的对比
JavaScript和Java从语言的设计上来说,虽然都含有Java但是是完全不同的两种语言,简单的说:
JavaScript
JavaScript是其实没法简单的用一句话来解释其设计,大了说,我们可以称他为一种多范式的语言(通常这个词用在C++身上),小了说我们可以把它当成是一种基于原型链的面向对象动态语言来看待。正是这种“灵活性”给各种奇技淫巧带来了可能性,表面上这减少了代码量,给码农们带来了幸福,而实际上,正是因为这种灵活性导致了程序在运行过程中很容易出现难以调试的Bug,而Bug的日志会让人摸不着头脑。
再者JavaScript设计上对异步没有很好的支持,对于Node这种完全异步的平台而言,编写复杂逻辑的应用程序简直就是灾难,举个例子:
通常,同步的逻辑是这样的:
function getForgeMetadata(cid, itemId) {
var character = getCharacter(cid);
var item = getItem(itemId);
var itemDesc = getItemDesc(item.type);
var forgeLevel = getForgeLevel(item);
return getForgeMetadata(character, itemDesc, forgeLevel);
}
然而,如果我们采用node.js中标准的回调格式来编写代码的话,您的代码会变成这样:
function getForgeMetadata(cid, itemId, cb) {
getCharacter(cid, function(err, character) {
if (err) return cb(err);
getItem(itemId, function(err, item) {
if (err) return cb(err);
getItemDesc(item.type, function(err, itemDesc) {
if (err) return cb(err);
var forgeLevel = getForgeLevel(item);
getForgeMetadata(character, itemDesc, forgeLevel, cb);
});
});
});
}
看到那一长串的if (err) return cb(err);你会不会有种想要自杀的感觉,后来终于社区也看不下去了就弄了个async库出来,但基本上差别也不太大,再后来有了promise(通常使用库q来实现),情况略有好转,代码可以变成这样:
function getForgeMetadata(cid, itemId) {
return getCharacter(cid)
.then(function(character) {
return getItem(itemId)
.then(function(item) {
return {'character': character, 'item': item};
});
};
}).then(function(meta) {
return getItemDesc(meta.item.type)
.then(function(itemDesc) {
meta.itemDesc = itemDesc;
return meta;
});
}).then(function(meta) {
var forgeLevel = getForgeLevel(meta.item);
return getForgeMetadata(meta.character, meta.itemDesc, forgeLevel);
});
}
然而这坑爹货的代码还是一样的难读和难以维护啊,终于有一个人看不下去了,他就是 @赵颉,他把C#中的await语义移植到了js中,弄了个Wind.js出来,这样大家终于能写人话了:
function getForgeMetadata(cid, itemId) {
var character = $await(getCharacter(cid));
var item = $await(getItem(itemId));
var itemDesc = $await(getItemDesc(item.type));
var forgeLevel = getForgeLevel(item);
return $await(getForgeMetadata(character, itemDesc, forgeLevel));
}
然而顽固不化的Node.js社区断然拒绝了这绝妙的设计,老赵也停止了Wind.js的开发,最终版本的Wind.js也没加入只差一步的调试功能,我们刚进到蜜罐就又被迫迁移到promise上来,继续痛苦的旅程。
如果你认为这就是Node.js的唯一噩梦的话,你就太天真了。完全异步化带来的另一个问题就是调试困难。由于异步过程中的循环是利用回调来实现的,如果你恰好有个本地额缓存,然后缓存命中时就直接同步调用了回调函数,那么恭喜你,你会遇到栈溢出的问题(假设循环10000次,将会有10000*n的栈深度,想象一下吧),所以通常情况下,大家会采用setImmediate函数来调用回调函数,其结果是,发生错误时的栈信息可能是类似这样的:
Cannot call undefined
at onImmediate
(不准确,大概就这意思)
可是你该怎么知道是哪个坑爹的漏传了这个cb函数导致它是undefined?
除此之外,JavaScript设计上是非常不严谨的一门语言,导致有大量陷阱存在,你试着做做这里的题目就明白这玩意儿有多坑了。
http://javascript-puzzlers.herokuapp.com
Java
说完了JavaScript的部分,我们再来看看Java。Java的定义很简单,它是一种面向对象的静态语言。然而坑爹的sun(oracle)在java语言的成长之路上做出了一个又一个的错误决定,其中首当其冲的就是采用类型抹除的方式实现泛型,简单的说List<string>和List<WhatTheFuckClass>居然是同一个类型。
同样的Java也没有await语义的支持,所以你看到的Java程序通常情况下是同步的,基本上有2种方式,要嘛来一个请求,开一个线程,要嘛一个固定大小的线程池,请求在池中逐个执行,反正总归会有线程因为IO或者其他原因处在sleep状态,其结果就是没办法(很难)实现单服高并发的承载能力。
再者由于Java的表达力实在不怎么样,所以通常情况下Java代码又臭又长。
平台的对比
Node.js相对来说是一个比较优秀的平台,从很早的版本开始(记得是0.6还是0.8)就已经把npm作为默认组件打包在一起了。npm对于Node.js生态圈的意义在于,用户再也不需要手工管理依赖关系,对于刚开始使用的用户来说,也非常容易搭建起工作环境。你可以直接使用npm install package_name来安装包,也可以直接在package.json中声明,然后使用npm install来自动解析和安装依赖,这绝对是相对其他平台优秀很多的地方。
然后Node.js的执行环境有着不可避免的局限性,就是它终究是个单线程的执行环境,没法直接发挥多核、多CPU的服务器的优势。所以通常大型Node.js项目会采用多进程的架构(比如网易的pomelo,我们公司的游戏服务器也是类似的架构),而不同的进程间采用restful的接口进行通信,而这样的代价就是花费大量的CPU时间在JSON序列化和反序列化的过程中,总之就是坑啊。
Java的生态环境可以说是非常优秀,不可否认的,Java平台拥有这个世界上最完善的,而且是经过生产环境验证的工业级的库。不过由于设计上就是同步的语言,也没有C#中的await机制,整个平台都是同步的,对于高并发的环境来说,确实做起来会有一定的困难。
结论
1)Node.js和Java本身不产生竞争关系
2)他们谁也不会消亡,如果非要说谁更可能消亡的话,我觉得是Node.js
3)如果你要做的是小型项目,或者逻辑非常简单的项目,可以选择Node.js,因为开发、调试都很很快。
4)如果你要做的是逻辑简单,而并发大的项目,可以选择Node.js,它还是很适合左右游戏的聊天服务器,长连接消息推送服务器之类的项目的。
5)如果你在做一个大规模项目(5人以上协作),还是用C#吧,C#并不是跟Windows绑定的语言,非windows平台上的Mono也非常好用。
希望这洋洋洒洒的胡言乱语对你有用~
计算机语言和技术平台很多时候不是谁更好谁更差的问题,而是谁更合适的问题,这里我们从语言和生态系统两个维度来坐下简要的对比。
语言的对比
JavaScript和Java从语言的设计上来说,虽然都含有Java但是是完全不同的两种语言,简单的说:
JavaScript
JavaScript是其实没法简单的用一句话来解释其设计,大了说,我们可以称他为一种多范式的语言(通常这个词用在C++身上),小了说我们可以把它当成是一种基于原型链的面向对象动态语言来看待。正是这种“灵活性”给各种奇技淫巧带来了可能性,表面上这减少了代码量,给码农们带来了幸福,而实际上,正是因为这种灵活性导致了程序在运行过程中很容易出现难以调试的Bug,而Bug的日志会让人摸不着头脑。
再者JavaScript设计上对异步没有很好的支持,对于Node这种完全异步的平台而言,编写复杂逻辑的应用程序简直就是灾难,举个例子:
通常,同步的逻辑是这样的:
function getForgeMetadata(cid, itemId) {
var character = getCharacter(cid);
var item = getItem(itemId);
var itemDesc = getItemDesc(item.type);
var forgeLevel = getForgeLevel(item);
return getForgeMetadata(character, itemDesc, forgeLevel);
}
然而,如果我们采用node.js中标准的回调格式来编写代码的话,您的代码会变成这样:
function getForgeMetadata(cid, itemId, cb) {
getCharacter(cid, function(err, character) {
if (err) return cb(err);
getItem(itemId, function(err, item) {
if (err) return cb(err);
getItemDesc(item.type, function(err, itemDesc) {
if (err) return cb(err);
var forgeLevel = getForgeLevel(item);
getForgeMetadata(character, itemDesc, forgeLevel, cb);
});
});
});
}
看到那一长串的if (err) return cb(err);你会不会有种想要自杀的感觉,后来终于社区也看不下去了就弄了个async库出来,但基本上差别也不太大,再后来有了promise(通常使用库q来实现),情况略有好转,代码可以变成这样:
function getForgeMetadata(cid, itemId) {
return getCharacter(cid)
.then(function(character) {
return getItem(itemId)
.then(function(item) {
return {'character': character, 'item': item};
});
};
}).then(function(meta) {
return getItemDesc(meta.item.type)
.then(function(itemDesc) {
meta.itemDesc = itemDesc;
return meta;
});
}).then(function(meta) {
var forgeLevel = getForgeLevel(meta.item);
return getForgeMetadata(meta.character, meta.itemDesc, forgeLevel);
});
}
然而这坑爹货的代码还是一样的难读和难以维护啊,终于有一个人看不下去了,他就是 @赵颉,他把C#中的await语义移植到了js中,弄了个Wind.js出来,这样大家终于能写人话了:
function getForgeMetadata(cid, itemId) {
var character = $await(getCharacter(cid));
var item = $await(getItem(itemId));
var itemDesc = $await(getItemDesc(item.type));
var forgeLevel = getForgeLevel(item);
return $await(getForgeMetadata(character, itemDesc, forgeLevel));
}
然而顽固不化的Node.js社区断然拒绝了这绝妙的设计,老赵也停止了Wind.js的开发,最终版本的Wind.js也没加入只差一步的调试功能,我们刚进到蜜罐就又被迫迁移到promise上来,继续痛苦的旅程。
如果你认为这就是Node.js的唯一噩梦的话,你就太天真了。完全异步化带来的另一个问题就是调试困难。由于异步过程中的循环是利用回调来实现的,如果你恰好有个本地额缓存,然后缓存命中时就直接同步调用了回调函数,那么恭喜你,你会遇到栈溢出的问题(假设循环10000次,将会有10000*n的栈深度,想象一下吧),所以通常情况下,大家会采用setImmediate函数来调用回调函数,其结果是,发生错误时的栈信息可能是类似这样的:
Cannot call undefined
at onImmediate
(不准确,大概就这意思)
可是你该怎么知道是哪个坑爹的漏传了这个cb函数导致它是undefined?
除此之外,JavaScript设计上是非常不严谨的一门语言,导致有大量陷阱存在,你试着做做这里的题目就明白这玩意儿有多坑了。
http://javascript-puzzlers.herokuapp.com
Java
说完了JavaScript的部分,我们再来看看Java。Java的定义很简单,它是一种面向对象的静态语言。然而坑爹的sun(oracle)在java语言的成长之路上做出了一个又一个的错误决定,其中首当其冲的就是采用类型抹除的方式实现泛型,简单的说List<string>和List<WhatTheFuckClass>居然是同一个类型。
同样的Java也没有await语义的支持,所以你看到的Java程序通常情况下是同步的,基本上有2种方式,要嘛来一个请求,开一个线程,要嘛一个固定大小的线程池,请求在池中逐个执行,反正总归会有线程因为IO或者其他原因处在sleep状态,其结果就是没办法(很难)实现单服高并发的承载能力。
再者由于Java的表达力实在不怎么样,所以通常情况下Java代码又臭又长。
平台的对比
Node.js相对来说是一个比较优秀的平台,从很早的版本开始(记得是0.6还是0.8)就已经把npm作为默认组件打包在一起了。npm对于Node.js生态圈的意义在于,用户再也不需要手工管理依赖关系,对于刚开始使用的用户来说,也非常容易搭建起工作环境。你可以直接使用npm install package_name来安装包,也可以直接在package.json中声明,然后使用npm install来自动解析和安装依赖,这绝对是相对其他平台优秀很多的地方。
然后Node.js的执行环境有着不可避免的局限性,就是它终究是个单线程的执行环境,没法直接发挥多核、多CPU的服务器的优势。所以通常大型Node.js项目会采用多进程的架构(比如网易的pomelo,我们公司的游戏服务器也是类似的架构),而不同的进程间采用restful的接口进行通信,而这样的代价就是花费大量的CPU时间在JSON序列化和反序列化的过程中,总之就是坑啊。
Java的生态环境可以说是非常优秀,不可否认的,Java平台拥有这个世界上最完善的,而且是经过生产环境验证的工业级的库。不过由于设计上就是同步的语言,也没有C#中的await机制,整个平台都是同步的,对于高并发的环境来说,确实做起来会有一定的困难。
结论
1)Node.js和Java本身不产生竞争关系
2)他们谁也不会消亡,如果非要说谁更可能消亡的话,我觉得是Node.js
3)如果你要做的是小型项目,或者逻辑非常简单的项目,可以选择Node.js,因为开发、调试都很很快。
4)如果你要做的是逻辑简单,而并发大的项目,可以选择Node.js,它还是很适合左右游戏的聊天服务器,长连接消息推送服务器之类的项目的。
5)如果你在做一个大规模项目(5人以上协作),还是用C#吧,C#并不是跟Windows绑定的语言,非windows平台上的Mono也非常好用。
希望这洋洋洒洒的胡言乱语对你有用~