Node.js官方文档:到底什么是阻塞(Blocking)与非阻塞(Non-Blocking)?

译者按: 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/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345