OverviewofBlockingvsNon-Blocking本文主要关注node.js中阻塞和非阻塞的区别。其中,eventloop和libuv会被引用,但你之前不需要知道这些知识。我们假设本文的读者至少对Javascript和node.js回调设计模式有基本的了解和掌握。本文中的“I/O”主要是指与由“libuv”驱动的磁盘和网络的交互。阻塞是指在node.js中额外的javascript操作必须等待非node.js操作完成才能执行。出现这种现象的原因是当发生阻塞操作时,eventloop无法继续执行javascript代码。在Node.js中,Javascript等低性能是由于CPU密集型计算,而不是等待非Javascript操作,例如I/O,这通常不是阻塞操作。Node.js标准库中使用libuv的同步方式,是一般意义上的阻塞操作。本机模块也将具有阻塞方法。Node.js标准库中的所有I/O都提供非阻塞的异步版本并接受回调函数。一些方法还提供以Sync作为后缀的同步版本。比较代码阻塞方法是同步执行的,非阻塞方法是异步执行的。我们以FileSystem模块为例,它是同步文件读取操作:constfs=require('fs');constdata=fs.readFileSync('/file.md');//happenshereBlockuntilthefileisread以下是异步文件读取操作:constfs=require('fs');fs.readFile('/file.md',(err,data)=>{if(错误)抛出错误;});第一个例子和第二个例子非常相似,但是因为第二行阻塞了其他javascript代码的执行,直到文件被读取,所以它显示了它自己的缺点。请记住,在同步执行版本中,如果抛出错误,我们需要捕获它,否则程序会崩溃。在异步执行的版本中,是否抛出这个异常由我们自己决定。让我们继续前面的例子,这里是同步版本:constfs=require('fs');constdata=fs.readFileSync('/file.md');//阻塞在这里直到文件被读取Aftertakenconsole.log(data);/*moreWork();在console.log之后执行。*/以下是异步文件读取操作:constfs=require('fs');fs.readFile('/file.md',(err,data)=>{if(err)throwerr;console.log(data);});/*moreWork();Executedbeforeconsole.log*/在上面的第一个例子中,console.log会在moreWork方法之前执行,而在第二个例子中,fs.readFile是一个非阻塞操作,所以代码可以继续执行到moreWork方法,而正在读取文件。无需等到文件被读取就可以执行moreWork方法的能力是允许更高吞吐量的关键设计选择。并发性和吞吐量Node.js中的JavaScript执行是单线程的,因此并发性是指事件循环在其他工作完成后执行JavaScript回调函数的能力。任何要并行运行的代码都必须允许JavaScript操作的事件循环在发生时继续运行,例如I/O。例如,让我们考虑以下情况。每个发送到WEB服务器的请求需要50ms完成,其中45ms是可以异步执行的数据库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()执行,这会导致严重的问题。在删除文件之前,文件已从内存中删除。更好的方法是让删除操作非阻塞,并保证正确的执行顺序,如下:constfs=require('fs');fs.readFile('/file.md',(err,data)=>{if(err)throwerr;console.log(data);fs.unlink('/file.md',(err)=>{if(err)throwerr;});});上面的代码通过在fs.readFile的回调中加入fs.unlink代码来保证正确的操作顺序。其他资源libuv关于Node.js
