当前位置: 首页 > 后端技术 > Node.js

Node.js官方文档:究竟什么是阻塞(Blocking)和非阻塞(Non-Blocking)?

时间:2023-04-03 17:15:41 Node.js

译者注:Node.js文档阅读系列之一。原文:OverviewofBlockingvsNon-Blocking译者:Fundebug为了保证可读性,本文采用意译而非直译。本篇博客将介绍Node.js的阻塞和非阻塞。会提到EventLoop和libuv,不理解也不影响阅读。读者只需具备一定的JavaScript基础,了解Node.js的回调函数(回调模式)即可。博客中多次提到I/O,主要是指使用libuv与系统的磁盘和网络进行交互。阻塞(Blocking)阻塞是指Node.js的一部分代码需要等待一些非Node.js的代码执行完才能继续执行。这是因为发生阻塞时,EventLoop无法继续执行。对于Node.js来说,当CPU密集型操作导致代码性能变差时,不能称之为阻塞。当你需要等待非Node.js代码执行时,可以称之为阻塞。Node.js中依赖libuv(以Sync结尾)的同步方法会导致阻塞,这是最常见的情况。当然一些不依赖libuv的Node.js原生方法也会造成阻塞。Node.js中所有I/O相关的方法都提供了异步版本,是非阻塞的,可以指定回调函数,比如fs.readFile。其中一些方法还有相应的阻塞版本,其函数名以Sync结尾,例如fs.readFileSync。代码示例阻塞方法是同步执行的,非阻塞方法是异步执行的。以读取文件为例,下面是同步执行代码:constfs=require('fs');constdata=fs.readFileSync('/file.md');//代码会阻塞直到文件读取完成,下面的代码不会执行console.log("Hello,Fundebug!");//读取文件后,会打印相应的异步代码,如下:constfs=require('fs');fs.readFile('/file.md',(err,data)=>{if(err)throwerr;});//代码不会因为读取文件而阻塞,会继续执行下面的代码console.log("Hello,Fundebug!");//在读取文件之前打印第一个示例代码要简单得多,但它的缺点是会阻塞代码执行,后面的代码需要等到整个文件都读取完才继续执行。在同步代码中,如果读取文件出错,需要用try...catch处理错误,否则进程会崩溃。对于异步代码,是否在回调函数中处理错误由开发人员决定。我们可以稍微修改示例代码,下面是同步代码:constfs=require('fs');constdata=fs.readFileSync('/file.md');console.log(数据);moreWork();//console.log之后,执行异步代码如下:constfs=require('fs');fs.readFile('/file.md',(err,data)=>{if(err)throwerr;console.log(数据);});更多的工作();//在console.log之前执行在第一个示例中,console.log将在moreWork()之前执行。在第二个例子中,因为fs.readFile()是非阻塞的,代码可以继续执行,所以moreWork()会在console.log之前执行。moreWork()可以继续执行而无需等待读取整个文件,这是Node.js能够提高吞吐量的关键。并发性和吞吐量Node.js中JS代码执行是单线程的,所以并发意味着EventLoop可以在执行完其他代码后执行回调函数。如果希望代码并发执行,则必须确保在执行所有非JavaScript代码(如I/O)时EventLoop继续运行。例如,假设web服务器的每个请求需要50ms完成,其中45ms是数据库的I/O操作。如果使用非阻塞的异步方式进行数据库I/O,可以节省45ms来处理其他请求,可以大大提高系统的吞吐量。EventLoop与许多其他语言不同,通常它们会创建新的线程来处理并发。混合使用阻塞和非阻塞代码可能会出现问题当我们处理I/O时,我们应该避免使用以下代码:constfs=require('fs');fs.readFile('/file.md',(err,data)=>{if(err)throwerr;console.log(data);});fs.unlinkSync('/file.md');上面的例子中,fs.unlinkSync()很可能先于fs.readFile()执行,也就是说,在我们读取file.md之前,这个文件已经被删除了。为了避免这种情况,我们应该使用非阻塞的方法来保证它们以正确的顺序执行。constfs=require('fs');fs.readFile('/file.md',(readFileErr,data)=>{if(readFileErr)throwreadFileErr;console.log(data);fs.unlink('/file.md',(unlinkErr)=>{如果(unlinkErr)抛出unlinkErr;});});在上面的例子中,我们把非阻塞的fs.unlink()放在了fs.readFile()的回调函数中。Fundebug参考libuv关于Node.jsFundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、ReactNative、Node.js和Java在线应用的实时BUG监控。自2016年双十一正式上线以来,Fundebug累计处理了10亿+错误事件,其付费客户包括谷歌、360、金山、人民网等众多品牌公司。欢迎大家免费试用!转载版权声明请注明作者Fundebug及本文地址:https://blog.fundebug.com/2019/06/12/overview-of-nodejs-blocking-vs-non-blocking/