JAVA程序员是如何转Node1

背景


先介绍一下背景吧,笔者在大学期间主要学的是JAVA,但学的也不是很深,就会基本的ssm,搞搞jquery,写个网站这种程度。找工作的时候公司面试并没有问语言相关的东西(我当时也没注意),等到进来之后才知道会统一进行培训,学习其他的语言。算机缘巧合吧,就选择了node方向。经过了一段时间的学习与实践,目前应该可以说入了个门了,故分享一下现阶段的学习心得,可以纯粹当个故事看,当然如果你和我有相同的经历或者有转node的想法,希望这篇文章对你有些借鉴意义。
       
阅读本文默认你已经有了一定的后端编程经验,使用过一些框架、写过一些小网站。

一些感受


首先说一下我对这门语言的直观感受。node给我的感觉最大的就是:发展很快,但是学习资料很少,不太适合没有学过其他语言的,完全新手的人学习。其次,node跟其他语言的比较起来也挺尴尬的。跟python相比,似乎node有的特性python都有,速度上应该也比他快不了多少。跟传统的JAVA写的服务来说又不及他稳定。可能最大的优点就是他是用javaScript来编程,对于前端来说可以无缝衔接过来,所以学习的成本比较低。
我翻阅了一些和node有关的书籍,感觉在时间上都比较滞后,这种滞后是相对与node的发展过于迅猛来说的。即便有一些新的内容的也不及官网翻阅的方便,而朴灵大神的书籍又过于深,不适合新手。
短期来看,node是相当活跃的一门语言,他的优秀也在于他的性能优异,而且自从有了async和await字段,这门语言的学习成本就被再次降低了。综合来说,如果你只学习过JAVA,不妨也接触一下这一门函数式编程的语言。

建议

如果你想快速上手这门语言,我给你的建议是:
 

1、快速学习一下javaScript的一些语法。  一开始没有必要接触的概念(坑)有:原型链。js也支持class写了,一开始就去了解原型链性价比太低。
2、学习koa并简单做个东西。
3、深入学习一下egg这个框架。

我的理由很简单,koa这个框架很简单,核心代码就几百行,很快就能上手。而egg这个框架是中国人开发出来的,文档资料也都是中文,甚至你哪些地方不明白都能以中文的形式询问到开发人员。

书籍


JavaScript入门https://wangdoc.com/javascript/
Es6入门的电子版http://es6.ruanyifeng.com/  (Es和js概念差不多,基本上可以说es就是js。)
以上的第一本书说的是一些最基础的语法、方法名。第二本书说的是这门语言的一些新特性,其中promise、async 和await最为关键。当然一开始可能会看不太懂,可以结合我的文章来理解。
可以随时用来查阅的文档:
node文档中文版http://nodejs.cn/api/
如果你要买一些书:
《JavaScript精髓》、朴灵的《Node深入浅出》。

你可以先用半天/一两天时间过一遍javaScript入门,然后再来看以下部分。

开始胡诌


先介绍一下node

Node.js是基于Chrome JavaScript运行时建立的一个平台, 采用事件驱动和非阻塞I/O模型。

在node出现之前,我们写的javaScript代码都是运行在浏览器的javaScript解析器上。后来有一个叫 Ryan Dahl的外国人,他希望用高级语言来写高性能web服务。而高性能web服务的有两个东西是必须的( 非阻塞IO、事件驱动),他发现javaScript正好符合这两个个特点,而且在javaScript中,IO只能写成异步调用的(这对Ryan Dahl来说是个有点),所以他选择了javaScript。同时,他把Chrome浏览器中运行javaScript的部分迁移出来(v8引擎),使普通环境下也可运行javaScript。

第一个问题: 为什么选择Node


这个问题的另一面是:node适合什么样的业务。
结论是:node适合IO密集型的应用。请注意,IO不单单是指操作硬盘,实际中网络传输、硬件获取信息都可以抽象成IO。
传统的apache服务器在应对网络请求的时候,都是一个请求对应一个线程或者一个有效请求对应一个线程。在当请求中包含了对磁盘的操作的时候,应用需要解决数据之间的锁问题、要对线程进行生成、上下文切换等一系列操作。这这起中花费的时间不说,cpu在调用数据的时候是阻塞的状态,所以他会一直等这个数据回来,这就造成了cpu的等待、空转。在这里面,使用户的响应变慢的瓶颈不在cpu,而在与IO。
使用node的话,由于他是单线程,所以没有线程切换、锁的问题。其次他是非阻塞IO,调用了IO操作之后不要求数据直接就能返回,cpu直接就开始处理下一个操作,等到了IO操作结束之后,IO操作会去通知cpu执行接下来的操作。这就使计算机的IO处理速度大大提升。


第二个问题: Node怎么实现这个异步过程


这里只做代码层面上的分析。
首先,什么是异步?举个例子就是:cpu向硬盘请求一个数据,他发送了一个请求之后就自己干自己的东西去了,等硬盘查找到了数据之后,会通知cpu进行接下来的处理。

这里涉及两个东西,硬盘找到东西之后,处理什么?怎么处理?
处理什么:硬盘中找到的数据。
怎么处理:cpu一开始就得把硬盘查找结束后要执行的代码一并发给硬盘,告诉他,你找完了数据,带着这个函数,一起回来找我继续处理。
这就是一个异步过程。

