译者按: Node.js文档阅读系列之一。

为了保证可读性,本文采用意译而非直译。

这篇博客将介绍Node.js的阻塞(Blocking)与非阻塞(Non-Blocking)。我会提到Event Loop与libuv,但是不了解它们也不会影响阅读。读者只需要有一定的JavaScript基础,理解Node.js的回调函数(callback pattern)就可以了。

博客中提到了很多次I/O,它主要指的是使用libuv与系统的磁盘与网络进行交互。

阻塞(Blocking)

阻塞指的是一部分Node.js代码需要等到一些非Node.js代码执行完成之后才能继续执行。这是因为当阻塞发生时,Event Loop无法继续执行。

对于Node.js来说,由于CPU密集的操作导致代码性能很差时,不能称为阻塞。当需要等待非Node.js代码执行时,才能称为阻塞。Node.js中依赖于libuv的同步方法(以Sync结尾)导致阻塞,是最常见的情况。当然,一些不依赖于libuv的原生Node.js方法有些也能导致阻塞。

Node.js中所有与I/O相关的方法都提供了异步版本,它们是非阻塞的,可以指定回调函数,例如fs.readFile。其中一些方法也有对应的阻塞版本,它们的函数名以Sync结尾,例如fs.readFileSync

代码示例

阻塞的方法是同步执行的,而非阻塞的方法是异步执行。

以读文件为例,下面是同步执行的代码:

const fs = require('fs');
const data = fs.readFileSync('/file.md'); // 文件读取完成之前,代码会阻塞,不会执行后面的代码
console.log("Hello, Fundebug!"); // 文件读取完成之后才会打印

对应的异步代码如下:

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
if (err) throw err;
}); // 代码不会因为读文件阻塞,会继续执行后面的代码
console.log("Hello, Fundebug!"); // 文件读完之前就会打印

第一个示例代码看起来要简单很多,但是它的缺点是会阻塞代码执行,后面的代码需要等到整个文件读取完成之后才能继续执行。

在同步代码中,如果读取文件出错了,则错误需要使用try...catch处理,否则进程会崩溃。对于异步代码,是否处理回调函数的错误则取决于开发者。

我们可以将示例代码稍微修改一下,下面是同步代码:

const fs = require('fs');
const data = fs.readFileSync('/file.md');
console.log(data);
moreWork(); // console.log之后再执行

异步代码如下:

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
if (err) throw err;
console.log(data);
});
moreWork(); // 先于console.log执行

在第一个示例中,console.log将会先于moreWork()执行。在第二个示例中,由于fs.readFile()是非阻塞的,代码可以继续执行,因此moreWork()会先于console.log执行。

moreWork()不用等待读取整个文件,可以继续执行,这是Node.js可以增加吞吐量的关键。

并发与吞吐量

Node.js中JS代码执行是单线程的,因此并发指的是Event Loop可以在执行其他代码之后再去执行回调函数。如果希望代码可以并发执行,则所有非JavaScript代码比如I/O执行时,必须保证Event Loop继续运行。

举个例子,假设Web服务器的每个请求需要50ms完成,其中45ms是数据库的I/O操作。如果使用非阻塞的异步方式执行数据库I/O的话,则可以节省45ms来处理其他请求,这可以极大地提高系统的吞吐量。

Event Loop这种方式与其他许多语言都不一样,通常它们会创建新的线程来处理并发。

混用阻塞与非阻塞代码会出问题

当我们处理I/O时,应该避免以下代码:

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
if (err) throw err;
console.log(data);
});
fs.unlinkSync('/file.md');

上面的示例中,fs.unlinkSync()很可能在fs.readFile()之前执行,也就是说,我们在读取file.md之前,这个文件就已经被删掉了。

为了避免这种情况,我们应该是要非阻塞方式,来保证它们按照正确的顺序执行。

const fs = require('fs');
fs.readFile('/file.md', (readFileErr, data) => {
if (readFileErr) throw readFileErr;
console.log(data);
fs.unlink('/file.md', (unlinkErr) => {
if (unlinkErr) throw unlinkErr;
});
});

上面的示例中,我们把非阻塞的fs.unlink()放在fs.readFile()的回调函数中。

参考

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有Google、360、金山软件、百姓网等众多品牌企业。欢迎大家免费试用

版权声明

转载时请注明作者Fundebug以及本文地址:

