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

在服务器端 Javascript 引擎的上下文中,什么是非阻塞 I/O 或异步 I/O?我认为这是相对于 Java 服务器端实现的一个优势。

100940 次浏览

同步与异步

同步执行通常是指代码按顺序执行。异步执行是指不按代码中出现的顺序运行的执行。在下面的示例中,同步操作将导致按顺序触发警报。在异步操作中,虽然 alert(2)似乎是第二次执行,但它不是。

同步: 1,2,3

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

异步: 1,3,2

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

阻塞与非阻塞

阻塞是指阻止进一步执行的操作,直到该操作完成。非阻塞指的是不阻塞执行的代码。在给定的示例中,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 时,它会将回调发送到第二个队列 主线程将继续运行(它不会等待)。现在,当 DB 操作完成并返回时,相应的回调将从第二个队列中提取出来,并在等待执行的第三个队列中排队。当引擎有机会执行其他内容时(比如执行堆栈被清空时) ,它会从第三个队列获取一个回调并执行它。

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

Code example