Skip to content

Latest commit

 

History

History
140 lines (82 loc) · 7.19 KB

05-异步-IO.md

File metadata and controls

140 lines (82 loc) · 7.19 KB

异步 IO

nodejs 的核心优势就在于异步 IO 的处理能力,因此肯定要重点展开讲解。其实细想一下,做一个 web server 无非就是处理 http 请求,本质上不就是 IO 吗?


目录

  • JS 单线程异步的特性
  • 什么是 IO ?
  • 多线程 web server
  • 异步 IO
  • 异步编程的问题

JS 单线程异步的特性

说到“异步 IO”,就先来说说“异步”。

JS 异步很重要

第一节讲解语法知识的时候,就强调过必须熟练掌握 JS 异步的相关知识,列出如下知识点。还推荐不熟悉的同学去阅读作者写的 深入理解 JS 异步

  • 异步和同步的区别
  • 异步和单线程
  • event loop
  • callback 方式
  • Promise
  • async/await

单线程

这里重点说一说单线程,因为它和下文有很大的联系。

我们都知道 JS 是一门单线程执行的语言,无法用 JS 代码新启动一个线程。什么是单线程,通俗来说就是单一时间只能做一件事,不能“一心二用”。例如

console.log(100)
var i, sum
for (i = 0; i < 100000000; i++) {
    sum++
}
console.log(200)

如上代码中,要执行 100000000 次循环,是非常耗费时间的,但是 JS 也只能这么来执行:先打印100,然后执行一个漫长的 for 循环(此处可能需要等待很久),最后打印200。那我们可不可以一边执行 for 循环,一边打印200呢?两者不受影响啊,一起执行也完全 OK 啊 —— 但是答案是不可以,如果一起执行的话,那就是多线程的了,JS 只能是单线程执行。

为何要单线程

JS 为何非得要单线程呢,像 java 那样做成多线程的不行吗?答案是不行。

在浏览器环境中,JS 是可以操作 DOM 结构的,而 DOM 只有一份。如果两段 JS 能同时执行的话,那么它们都同时操作同一个 DOM 节点,不就发生冲突了吗?因此,为了避免 DOM 操作的冲突,JS 不能同时执行,只能单线程执行。另外,不光 JS 是单线程,而且 JS 和浏览器渲染公用一个线程,即 JS 执行时,浏览器渲染会等待、卡顿。

最后,单线程使得 JS 入门简单使用方便,也不会出现线程思索、状态同步的琐碎问题,简单才容易做大、做广,大道至简。

延伸到 nodejs 中

nodejs 借用了 Chrome 浏览器中的 v8 引擎来解析 JS ,因此就将其单线程的特性保留了下来。但是 nodejs 提供了 Child_Process 和 Cluster 来操作进程,能解决单线程遇到的一些问题,下一节会介绍。

解决单线程 - 异步

上文那 100000000 次循环纯粹为了演示,实际项目中不会存在这样的场景 —— 即不会存在 CPU 计算成为速度上的瓶颈,而是网络或者文件读写成为速度上的瓶颈。

网络请求和文件读写遇到性能瓶颈这是很正常的,那对于 JS 这种单线程的语言,该怎么办呢?—— 难道执行一次网络请求,就要一直等到网络请求结束(可能花费 1s 5s 甚至 10s)之后才能继续执行下去?—— 当然不是,JS 解决这个问题就是用了异步。

好了,讲到这里讲出了异步,就不在继续了,再将就是异步的语法了。


什么是 IO

所谓 IO ,就是 input 和 output ,即输入和输出。

当 JS 运行在浏览器端的时候就有 IO ,且只有一种 IO —— 网络 IO ,即 http 请求。例如 ajax 或者异步加载 script 和图片。当 JS 运行在 server 端时,IO 是最常见的,除了计算,剩下的就是 IO ,可以总结为两类:

  • 网络 IO :通过网络请求访问其他机器或者服务器的数据,或者提交数据
  • 文件 IO :读取文件内容,或者写内容到文件里

无论针对哪个语言、哪个框架、哪个操作系统,IO 都有一个不变的特点 —— 慢 。现在 CPU 的计算速度是非常快的,相比之下,读取硬盘和等待网络请求就变的非常缓慢。大家应该也能经常听到“IO 瓶颈”之类的词,这就表示其他地方很快,就卡在 IO 这块了,因此叫做“瓶颈”。

慢,但是肯定是有解决方案的。


多线程 web server

PHP 也没法创建线程,即也是单线程执行的,而且 PHP 也没有 JS 一样的异步,遇到 IO 的时候只能等待完成之后再继续下一步的执行。但是 PHP 作为世界上最好的语言,肯定有解决方案。

这个解决方案不是 PHP 搞定的,而是 web server 服务器搞定的,例如 Apache 。Apache 服务器每接收一个 http 请求都会新建一个线程,在该线程这个封闭的环境下执行 PHP 代码。

  • 好处:就是处理各个 http 请求在每个独立的线程中,上下文相互独立,不相互影响,独立性好。
  • 坏处:就是 http 并发量大了之后,创建如此多线程内存吃不消,因此会有著名的 C10K 问题。

(关于以上内容:笔者不是专业搞 PHP 的,有解释不到位的,欢迎补充)


异步 IO

相比上文 Apache 多线程的方式,nodejs 针对所有 http 请求,都只有一个线程。先解释一下,这里说的单线程和下一节将的多进程不是一回事,一个是线程一个是进程,不要混了。

  • 好处:减少线程开销,能承受更多的 http 的并发请求。著名的 nginx 也是用类似的方式做到高性能的。
  • 坏处:所有 http 请求公用一个线程,一个上下文,一点崩溃即权限崩溃,对于程序的稳定性要求高

到这里就接上了本文一开始将的话题 —— 单线程的解决方案就是异步,IO 是瓶颈,那就用异步处理 IO,即 异步 IO ,即 nodejs 用单线程、异步的方式处理 IO 时能支持更多的并发请求。

上文说过,nodejs 中最常见的 异步 IO ,第一是 网络 IO,第二是 文件 IO ,至于详细的语法和 API 用法,就不再详细演示了,不是本文的重点。

【扩展】事件监听算不算异步?

前端代码如

$('#btn1').on('click', function (event) {

})

nodejs 代码如

req.on('data', function (chunk) {

})

这种将 callback 函数作为参数传递的形式,看着都像是异步。这里就提出一个疑问吧,想搞清楚这个疑问就必须要详细了解 event loop ,推荐大家去看一个视频 what the hack is event loop(看不了就下载),也可以看作者写的 这篇文章


异步编程的问题

关于异步编程的问题和解决方案,我在 《深入浅出 nodejs》读书笔记 中整理的比较详细,可以直接去参考,没必要再拷贝一遍。


总结

本文关于异步的语法和 API 一点都没讲,但是本文内容却很多。能了解 异步 IO 单线程 等这些底层概念,比知道语法和 API 用法更重要。