https://blog.fundebug.com/2019/06/12/overview-of-nodejs-blocking-vs-non-blocking/

Node.js官方文档:到底什么是阻塞(Blocking)与非阻塞(Non-Blocking)?的更多相关文章

  1. node.js官方文档解析 02—buffer 缓冲器

    Buffer 类的实例类似于整数数组,但 Buffer 的大小是固定的.且在 V8 堆外分配物理内存.Buffer 的大小在被创建时确定,且无法调整. Buffer 类在 Node.js 中是一个全局 ...

  2. node.js官方文档chm电子书的制作

    制作软件:WebCHMSetup2.22.zip,http://www.onlinedown.net/soft/31553.htm 制作好的电子书:Node.js(v6.10.2).zip 参考链接: ...

  3. node.js官方文档解析 01—assert 断言

    assert-------断言 new assert.AssertionError(options) Error 的一个子类,表明断言的失败. options(选项)有下列对象 message < ...

  4. Node.js 官方文档中文版

    这目录也是醉了 . 列出跟没列出没两样

  5. bootbox.js官方文档中文版

    bootbox.js官方文档中文版简介:Bootbox.js是一个小型的JavaScript库,基于Bootstrap模态框开发,用于创建可编程的对话框. 不像原生的alert等对话框,所有的Boot ...

  6. Hui之Hui.js 官方文档

    基础 // 判断值是否是指定数据类型 var result = hui.isTargetType("百签软件", "string"); //=>true ...

  7. bootbox.js官方文档

    简介 Bootbox.js是一个小型的JavaScript库,基于Bootstrap模态框开发,用于创建可编程的对话框. 不像原生的alert等对话框,所有的Bootstrap模态框生成的都是非阻塞事 ...

  8. Vue.js官方文档学习笔记(一)起步篇

    Vue.js起步 Vue.js介绍 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库 ...

  9. vue.js官方文档 PDF

    链接:https://pan.baidu.com/s/1jHMBb5W 密码:gsks

随机推荐

  1. android主流开源自动化框架(monkeyrunner,robotium,uiautomator)转载

    摘要: android自动化框架小结:monkey,monkeyrunner,cts,robotium,uiautomator android自动化框架: Uiautomator: 优点:可以对所有操 ...

  2. spark写入空值到Oracle

    转自:https://blog.csdn.net/qq_33792843/article/details/83750025 val nullStr = org.apache.spark.sql.fun ...

  3. 【java】@SuppressWarnings

    作用:用于抑制编译器产生警告信息. 示例1——抑制单类型的警告: 示例2——抑制多类型的警告: 示例3——抑制所有类型的警告: 三.注解目标 通过 @SuppressWarnings 的源码可知,其注 ...

  4. HTML中,input元素的 Disabled属性 所产生的后端无法接收数据的问题

    背景 今天从前端提交 form表单 数据时,发现 设置 Disabled 的 input 元素的字段数据在后端无法接收到 原因 查阅资料(来自W3school): disabled 属性规定应该禁用 ...

  5. myeclipse开发工具的简单使用

    一.使用eclipse.myeclipse开发JAVA程序 将程序开发环境和调试环境集合在一起,提高开发效率 1.创建java项目2.创建程序包3.编写JAVA源程序4.运行JAVA程序 二.程序移植 ...

  6. django报错:django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module. Did you install mysqlclient?

    django 迁移数据库报错 django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.Did you ins ...

  7. tornado内置接口调用顺序initialize\prepare...

    一. initialize方法 首先, 该方法是框架预留的一个初始化时加载自定义内容的钩子, 其会在http请求方法之前调用 二. prepare方法 预处理方法, 在执行对应的请求方法之前调用. h ...

  8. 【转】使用Hibernate的好处是什么?

    一.Hibernate是JDBC的轻量级的对象封装,它是一个独立的对象持久层框架,和App Server,和EJB没有什么必然的联系.Hibernate可以用在任何JDBC可以使用的场合,例如Java ...

  9. Git的回滚

    Git常用命令: git add .   #进行提交 git commit -m v2 #v2版本 cat index.html #查看文件内容已经变化 git log #获取到提交信息 git re ...

  10. Linux查看进程和已知端口是否启动

    如查看activeMQ的61616这个端口是否启动,以及直接查看activeMQ进程是否启动,可以通过如下命令进行查看 其中netstat命令必须知道端口,如果能查出就证明已启动,如果查出没有结果则表 ...