Node.js之异步编程
> 文章原创于公众号:程序猿周先森。本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号。

其实对大部分的开发者来说,异步编程与一般自然语言的线性思维会有所冲突。所以大部分开发者不能适应直接面向事件驱动进行编程,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元素绑定不同的事件。

但是对于Node.js而言,多个异步调用的场景比比皆是。

其实对于最后的结果来说这样的函数嵌套结构是没有任何问题的,但是这样并没有利用好Node.js异步I/O带来的并行优势。而且函数嵌套过深,对于开发人员的后期维护也会造成困难。
**(2)阻塞代码**
在JavaScript中,并没有类似Java的sleep()这样的线程沉睡功能,可以进行延时操作的只有setInterval()和setTimeout()这两个函数。那么如果我们需要在JavaScript中实现延迟1s要怎么做呢?其实大部分开发者可能会这么去进行实现:

但是请记住Node.js是单线程模型,所以在执行的时候CPU资源会全部为了这段代码进行服务。从而导致其他请求全部被视而不见。所以我们可以采用setTimeout改写一下代码效果会更好:

但是这里有一个问题,如果我把后面的时间设成0,是不是意味着马上执行代码呢?这个问题大家可以思考一下,对答案感兴趣的可以直接在公众号发消息,我会及时回复。
**(3)多线程编程**
因为Node.js是单线程模型,对于多核CPU服务端而言,其实Node.js单进程是没有充分利用好多核CPU的,所以浏览器可以将JavaScript与UI渲染分离,就可以更好的去利用多核CPU为大量计算做服务。但是这种开发模式开发者要面临跨线程的编程,对于JavaScript一直走的单线程编程路线来说会增加一定的难度。
**(4)异常处理**
我们在使用Java进行异常处理其实是非常方便的,可以直接通过try/catch/finally语句进行异常捕获以及异常处理。

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

上述代码中,执行checkLogin如果出现异常,则回调函数的第一个参数err则不为空,我们就可以根据这个err参数对异常进行处理。
**异步编程解决方案**
1. 事件发布/订阅模式
1. Promise/Deferred模式
1. 流程控制库
由于这三种方案涉及知识点较杂,这篇文章暂时不对这三种方案作具体介绍,下一篇文章会对这三种方案作具体介绍。
**异步并发控制**
在Node.js中,我们可以很轻易的利用异步发起并行调用,但是如果并发量过大,我们的服务器会承受不住,比如如果是对文件系统进行大量并发调用,操作系统的文件描述符数量会在瞬间被用光。所以对于异步编程来说并发很容易实现,但是也要有一定的过载保护。这里主要讲一种过载解决方案:async.
async提供了一个方法parallelLimit()用于处理异步调用的限制。

parallelLimit()方法有一个用于限制并发数量的参数,使得任务只能同时并发一定数量,而不能无限量同时并发。上面的代码,我们并发数设置为1,所以只能同时并发一个任务。
但是parallelLimit()有一个缺点:无法动态的添加并行任务。但是async提供了queue()方法可以动态添加并行任务,这对于遍历文件目录等操作是非常高效的。但是queue()接收的参数是固定的,丢失了parallelLimit()的多样性。

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

