> 文章原创于公众号:程序猿周先森。本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号。
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133629699-504481514.jpg)

其实对大部分的开发者来说,异步编程与一般自然语言的线性思维会有所冲突。所以大部分开发者不能适应直接面向事件驱动进行编程,Node.js是首个将异步编程带到应用层面的平台,Node.js无时无刻不透露出异步的信息。在接触Node的过程中,很多人只是很粗略的接触了几个回调函数之后就放弃了,确实Node使用异步编程,容易陷入回调地狱,但是Node异步编程的难题其实已经基本解决,可以通过事件发布/订阅模式,或者通过Promise/Defferred模式,其实都可以完美的去解决回调陷阱的问题。

其实大部分开发人员都习惯线性思维去思考问题,所以同步编程一直很流行。但是单线程同步模型中,CPU与I/O操作无法重叠进行,所以性能问题也就摆在了开发人员的面前。在大多数语言中,提高性能的方式一般使用多线程的方式解决,但是多线程中线程切换耗费的开销,以及锁以及线程同步等问题,所以多线程会给开发人员业务逻辑带来麻烦。而Node直接采用异步编程,可以使CPU与I/O操作并行不用相互等待,可以让资源等到更好的利用。

**异步IO与非阻塞IO的区别**

非阻塞IO是由于完整的I/O没有完成,立即返回的并不是我们执行的最终数据,而仅仅是返回当前的调用状态,为了获得完整数据需要进行轮询重复调用I/O操作确认是否完成。异步I/O可实现不等待数据读取完成,执行I/O操作后后立刻返回,数据写入缓存,由底层完成监听操作,并返回成功或失败的信息给应用。

**异步编程优点**

Node.js最大的优点莫过于基于事件驱动的非阻塞I/O模型,非阻塞I/O可以使CPU与I/O操作不用相互等待,可以让资源可以得到更好的利用。Node.js为了解决编程模型中阻塞I/O的性能问题,采用单线程异步模型,所以Node.js更适合I/O密集型问题,因为Node.js面向驱动进行编程,所以需要面对海量请求,当海量请求同时作用在单线程上时,就需要防止任何一个会过度消耗时间片的请求。所以只要合理利用Node.js的异步模型,加上V8引擎的高性能,就可以充分发挥CPU与I/O并行的优势。

**异步编程的难点**

Node.js借助异步I/O模型以及V8引擎,突破了单线程的性能瓶颈,让JavaScript在后端体现了实用价值。但是也由于异步编程会给开发者带来一些难点。

**(1)函数嵌套过深**

在前端JavScript中,DOM事件绑定一般较少存在多重事件绑定的情况。一般为不同的DOM元素绑定不同的事件。
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133629997-101116599.jpg)

但是对于Node.js而言,多个异步调用的场景比比皆是。
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133630207-564054573.jpg)

其实对于最后的结果来说这样的函数嵌套结构是没有任何问题的,但是这样并没有利用好Node.js异步I/O带来的并行优势。而且函数嵌套过深,对于开发人员的后期维护也会造成困难。

**(2)阻塞代码**

在JavaScript中,并没有类似Java的sleep()这样的线程沉睡功能,可以进行延时操作的只有setInterval()和setTimeout()这两个函数。那么如果我们需要在JavaScript中实现延迟1s要怎么做呢?其实大部分开发者可能会这么去进行实现:
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133630451-217911039.jpg)

但是请记住Node.js是单线程模型,所以在执行的时候CPU资源会全部为了这段代码进行服务。从而导致其他请求全部被视而不见。所以我们可以采用setTimeout改写一下代码效果会更好:
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133630715-149423078.jpg)

但是这里有一个问题,如果我把后面的时间设成0,是不是意味着马上执行代码呢?这个问题大家可以思考一下,对答案感兴趣的可以直接在公众号发消息,我会及时回复。

**(3)多线程编程**

因为Node.js是单线程模型,对于多核CPU服务端而言,其实Node.js单进程是没有充分利用好多核CPU的,所以浏览器可以将JavaScript与UI渲染分离,就可以更好的去利用多核CPU为大量计算做服务。但是这种开发模式开发者要面临跨线程的编程,对于JavaScript一直走的单线程编程路线来说会增加一定的难度。