写个伪代码,比如说我要cpu发送去硬盘查找一个数据,找完之后输出出来

// 同步
let res = getData(位置x);
输出 res;

//异步
function print(data){
    输出 data
}
getData(位置x,print)  //注意 这里的print是一个函数

这里运行了getData这个函数之后,cpu就会跟硬盘说“去位置x的地方给我找个数据,找到了之后,带着数据(data),和后面要执行的函数(print),一起返回来找我继续处理”。
硬盘就开始查找了,过了一段时间(这个时间不确定),硬盘找到了数据,假设是b,硬盘就会过来告诉cpu说,诶,执行一下print(b)。最后,b就被输出到了屏幕上。

从这个例子中就可以看出,要想能够实现这种异步编程,有一个必要的条件就是,**函数可以作为参数传递**。而javaScript原生就支持这种特性。
javaScript可以进行函数式编程,简单来说,函数是一个普通对象,可以作为参数传递,也可作为返回值返回。学习函数式编程对我的冲击还是不小的,比如说现在可以这么写一段代码

function func1(func,arg){
    return func(arg);
}
function func2(arg){
    console.log(arg);
}
func1(func2,3); 
//输出3

刚开始接触的人应该都会这样的疑惑,这个函数不是脱裤子放屁吗。
但是,正因为有了这种写法,我们可以在return func(arg)前加上一些处理逻辑。这时候代码就变成了。

function func1(func,arg){
    //func1的逻辑
    return func(arg);
}

上述中func称为func1的回调函数。
回调函数,就是将函数作为参数传入另一个函数中,等他办完了自己的事情再回头来调用。
举一个别人举过的例子:约会结束后你送你女朋友回家,离别时,你肯定会说:“到家了给我发条信息,我很担心你。” 其实这就是一个回调的过程。你留了个参数函数(要求女朋友给你发条信息)给你女朋友,然后你女朋友回家,回家的动作是主函数。她必须先回到家以后,主函数执行完了,再执行传进去的函数,然后你就收到一条信息了。

带来的问题

回头看看这个回调函数的写法,就可以发现其实他在语义上实在是非常反人类的。举个跟上面类似的例子。比如我现在要写一个函数,他要先执行一个mysql的操作(IO操作),然后再将结果输出

function printResult(sqlStr) {
    connection.query(sqlStr,print);//看到这个print会不会觉得很莫名其妙。
}
function print(err,result) {
    console.log(result);
}
printResult('select * from user'); //输出[Object object]

上面的connection.query是一个操作数据库的方法,他的参数就是(sql,callback,fields)callback就是回调函数的意思,而query方法会把这个回调函数的参数的第一参数识别为err,第二个参数识别为result。我们设计这个print的时候也要根据这个设计。
假设我们瞎设计这个print  写成这样
 

function printResult(sqlStr) {
    connection.query(sqlStr,print);
}
function print(result) {
    console.log(result);
}
printResult('select * from user'); //输出null  因为没有异常

回调函数使用和阅读的过程中一个很大的问题就是函数与函数之间的关联比较大,不好理解。这还只是一层回调函数,如果我想经过一些判断再进行这个查询和printResult。

function fun1(printResult,bol) { //懒得起名字
    if(bol){
        printResult('select *from user');
    } else {
        printResult('select *from manager');
    }
}


你看,传统的异步函数的写法就是这个回调函数,他的一个极大的问题就是要经过层层的回调来实现它的“通知”的功能,当逻辑变得复杂,这里面的层数也会不断加深。

现在,一个比较好的解决方案就是async和await。根据ES6电子书中的介绍,我们可以这么写我们的代码。

async printResult(sqlStr) {
    const [rows] = await promisePool.query(sqlStr);
        console.log(rows);
        return 1;
}
async func(){
    const res = await printResult('select * from user');
}

这里的写法和上面最大的区别就在于,直观上我现在直接就从数据库获取了数据之后就可以进行输出了,而不用再写成回调。
async 和await的一个简单解释就是,可以把耗时操作写进async函数中,而在async函数中如果要获取另一个async函数的返回值,就可以用await来获取(如上面的rows和res)。
这样就将异步的回调写法转换成为直观的同步写法了,而且它还可以享受到异步的速度。需要指出的是,async和await是通过Promise来实现的,Promise这个工具可以解决一些回调问题,而async更多的是强调异步问题。可以用async写的都可以用Promise来写,反之则不行,具体可以看看上面推荐的Es6的电子书。

注意

请注意,虽然这种方法将异步转化为了同步,但是他内部还是使用这种回调,每当有异步请求,内部一定是通过参数层层往下走,执行完了再层层往上的,这一点决定了node中网络编程和其他同步语言的不同。在阅读网上的各种博客的时候,心里要想清楚这是回调的写法还是async的写法还是Promise的写法,并且他们之间是可以穿插使用的。
我想,这就是node最基础的东西。有了这些,几乎就可以开始写代码了。


后记


到目前为止,我还没有深度学习过express。这也是一个非常优秀的开源框架,查阅国内的各种基础的node书籍,介绍的框架都是express,这种感觉就像是学java的都会接触ssm一样。
我现在所学的是koa和egg,其中koa是express原班人马再重新写的一个框架。而egg是在koa之上的另一层封装。
这两个框架我都有看过一些源码,下篇就来聊一聊他们都做了什么。
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值