从javascript异步编程特点理解node服务器的I/O密集型优势

最近读了篇介绍js异步特性的博文,在加上自己平时在这方面也有一点琢磨和理解,感觉确实又“悟”出点东西来,分享出来给在这方面同样有困惑或不太确定自己的理解对不对的同学看。

原博有一系列介绍js异步编程特性的文章,强烈推荐阅读学习(地址),按照博主自己的介绍:

会按一般学习异步编程的顺序,首先介绍一下异步的原理,然后介绍各种异步编程的方法,从回调函数开始,然后慢慢进入Promise和Generator等对异步编程体验进行改进的技术。期间也会大概提一下事件监听方式的异步调用,但是因为太多的事件监听会让程序流程变的不清晰,所以不太推荐(其实事件模式本质上也是回调函数)。Promise和Generator才是到目前为止(ES6),最好的异步编程方式。

好,进入正文
什么是异步?同步异步与阻塞非阻塞有什么关系?

node.js的“一切皆异步”的思想很有创意,目的是可以让开发者轻松编写高性能的web服务端,而不会“不小心”就用同步api阻塞了服务器从而影响性能。其他的语言比如php, python, java等基于同步的语言,虽然也有异步api,但毕竟编程人员的“思想上是同步的”,有时候不可避免的会写出阻塞的代码,node.js的目标是造就“思想上是完全异步”的编程人员和编程语言:)

异步跟同步最大的不同就是异步api或函数被“调用”后不会等它运行结束再执行它后面的代码,而是调用之后直接往下执行,异步函数的“执行”实际上是放在“其他地方”,待“执行”完成后再把结果通过回调函数来进行进一步的使用或处理(所以异步函数书写的时候不要用”return”来返回值哦,必须通过回调函数来返回值)。这里为什么强调“调用”和“执行”两个词呢?就是为了更好的理解异步的过程。

打个比喻,你的领导要做个电子报表,笨领导的做法是,在自己电脑上把该统计的统计了,该算的算了,最后生成一张报表,然后用这个报表做接下去的工作;聪明的领导,会发个邮件给下属,把报表的要求写清楚,下属在自己的电脑上把报表做完后,发个邮件把报表交回给领导。在下属做报表的时候,领导可以在自己电脑上继续做其他事情,比如玩游戏、看视频等等(你懂的)。

在上面这个例子中,领导是编程者(你),领导的电脑是当前线程,下属的电脑是另一个线程(如果有多个下属就相当于有个线程池)。做报表这件工作是个异步函数,发邮件给下属相当于调用这个函数,下属电脑上做报表相当于在另一个线程异步执行这个函数,执行完了发邮件把报表发回给领导相当于调用回调函数,领导就可以使用这个报表接着做下面的工作(相当于回掉函数里面的代码)。下属做报表的时候领导完全不用管而是可以继续干其他事情。

通过这个例子可以清楚的看到,领导只能在他自己的电脑(用户线程)上工作,异步的函数都是在下属的电脑上(异步线程)做的。这一点在很多文章当中并没有讲的很清楚,所以容易造成困扰,因为很多人只是一味的强调javascript是单线程的,但单线程怎么能实现异步呢?就并没有讲清楚。其实所谓的单线程是指用户线程是单线程,而另外还有一个或多个线程处理异步代码的执行

接下来再说说阻塞的问题。很多文章里面在讲解的时候同步异步阻塞非阻塞混为一团,新手很难理解,最容易产生的误解就是同步=阻塞,异步=非阻塞。其实阻塞非阻塞跟同步异步没有任何关系。简单讲,阻塞就是一个api或者函数运行时间过长,而独占cpu导致其他代码不能运行,那么多长时间算阻塞呢?相信这只是一个相对的概念,只要明显影响到程序的性能和用户体验,就算阻塞吧。那跟同步异步是什么关系呢?阻塞的代码如果同步执行就会阻塞到自己后面代码的运行,所以自然而然的就想到异步来执行阻塞的代码,然而异步真能解决阻塞问题吗?下面继续讲。

node.js里面的异步

大家知道Javascript的基本语法跟其他编程语言大同小异,最大的不同就是把异步摆在首位,特别是node.js更是大部分api都是异步的,小量同步api。这与其他大部分语言刚好相反,也给习惯于同步编程思维的同学造成了很大的困扰,就是常说的转不过弯来,习惯性的认为代码是按顺序一行一行的往下执行。

上面介绍了异步非阻塞的概念,那么具体到node.js是怎么实现的呢?这里又会涉及到I/O的概念,具体不讲I/O的细节(操作系统原理都会讲),关键就一点,I/O操作通常比较耗时但不会独占CPU,典型的I/O比如文件读写,远程数据库读写,网络请求等。 先讲耗时,如果用同步API来进行I/O操作,在返回结果之前就只能等待,所以最好的办法上面已经讲过,就是进行异步操作。接着说一下不会霸占CPU的好处。在node.js进程里面,有一个用户线程(javascript所宣称的单线程)和一个异步线程池(用户无法直接访问), 如果跑在异步线程上的代码是阻塞的,那么这种异步根本就起不到消除阻塞的作用,为什么?原因就是阻塞代码会霸占cpu,导致本进程所有代码都等待不管是哪个线程。但是,,,刚刚讲的node.js里面的I/O API都是不会霸占CPU的,所以是非阻塞的,就不会出现这个问题。这就是node.js的最引以为傲的特性之一:异步非阻塞I/O.

上面只是强调异步非阻塞,那么对于真正的阻塞代码,node.js怎么办呢?不好意思。。。单个node.js进程真无能为力,跟同步编程语言相比没什么优势,只能用传统的方式,多进程,多开几个node.js进程,甚至多开几个服务器。任何语言都有它的强项和弱项,node.js的强项就是它本来的设计初衷:让开发者能够轻松的编写高性能的web服务器(进行的最多的就是网络和数据库的I/O操作),而不是做大量的CPU密集型的运算(不过我赶脚,就算要做很多阻塞操作,用多进程和多服务器又有何不可?node.js对此提供了足够的支持)。

要理解javascript异步编程和其他语言同步编程的区别,可以从一个最简单的例子开始。在同步为主的语言中,如果需要等待10秒钟,通常是类似sleep 10之类的语句,在10秒之内整个进程挂起,也就是阻塞10秒。但javascript不是这样,它是使用setTimeout函数,从字面意思就已经可以看出区别了, 设一个timeout的时间, 在这段时间内cpu可以继续运行其他代码, 等10秒时间到了, 就用回调函数的形式来做应该10秒之后才做的事情。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值