@@ -5,30 +5,31 @@ layout: docs.hbs
55
66# 阻塞对比非阻塞一览
77
8- 本概论涵盖了在 Node.js 中 ** 阻塞** and ** 非阻塞** 的区别,同时也会牵涉到时间轮询和 libuv 方面,不需要先行了解这些方面的知识也可以继续阅读 。我们假定读者对于 JavaScript 语言和 Node.js 的回调机制有一个基本的了解。
8+ 本文介绍了在 Node.js 中 ** 阻塞** 和 ** 非阻塞** 调用的区别。本文涉及事件循环和 libuv ,但不必对其有事先了解 。我们假定读者对于 JavaScript 语言和 Node.js 的回调机制有一个基本的了解。
99
10- > "I/O" 指的是系统磁盘和由 [ libuv] ( http://libuv.org/ ) 支持的网络之间的交互 。
10+ > "I/O" 主要指由 [ libuv] ( http://libuv.org/ ) 支持的,与系统磁盘和网络之间的交互 。
1111
1212## 阻塞
1313
14- ** 阻塞** 是说 Node.js 中其它的 JavaScript 命令必须等到一个非 JavaScript 操作完成之后才可以执行 。这是因为当 ** 阻塞** 发生时,事件机制无法继续运行JavaScript 。
14+ ** 阻塞** 是指在 Node.js 程序中,其它 JavaScript 语句的执行,必须等待一个非 JavaScript 操作完成 。这是因为当 ** 阻塞** 发生时,事件循环无法继续运行JavaScript 。
1515
16- 在 Node.js 中,JavaScript由于 CPU 密集操作而表现不佳。而不是等待非 JavaScript操作 (例如I/O)。这被称为 ** 阻塞** 。在 Node.js 基本类库中,使用 libuv 的同步方法大多数都是 ** 阻塞** 的。原生方法也可能是 ** 阻塞** 的 。
16+ 在 Node.js 中,JavaScript 由于执行 CPU 密集型操作,而不是等待一个非 JavaScript 操作 (例如I/O)而表现不佳,通常不被称为 ** 阻塞** 。在 Node.js 标准库中使用 libuv 的同步方法是最常用的 ** 阻塞** 操作。原生模块中也有 ** 阻塞** 方法 。
1717
18- 所有在 Node.js 中提供的 I/O 方法也包括异步版本,它们都是 ** 非阻塞** 的,接受回调函数。一些方法同时也具备 ** 阻塞** 功能,它们的名字结尾都以 ` Sync ` 结尾。
18+ 在 Node.js 标准库中的所有 I/O 方法都提供异步版本, ** 非阻塞** ,并且接受回调函数。某些方法也有对应的 ** 阻塞** 版本,名字以 ` Sync ` 结尾。
1919
2020
2121## 代码比较
2222
23- ** 阻塞** 方法执行起来是 ** 同步地** ,但是 ** 非阻塞** 方法执行起来是 ** 异步地** 。
24- 如果你使用文件系统模块读取一个文件,同步方法看上去如下:
23+ ** 阻塞** 方法 ** 同步** 执行,** 非阻塞** 方法 ** 异步** 执行。
24+
25+ 使用文件系统模块做例子,这是一个 ** 同步** 文件读取:
2526
2627``` js
2728const fs = require (' fs' );
28- const data = fs .readFileSync (' /file.md' ); // blocks here until file is read
29+ const data = fs .readFileSync (' /file.md' ); // 在这里阻塞直到文件被读取
2930```
3031
31- 这是一个与之功能等同的 ** 异步** 版本示例 :
32+ 这是一个等同的 ** 异步** 示例 :
3233
3334``` js
3435const fs = require (' fs' );
@@ -37,43 +38,43 @@ fs.readFile('/file.md', (err, data) => {
3738});
3839```
3940
40- 第一个示例看上去比第二个似乎简单些 ,但是有一个缺陷:第二行语句会 ** 阻塞** 其它 JavaScript 语句的执行直到整个文件全部读取完毕。注意在同步版本的代码中,任何异常都会抛出,会导致整个程序崩溃。在异步版本示例代码中,它由作者来决定是否抛出异常 。
41+ 第一个示例看上去比第二个简单些 ,但是有一个缺陷:第二行语句会 ** 阻塞** 其它 JavaScript 语句的执行直到整个文件全部读取完毕。注意在同步版本中,如果错误被抛出,它需要被捕获否则整个程序都会崩溃。在异步版本中,由作者来决定错误是否如上所示抛出 。
4142
42- 让我们扩展一点我们的同步代码 :
43+ 让我们稍微扩展一下我们的例子 :
4344
4445``` js
4546const fs = require (' fs' );
46- const data = fs .readFileSync (' /file.md' ); // blocks here until file is read
47+ const data = fs .readFileSync (' /file.md' ); // 在这里阻塞直到文件被读取
4748console .log (data);
48- // moreWork(); will run after console.log
49+ // moreWork(); 在console.log之后执行
4950```
5051
51- 这是一个类似的,但是功能上不等同的异步代码示例版本 :
52+ 这是一个类似但不等同的异步示例 :
5253
5354``` js
5455const fs = require (' fs' );
5556fs .readFile (' /file.md' , (err , data ) => {
5657 if (err) throw err;
5758 console .log (data);
5859});
59- // moreWork(); will run before console.log
60+ // moreWork(); 在console.log之前执行
6061```
6162
62- 第一个示例代码中 , ` console.log ` 将在 ` moreWork() ` 之前被调用。在第二个例子中, ` fs.readFile() ` 因为是 ** 非阻塞** 的,所以 JavaScript 会继续执行 , ` moreWork() ` 将被首先调用。` moreWork() ` 无需等待文件读完而先行执行完毕,这对于高效吞吐来说是一个绝佳的设计 。
63+ 在上述第一个例子中 , ` console.log ` 将在 ` moreWork() ` 之前被调用。在第二个例子中, ` fs.readFile() ` 是 ** 非阻塞** 的,所以 JavaScript 执行可以继续 , ` moreWork() ` 将被首先调用。在不等待文件读取完成的情况下运行 ` moreWork() ` 的能力是一个可以提高吞吐量的关键设计选择 。
6364
6465
65- ## 并行和吞吐
66+ ## 并发和吞吐量
6667
67- 在 Node.js 中 JavaScript 的执行是单线程的,所以并行与事件轮询能力(即在完成其它任务之后处理 JavaScript 回调函数的能力)有关。任何一个企图以并行的方式运行的代码必须让事件轮询机制以非 JavaScript 操作来运行, 像 I/O 操作 。
68+ 在 Node.js 中 JavaScript 的执行是单线程的,因此并发性是指事件循环在完成其他工作后执行 JavaScript 回调函数的能力。任何预期以并行方式运行的代码必须让事件循环继续运行,因为非 JavaScript 操作( 像 I/O )正在发生 。
6869
69- 举个例子,让我们思考一个案例:案例中每个对服务器的请求消耗 50 毫秒完成,其中的 45 毫秒又是可以通过异步操作而完成的数据库操作 。选择 ** 非阻塞** 操作可以释放那 45 毫秒用以处理其它的请求操作。这是在选择 ** 阻塞 ** 和 ** 非阻塞 ** 方法上的重大区别 。
70+ 例如,让我们思考这样一种情况:每个对 Web 服务器的请求需要 50 毫秒完成,而那 50 毫秒中的 45 毫秒是可以异步执行的数据库 I/O 。选择 ** 非阻塞** 异步操作可以释放每个请求的 45 毫秒来处理其它请求。仅仅是选择使用 ** 非阻塞 ** 方法而不是 ** 阻塞 ** 方法,就是容量上的重大区别 。
7071
71- Node.js 中的事件轮询机制和其它语言相比而言有区别,其它语言需要创建线程来处理并行任务 。
72+ 事件循环不同于许多其他语言的模型,其它语言创建额外线程来处理并发工作 。
7273
7374
74- ## 把阻塞和非阻塞代码混在一起写的危险
75+ ## 混合阻塞和非阻塞代码的危险
7576
76- 在处理 I/O 问题时,有些东西必须避免。下面让我们看一个例子 :
77+ 处理 I/O 时应该避免一些模式。我们来看一个例子 :
7778
7879``` js
7980const fs = require (' fs' );
@@ -84,7 +85,7 @@ fs.readFile('/file.md', (err, data) => {
8485fs .unlinkSync (' /file.md' );
8586```
8687
87- 在以上的例子中, ` fs.unlinkSync() ` 极有可能在 ` fs.readFile() ` 之前执行,所以在真正准备开始读取文件前此文件就已经被删除了。一个更好的处理方法就是彻底让使它变得 ** 非阻塞化 ** ,并且保证按照正确顺序执行 :
88+ 在以上的例子中, ` fs.unlinkSync() ` 极有可能在 ` fs.readFile() ` 之前执行,它会在实际读取之前删除 ` file.md ` 。更好的写法是完全 ** 非阻塞 ** 并保证按正确顺序执行 :
8889
8990``` js
9091const fs = require (' fs' );
@@ -97,10 +98,10 @@ fs.readFile('/file.md', (readFileErr, data) => {
9798});
9899```
99100
100- 以上代码在 ` fs.readFile() ` 用异步方式调用 ` fs.unlink() ` ,这就保证了执行顺序的正确 。
101+ 以上代码在 ` fs.readFile() ` 的回调中对 ` fs.unlink() ` 进行了 ** 非阻塞 ** 调用,这保证了正确的操作顺序 。
101102
102103
103- ## 其它资料
104+ ## 其它资源
104105
105106- [ libuv] ( http://libuv.org/ )
106- - [ 关于 Node.js] ( https://nodejs.org/en /about/ )
107+ - [ 关于 Node.js] ( https://nodejs.org/zh-cn /about/ )
0 commit comments