Node.js中的非阻塞或异步I/O是什么?

154

在服务器端JavaScript引擎的上下文中,非阻塞I/O或异步I/O是什么?我看到这被提及为优于Java服务器端实现的优点。


3
理解这个概念可以从浏览器环境中 script 标签开始思考。Zakas 写了一篇很好的文章 - 前几节就足以解释「阻塞」的概念:http://www.nczonline.net/blog/2010/08/10/what-is-a-non-blocking-script/ - netpoetica
2个回答

355

同步 vs 异步

同步执行通常指代码按顺序依次执行。异步执行指的是不按代码顺序执行的情况。在下面的例子中,同步操作导致警报按顺序触发。而在异步操作中,尽管alert(2)看起来是第二个执行的,但实际上它不是。

同步:1、2、3

alert(1);
alert(2);
alert(3);

异步:1,3,2

alert(1);
setTimeout(() => alert(2), 0);
alert(3);

阻塞 vs 非阻塞

阻塞是指操作会一直等待直到完成,期间无法执行其他操作。非阻塞则是指代码不会等待操作完成就继续执行。在本例中,localStorage 是一个阻塞操作,因为它需要等待读取完成。另一方面,fetch 则是一个非阻塞操作,因为它不会阻止 alert(3) 的执行。

// Blocking: 1,... 2
alert(1);
var value = localStorage.getItem('foo');
alert(2);

// Non-blocking: 1, 3,... 2
alert(1);
fetch('example.com').then(() => alert(2));
alert(3);

优点

非阻塞的异步操作之一的优点是可以最大化单个CPU和内存的使用。

同步,阻塞的例子

同步、阻塞操作的一个例子是某些Web服务器(如Java或PHP中的服务器)如何处理IO或网络请求。如果您的代码从文件或数据库中读取,则您的代码会“阻塞”其后执行的所有内容。在此期间,您的计算机正在保留内存和处理时间以用于不做任何事情的线程。

为了满足该线程停滞时的其他请求,取决于您的软件。大多数服务器软件所做的是生成更多线程以满足其他请求。这需要更多消耗内存和更多处理。

异步,非阻塞的例子

异步、非阻塞服务器 - 如Node制作的服务器 - 只使用一个线程来服务所有请求。这意味着Node的一个实例充分利用了单个线程。创建者设计它的前提是I/O和网络操作是瓶颈。

当请求到达服务器时,它们逐个进行服务。但是,当服务的代码需要查询数据库时,它将回调发送到第二个队列中,并且主线程将继续运行(它不会等待)。现在,当DB操作完成并返回时,相应的回调从第二个队列中拉出并排队到第三个队列中,等待执行。当引擎有机会执行其他操作(例如执行堆栈为空时),它会从第三个队列中获取回调并执行它。


5
你的第二段"Blocking in PHP"我不确定理解得是否正确。你是在说"虽然PHP通常会在IO操作中进行阻塞,但由于操作系统自动线程化IO操作,它不会阻塞"吗?或者,你是在说在PHP中这不是一个问题,因为PHP会自动为每个请求创建一个新线程,所以一个被阻塞的请求不会使整个PHP环境停止运行?(我猜测应该是后者...) - dcow
2
等一下,如果它的意思是后者,那么像reactPHP或其他非阻塞I/O PHP相比,阻塞式I/O PHP有什么优势呢?我还是很困惑。 - Sunu Pinasthika Fajar
1
@JosephtheDreamer当你说“当你 (IO操作) 完成后,我(NodeJS)会回到你这里。 在此期间,我将做其他事情。”时,这是否意味着在node.js中,当代码正在运行时,io操作会与您的javascript代码并行在后台运行?或异步函数实际上是何时被调用的? - Charlie Parker
5
是的。异步操作与您的代码并行运行。但异步操作返回结果的回调会在主代码空闲时排队执行。 - Joseph
2
@CharlieParker 这里有一篇帖子,更多地涉及异步机制的内部。链接:https://dev59.com/H2Up5IYBdhLWcg3wF0hq - Joseph
显示剩余11条评论

12
var startTime = new Date().getTime();
var getEndTime = () => {
    var tempEndTime = new Date().getTime();
    var second = (tempEndTime - startTime)/1000
    return `took ${second} sec...to finish\n`
}

console.log('1: start App', getEndTime())
setTimeout(()=>{
    console.log('2: setTimeout', getEndTime())
}, 1000)
console.log('3: End App', getEndTime())

// console -> Process Order:  1 -> 3 -> 2

代码示例


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接