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