> 文章原创于公众号:程序猿周先森。本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号。
![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)

Node.js之异步编程的更多相关文章

  1. 深入浅出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 ...

  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% ...

  3. Web worker 与JS中异步编程的对比

    0.从一道题说起 var t = true; setTimeout(function(){ t = false; }, 1000); while(t){ } alert('end'); 问,以上代码何 ...

  4. Node.js之异步流控制

    前言 在没有深度使用函数回调的经验的时候,去看这些内容还是有一点吃力的.由于Node.js独特的异步特性,才出现了"回调地狱"的问题,这篇文章中,我比较详细的记录了如何解决异步流问 ...

  5. Node单线程与异步编程的初步理解

    最近学习了一些关于node的单线程与异步的知识,想拿过来和大家分享下: var async = require('async') //并行无关联,等待事件为最长时间请求过程.如以下两个任务执行时间 c ...

  6. Node.js的异步IO和事件轮询

     想象一下,以前我们在写程序时, 如果程序在I/O上阻塞了,当有更多请求过来时,服务器会怎么处理呢?在这种情景中通常会用多线程的方式.一种常见的实现是给每个连接分配一个线程,并为那些连接设置一个线程池 ...

  7. 什么是node.js的事件驱动编程

    Node.js现在非常活跃,相关生态社区已经超过Lua(基本上比较知名的功能都有nodejs模块实现).但是我们为何要使用Node.Js?相比传统的webserver服务模式,nodejs有什么优点优 ...

  8. node.js的异步I/O、事件驱动、单线程

    nodejs的特点总共有以下几点 异步I/O(非阻塞I/O) 事件驱动 单线程 擅长I/O密集型,不擅长CPU密集型 高并发 下面是一道很经典的面试题,描述了node的整体运行机制,相信很多人都碰到了 ...

  9. JS实现异步编程的4种方法

    一.回调函数 这是异步编程最基本的方法. 假定有两个函数f1和f2,后者等待前者的执行结果. f1(); f2(); //如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数. fu ...

随机推荐

  1. Powered by .NET Core 进展0819:高速开车车况汇报

    继续以流水账的方式向大家汇报,自从上周六上午将 .net core 版博客站点从 windows 部署切换到 linux 上的 docker-compose 部署后,到目前一直在线. Linux 上没 ...

  2. 并发编程 Semaphore的使用和详解

    类Semaphore的基本使用 Semaphore的作用:限制线程并发的数量 课外话题[多线程的同步概念]:其实就是排着队去执行一个任务,执行任务是一个一个的执行,这样的优点是有助于程序逻辑的正确性, ...

  3. 2019Hexo博客Next主题深度美化 打造一个炫酷博客(2)-奥怪的小栈

    219/8/1 更新 本文转载于:奥怪的小栈 这篇文章告诉你在搭建好博客后,面对网上千篇一律的美化教程怎么才能添加自己独特点,使人眼前一亮. 本站基于HEXO+Github搭建. 所以你需要准备好HE ...

  4. java表达式

    JAVA表达式优先级: (如果表达式复杂可直接括号处理) 资源来自尚学堂java视频

  5. Kali-Linux-美化与优化

    照理说,linux的桌面是不应当存在在这个世界上的,作为一个linux用户,一味捣鼓桌面显得hin-不专业.但是,虚拟机要用到,浏览器要用到--更何况,自己的老婆能不打扮一下么? update:201 ...

  6. HackerRank - maximum-gcd-and-sum

    题意:给你两个等长的数列,让你在两个数列中各选择一个数字,使得这两个数的gcd是这n * n种组合中最大的. 思路:如果上来就考虑分解因式什么的,就想偏了,假设数列1的最大数为max1,数列2的最大数 ...

  7. Mysql根据出生日期计算年龄五种方法比较

    方法一 SELECT DATE_FORMAT(FROM_DAYS(TO_DAYS(NOW())-TO_DAYS(birthday)), '%Y')+0 AS age 方法一缺陷,就是当日期为未来日期时 ...

  8. Delphi - Indy TIdFTPServer封装类

    在Delphi 7开发下有强大的Indy控件,版本为9,要实现一个FTP服务器,参考自带的例子,发现还要写很多函数,而且不支持中文显示文件列表等等. 于是,自己改进封装了下,形成一个TFTPServe ...

  9. CF803G - Periodic RMQ Problem 动态开点线段树 或 离线

    CF 题意 有一个长度为n × k (<=1E9)的数组,有区间修改和区间查询最小值的操作. 思路 由于数组过大,直接做显然不行. 有两种做法,可以用动态开点版本的线段树,或者离线搞(还没搞)( ...

  10. HDU2874 Connections between cities 最近公共祖先

    第一次按常规的方法求,将所有的查询的u,v,和最近公共祖先都保存起来,然后用tarjan+并查集求最近公共祖先.因为询问的次数过多,所以在保存查询的时候总是MLE,后来参考了一下别人的代码,才突然觉悟 ...