文章最初发表于公众号:程序员周先森。本平台不定期更新,喜欢我的文章,请关注我的微信公众号。事实上,对于大多数开发者来说,异步编程与一般自然语言的线性思维是相冲突的。因此,大多数开发人员无法适应直接的事件驱动编程。Node.js是第一个将异步编程带到应用程序级别的平台。Node.js一直在揭示异步信息。很多人在接触Node的过程中,只用了几个回调函数就放弃了。诚然Node采用异步编程,容易陷入回调地狱,但是Node异步编程的问题已经基本解决,可以通过事件释放。/订阅模式,或者通过Promise/Deffered模式,其实可以完美解决回调陷阱的问题。事实上,大部分开发者习惯于用线性思维来思考问题,所以同步编程一直很流行。但是在单线程同步模型中,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操作相互等待,让资源得到更好的利用。为了解决编程模型中阻塞I/O的性能问题,Node.js采用了单线程异步模型,所以Node.js更适合I/O密集型问题,因为Node.js是面向驱动程序编程,所以需要面对海量请求,当大量请求同时作用于单个线程时,需要防止任何一个请求消耗过多的时间片。所以只要合理使用Node.js的异步模型,再加上V8引擎的高性能,就可以充分发挥CPU和I/O并行的优势。异步编程难点Node.js采用异步I/O模型和V8引擎,突破了单线程的性能瓶颈,让JavaScript在后端体现实用价值。也因为异步编程会给开发者带来一些困难。(1)函数嵌套太深在前端JavaScript中,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中,异常作为回调函数回调的第一个参数返回。如果为空,则表示回调函数没有抛出任何异常。上面代码中,如果checkLogin执行过程中出现异常,回调函数的第一个参数err不为空,我们可以根据这个err参数来处理异常。异步编程解决方案事件发布/订阅模式Promise/延迟模式流程控制库由于这三种方案涉及的知识点比较复杂,本文暂不对这三种方案进行详细介绍。下一篇文章将对这三种方案进行具体介绍。异步并发控制在Node.js中,我们可以很方便的使用异步发起并行调用,但是如果并发量过大,我们的服务器就会承受不住。例如,如果对文件系统有大量的并发调用,操作系统的文件描述符数量可以瞬间用完。所以对于异步编程来说,并发很容易实现,但是要有一定的过载保护。这里主要讲一个重载的解决方案:async.async提供了一个方法parallelLimit()来处理异步调用的limit。parallelLimit()方法有个参数限制任务的并发数,让任务只能同时并发一定数量,不能无限并发。在上面的代码中,我们将并发数设置为1,这样同一时间只能并发执行一个任务。但是parallelLimit()有一个缺点:不能动态添加并行任务。但是async提供了queue()方法动态添加并行任务,对于遍历文件目录等操作非常高效。但是queue()接收的参数是固定的,parallelLimit()的多样性就丢失了。本文的内容到这里就结束了。这篇文章主要是概念性的。如果对Node.js有一定了解的话可能更适合,所以这里在csdn上有一篇比较基础的关于异步编程的文章:https://blog。csdn.net/O4dC8Oj...欢迎关注我公众号:程序员周先森
