译者按: Node.js文档阅读系列之一。html
为了保证可读性,本文采用意译而非直译。node
这篇博客将介绍Node.js的阻塞(Blocking)与非阻塞(Non-Blocking)。我会提到Event Loop与libuv,可是不了解它们也不会影响阅读。读者只须要有必定的JavaScript基础,理解Node.js的回调函数(callback pattern)就能够了。数据库
博客中提到了不少次I/O,它主要指的是使用libuv与系统的磁盘与网络进行交互。小程序
阻塞指的是一部分Node.js代码须要等到一些非Node.js代码执行完成以后才能继续执行。这是由于当阻塞发生时,Event Loop没法继续执行。微信小程序
对于Node.js来讲,因为CPU密集的操做致使代码性能不好时,不能称为阻塞。当须要等待非Node.js代码执行时,才能称为阻塞。Node.js中依赖于libuv的同步方法(以Sync结尾)致使阻塞,是最多见的状况。固然,一些不依赖于libuv的原生Node.js方法有些也能致使阻塞。api
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专一于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/