Node.js之异步编程的更多相关文章
- 深入浅出Node.js (4) - 异步编程
4.1 函数式编程 4.1.1 高阶函数 4.1.2 偏函数用法 4.2 异步编程的优势与难点 4.2.1 优势 4.2.2 难点 4.3 异步编程解决方案 4.3.1 事件发布/订阅模式 4.3.2 ...
- 【译】深入理解python3.4中Asyncio库与Node.js的异步IO机制
转载自http://xidui.github.io/2015/10/29/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3python3-4-Asyncio%E5%BA%93% ...
- Web worker 与JS中异步编程的对比
0.从一道题说起 var t = true; setTimeout(function(){ t = false; }, 1000); while(t){ } alert('end'); 问,以上代码何 ...
- Node.js之异步流控制
前言 在没有深度使用函数回调的经验的时候,去看这些内容还是有一点吃力的.由于Node.js独特的异步特性,才出现了"回调地狱"的问题,这篇文章中,我比较详细的记录了如何解决异步流问 ...
- Node单线程与异步编程的初步理解
最近学习了一些关于node的单线程与异步的知识,想拿过来和大家分享下: var async = require('async') //并行无关联,等待事件为最长时间请求过程.如以下两个任务执行时间 c ...
- Node.js的异步IO和事件轮询
想象一下,以前我们在写程序时, 如果程序在I/O上阻塞了,当有更多请求过来时,服务器会怎么处理呢?在这种情景中通常会用多线程的方式.一种常见的实现是给每个连接分配一个线程,并为那些连接设置一个线程池 ...
- 什么是node.js的事件驱动编程
Node.js现在非常活跃,相关生态社区已经超过Lua(基本上比较知名的功能都有nodejs模块实现).但是我们为何要使用Node.Js?相比传统的webserver服务模式,nodejs有什么优点优 ...
- node.js的异步I/O、事件驱动、单线程
nodejs的特点总共有以下几点 异步I/O(非阻塞I/O) 事件驱动 单线程 擅长I/O密集型,不擅长CPU密集型 高并发 下面是一道很经典的面试题,描述了node的整体运行机制,相信很多人都碰到了 ...
- JS实现异步编程的4种方法
一.回调函数 这是异步编程最基本的方法. 假定有两个函数f1和f2,后者等待前者的执行结果. f1(); f2(); //如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数. fu ...
随机推荐
- IDEA导入spring-boot-plus(二)
IDEA导入spring-boot-plus 安装lombok插件 !!!请先确保IDEA已安装lombok插件!!! IDEA在线安装lombok插件 IDEA离线下载安装lombok 如果在线安装 ...
- 卷积神经网络cnn的实现
卷积神经网络 代码:https://github.com/TimVerion/cat 卷积层 卷积层:通过在原始图像上平移来提取特征,每一个特征就是一个特征映射 原理:基于人脑的图片识别过程,我们可以 ...
- Code signing is required for product type 'Unit Test Bundle' in SDK 'iOS 11.0.1'
Code signing is required for product type 'Unit Test Bundle' in SDK 'iOS 11.0.1' 进入 projects and lis ...
- Java 从入门到进阶之路(二)
之前的文章我们介绍了一下用 IDEA 编辑器创建一个 Java 项目并输出 HelloWorld,本章我们来看一下 Java 中的变量和基本数据类型. 在这之前我们先来看一下 Java 中的关键字,这 ...
- Linux下复位USB设备
有时候USB设备出错,这时我们希望通过软件复位一下USB设备,可以参考下面这段代码: #include <stdio.h> #include <unistd.h> #inclu ...
- MySQL里的COUNT
count(*).count(1).count(主键).count(字段)的执行效率 在没有where条件的情况下 MyISAM引擎返回结果会比InnoDB快上很多,主要是因为MyISAM会单独记录了 ...
- C#_细说Cookie_Json Helper_Cookies封装
阅读目录 开始 Cookie 概述 Cookie的写.读过程 使用Cookie保存复杂对象 Js中读写Cookie Cookie在Session中的应用 Cookie在身份验证中的应用 Cookie的 ...
- Vim高手,从来不用鼠标
Vim脱离鼠标第一步 平时不可缺少的会用到vim,但是避免不了鼠标,事实上,省略鼠标是完全可以的,没有想像中那么难,看我短短几行带大家一起省略鼠标. 对了,vim有三种模式,基本模式就是用来输入命令的 ...
- java学习之- 线程运行状态
标签(空格分隔): 线程运行状态 线程的运行状态: 如下是是我编写的一个图,大家可以作为参考: 1.new一个thread子类也是创建了一个线程: 2.创建完毕之后start()-----运行, 3. ...
- Windows下Python 3.6 + VS2017 + Anaconda 解决Unable to find vcvarsall.bat问题
Python 3.6 + VS2017 + Anaconda 解决Unable to find vcvarsall.bat问题 已经安装了VS2017,需要将项目中的C代码翻译为Python代码,在编 ...