**(4)异常处理**

我们在使用Java进行异常处理其实是非常方便的,可以直接通过try/catch/finally语句进行异常捕获以及异常处理。
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133630945-1147187223.jpg)

但是在异步编程中这种常用的异常处理并不一定适用,因为前面有讲过异步编程时异步I/O提交请求后马上返回,因为异常一般不会发生在此阶段,这时候你对这段代码执行try/catch操作进行异常捕获其实不会发挥作用,因为try/catch只能捕获当次事件内发生的异常,对事件执行结束返回的回调函数callback中抛出的异常其实是无能为力的,所以在Node.js中,将异常作为回调函数callback的第一个参数传回,如果为空时,则表示回调函数没有抛出任何异常。
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133631199-1527212806.jpg)

上述代码中,执行checkLogin如果出现异常,则回调函数的第一个参数err则不为空,我们就可以根据这个err参数对异常进行处理。

**异步编程解决方案**

1. 事件发布/订阅模式

1. Promise/Deferred模式

1. 流程控制库

由于这三种方案涉及知识点较杂,这篇文章暂时不对这三种方案作具体介绍,下一篇文章会对这三种方案作具体介绍。

**异步并发控制**

在Node.js中,我们可以很轻易的利用异步发起并行调用,但是如果并发量过大,我们的服务器会承受不住,比如如果是对文件系统进行大量并发调用,操作系统的文件描述符数量会在瞬间被用光。所以对于异步编程来说并发很容易实现,但是也要有一定的过载保护。这里主要讲一种过载解决方案:async.

async提供了一个方法parallelLimit()用于处理异步调用的限制。
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133631433-49340192.jpg)

parallelLimit()方法有一个用于限制并发数量的参数,使得任务只能同时并发一定数量,而不能无限量同时并发。上面的代码,我们并发数设置为1,所以只能同时并发一个任务。

但是parallelLimit()有一个缺点:无法动态的添加并行任务。但是async提供了queue()方法可以动态添加并行任务,这对于遍历文件目录等操作是非常高效的。但是queue()接收的参数是固定的,丢失了parallelLimit()的多样性。
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133631662-245929770.jpg)

本篇到这里内容就结束了,本篇主要还是偏概念,可能得对Node.js有一定了解才更适合,所以这里推荐csdn一篇比较基础的关于异步编程的文章:https://blog.csdn.net/O4dC8OjO7ZL6/article/details/79987819

**欢迎关注我个人公众号:程序猿周先森**
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921133632412-2101656717.jpg)

最新文章

  1. 网站收集ing....
  2. jQuery Mobile 工具栏
  3. Proximal Gradient Descent for L1 Regularization
  4. Java基础之多线程
  5. 总结Ajax跨域调用问题
  6. 获取week of year的小程序
  7. STL采用的标准模板库
  8. .net 链接oracle
  9. ubuntu vi编辑insert时上下左右建为ABCD
  10. 如何为 Jupyter Notebook 安装代码提示功能
  11. 后端api规范说明文档
  12. P1087 FBI树 二叉树
  13. map()实现zip()功能
  14. Socket 服务端使用多线程实现多客户端通讯实例
  15. 【bzoj3091】 城市旅行
  16. vapply
  17. 040——VUE中组件之组件间的数据参props的使用实例操作
  18. Array对象的判定
  19. unix 网络编程第八章 UDP
  20. 关于运行robot framework 报错解决方法,ModuleNotFoundError: No module named 'robot'

热门文章

  1. Go-项目结构和代码组织
  2. JD面试 || 移除教室人数
  3. springboot的mybatis的xml相关的配置
  4. vue-cli报错:Class constructor FileManager cannot be invoked without 'new'
  5. python 39 socketserver 模块并发通信
  6. 详解javascript中的this的指向问题
  7. centos7.2+jdk7.9搭建haddoop2.7.0伪分布式环境(亲测成功)
  8. 【selenium】- 常见浏览器的启动
  9. HDU4578 线段树(区间更新 + 多种操作)和平方,立方
  10. CodeForces 1082 D Maximum Diameter Graph