已关闭,此问题为opinion-based。目前不接受答复。
**想改善这个问题吗?**更新问题,以便editing this post可以用事实和引用来回答。
2天前关闭。
这篇帖子昨天编辑并提交审核,未能重新打开帖子:
原始关闭原因未解决
Improve this question
我正在看这段描述JavaScript中的文件遍历算法的要点
// ES6 version using asynchronous iterators, compatible with node v10.0+
const fs = require("fs");
const path = require("path");
async function* walk(dir) {
for await (const d of await fs.promises.opendir(dir)) {
const entry = path.join(dir, d.name);
if (d.isDirectory()) yield* walk(entry);
else if (d.isFile()) yield entry;
}
}
// Then, use it with a simple async for loop
async function main() {
for await (const p of walk('/tmp/'))
console.log(p)
}
我被这种冲动(语言特性的冲动,而不是作者的冲动)吹走了,在算法的每一个缝隙里都塞进了async / await。我不太理解Node.js的架构,但我假设在严重异步的本质背后有一些意图?这是非常令人困惑的,对我来说是新的,来自一个主要是声明性的过程C/C++线程/进程模型。我想知道的不是“模型是什么”或“它是否更好”,而是“驱动异步性的思想是什么?”“这是浏览器对响应性的需求超过了基于通用任务的效率的遗迹吗?
我的问题是“为什么有这么多的异步性?“我不是在寻找一个观点,我正在寻找一个了解Node.js或JavaScript历史和演变的人来解释它的架构。
Gist:https://gist.github.com/lovasoa/8691344
1条答案
按热度按时间lsmepo6l1#
比如,它可能效率较低,我们不在乎,我们只需要坐在屏幕前的人在我们渲染页面上的N百万个元素时不会失去焦点?
好吧,在高层次上,这句话的第一部分和第二部分是冲突的。如果服务器效率低下,那么它将无法正确地响应大约在同一时间到达的一堆客户端请求。因此,您希望使服务器高效,以便它能够尽可能响应客户端请求。
现在,我们需要后退几步,了解您显示的代码中到底发生了什么。首先,虽然语言恰好是Javascript,但代码的核心逻辑以及它如何使用
async
,await
和生成器不仅仅是因为Javascript语言。这是因为JavaScript运行的特定环境,在本例中是nodejs。该环境使用事件循环并运行JavaScript的单个线程。其他OS线程用于各种系统和一些库实现,但当nodejs运行JavaScript时,它一次只运行一段JavaScript(单线程)。
同时,当您设计服务器时,您希望它能够响应大量传入的请求。您不希望它必须处理一个请求,并让所有其他请求等待,直到第一个请求完成后才开始处理下一个请求。但是,nodejs事件循环模型不使用多线程,因此不会直接同时运行多个请求处理程序。
nodejs部署的解决方案来自于这样一个事实,即对于各种各样的服务器请求处理程序,主要的活动和处理请求所花费的大部分时间是I/O(如网络,文件I/O或数据库I/O)。这些都是较低级别的操作,有自己的(非JavaScript)实现。
因此,它为所有I/O操作部署了一个异步模型。一个写得很好的服务器可以启动一个异步I/O操作,当它处理时(不是在nodejs解释器本身运行的代码中),解释器和nodejs事件循环可以自由地做其他事情,处理另一个请求。一段时间后,当异步操作完成时,一个事件将被插入到事件循环中,当解释器完成它正在做的任何事情时,它可以处理异步操作的结果并继续该操作。
通过这种方式,Javascript只在单个线程中执行,但许多传入的请求可以同时“在处理中”。
是的,这是一个完全不同的模型从你的老学校C/C线程模型。你要么学习这个不同的模型,用nodejs编写高效的服务器代码,要么不学习。如果你想坚持使用旧的模型,那么选择一个不同的环境,在线程中运行请求处理程序(Java,C等),并且设计得很好(当然,要正确编写和彻底测试所有多线程并发的相关设计和测试开销)。
nodejs模型的一个很大的好处是,它不容易受到多线程执行模型的许多并发问题的影响。nodejs模型也有缺点,偶尔需要解决。例如,如果你在一个用Javascript编写的请求处理程序中有大量的CPU代码,那么这仍然会使事情陷入困境,你需要找到一种方法来将这些CPU密集的东西从主事件循环中取出,进入其他线程或进程(甚至可能是工作队列)。但是,I/O都是异步的,可以留在主线程中而不会使任何事情陷入困境。
也许我是一个守旧派(计算机科学的学术背景,主要是一个C/C++内核的家伙),但这有什么好处呢?
需要在单独的并发线程中使用的代码越少,并发错误可能就越少,并且代码更容易完全测试。
为什么我需要一个异步循环?
你想要一个循环,因为你想循环一些东西。当您不想阻塞事件循环或当异步操作是完成任务所必须的唯一操作类型(例如在数据库中进行查找)时,您希望在该循环中使用异步操作。
作为开发人员,我怎么知道我是否优化了某些东西?
使用异步操作并不是为了优化某些东西。这是关于编写良好的服务器代码和不阻塞事件循环的核心设计。而且,事实上,nodejs中的接口(如数据库接口或网络接口)可能只提供异步接口。
你问这个问题的方式表明,如果你能更好地理解核心nodejs架构,并阅读更多关于事件循环如何工作以及异步I/O操作如何工作的知识,你会受益匪浅。
这仅仅是关于响应性吗?
首先,如果你使用的API只是异步的(比如网络或数据库),你没有任何选择。您将进行异步代码设计以使用该API。如果你可以选择是使用异步还是同步API(就像你在nodejs中使用文件系统访问一样),那么你可以选择是在每个API调用时阻塞事件循环还是不阻塞事件循环。如果您阻止了事件循环,那么您将严重损害服务器的可伸缩性和响应能力。
我被(语言特性的,而不是作者的)冲动所震撼,把async / await塞进算法的每一个缝隙。
该代码示例确实尝试在
async
、await
、generators和yield
的同一个实现中使用异步语言特性的厨房Flume。我一般不这样做。该实现的要点是能够创建一个可以像这样相当简单地使用的接口:而
walk()
的内部是异步的。这种实现对API的用户隐藏了几乎所有的异步实现复杂性,这使得API更容易编码。在正确的位置使用一个await
,API的用户几乎可以像同步一样进行编码。使异步操作更容易编码是这些JavaScript语言特性(promises,async,await,generators等)的目的。事件循环模型的优势
事件循环编程相关文章
Why the Cool Kids Use Event Loops-这恰好是关于在Java编程中使用事件循环的,但讨论适用于任何环境
The Case of Threads vs Events