Node.js 和文件系统:这是竞争条件吗?

发布于 2024-11-19 11:35:05 字数 1528 浏览 0 评论 0原文

我在类中有以下代码。 (它是coffescript——它用于couchdb实用程序!——但这实际上是一个node.js问题)。我正在尝试使用 Node 0.49 以 Node Way 的方式进行操作,这意味着对文件系统操作使用异步调用。起初,我很抓狂,因为在处理过程中 this.sentinel 多次变为零,所以我知道我在那里做错了什么。但后来我遇到了一个更奇怪的问题:在 load_directory 中,看到那些 console.log() 调用吗?观察当我运行这个时会发生什么。

check_sentinel: ->
    @sentinel--
    if @sentinel == 0
        @emit('designDirLoaded', @object)

load_file: (rootdir, filename, object) ->
    @sentinel++
    fname = path.join(rootdir, filename)
    @manifest.push(fname)
    fs.readFile fname, (err, data) =>
        object[filename] = data
        @check_sentinel()

load_directory: (dirpath, object) ->
    @sentinel++
    fs.readdir dirpath, (err, files) =>
        for fname in files
            console.log("X1: ", fname)
            fs.stat path.join(dirpath, fname), (err, stats) =>
                console.log("X2: ", fname)
                if stats.isFile()
                    @load_file(dirpath, fname, object)
                if stats.isDirectory()
                    object[fname] = {}
                    @load_directory(path.join(dirpath, fname), object[fname])
        @check_sentinel()

这就是我得到的结果:

X1:  memberByName.js
X1:  memberByClub.js
X2:  memberByClub.js
X2:  memberByClub.js

这是超现实的,它看起来很像竞争条件。 “memberByName”被传递给 fs.stat(),后者又将“memberByClub”传递给 load_file(),这意味着......什么?因为 load_file() 立即返回,所以它会争先恐后地向函数调用提供数组中的下一个文件名?或者我对给定范围内价值观的持久性有一些误解?

I have the following code inside a class. (It's coffeescript-- and it's for a couchdb utility!-- but this is really a node.js question). I'm attempting to do things The Node Way, using Node 0.49, and that means using asynchronous calls for filesystem operations. At first, I was pulling my hair out because this.sentinel went to zero several times during the course of processing, so I know I'm doing something wrong there. But then I hit an even weirder issue: down in load_directory, see those console.log() calls? Watch when happens when I run this.

check_sentinel: ->
    @sentinel--
    if @sentinel == 0
        @emit('designDirLoaded', @object)

load_file: (rootdir, filename, object) ->
    @sentinel++
    fname = path.join(rootdir, filename)
    @manifest.push(fname)
    fs.readFile fname, (err, data) =>
        object[filename] = data
        @check_sentinel()

load_directory: (dirpath, object) ->
    @sentinel++
    fs.readdir dirpath, (err, files) =>
        for fname in files
            console.log("X1: ", fname)
            fs.stat path.join(dirpath, fname), (err, stats) =>
                console.log("X2: ", fname)
                if stats.isFile()
                    @load_file(dirpath, fname, object)
                if stats.isDirectory()
                    object[fname] = {}
                    @load_directory(path.join(dirpath, fname), object[fname])
        @check_sentinel()

Here's what I get:

X1:  memberByName.js
X1:  memberByClub.js
X2:  memberByClub.js
X2:  memberByClub.js

This is surreal, and it looks a lot like a race condition. "memberByName" gets passed to fs.stat(), which in turn passes "memberByClub" to load_file(), implying... what? That because load_file() returned immediately, it raced around and presented the next file name in the array to the function call? Or do I have some misunderstanding about the persistence of values in a given scope?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

单身狗的梦 2024-11-26 11:35:06

不,你所看到的是预期的。您必须记住的一件事是 fs.stat 是异步的。因此,外部循环(for fname in files)将在调用 fs.stat 的任何回调之前完成循环。

您看到 memberByClub.js 两次的原因是您在日志记录语句中使用 fname,但该变量来自闭包,在回调时该变量已更改调用fs.stat

您可以使用 do (fname) => 包装内部循环以获得正确的日志记录语句,但我认为您需要重新构造代码以实现您想要对整个类执行的操作。

load_directory: (dirpath, object) ->
    @sentinel++
    fs.readdir dirpath, (err, files) =>
        for fname in files
            do (fname) =>
                console.log("X1: ", fname)
                fs.stat path.join(dirpath, fname), (err, stats) =>
                    console.log("X2: ", fname)
                    if stats.isFile()
                         @load_file(dirpath, fname, object)
                    if stats.isDirectory()
                        object[fname] = {}
                        @load_directory(path.join(dirpath, fname), object[fname])
        @check_sentinel()

No, what you see is expected. One thing you have to remember is that fs.stat is asynchronous. So, the outer loop (for fname in files) will finish looping before any of the callbacks to fs.stat is called.

The reason why you see memberByClub.js twice is that you are using fname in the logging statement, but that variable is from the closure, which has changed by the time your callback to fs.stat is called.

You can wrap the inner loop with do (fname) => to get the correct logging statements, but I think you need to restructure your code to achieve what you are trying to do with the whole class.

load_directory: (dirpath, object) ->
    @sentinel++
    fs.readdir dirpath, (err, files) =>
        for fname in files
            do (fname) =>
                console.log("X1: ", fname)
                fs.stat path.join(dirpath, fname), (err, stats) =>
                    console.log("X2: ", fname)
                    if stats.isFile()
                         @load_file(dirpath, fname, object)
                    if stats.isDirectory()
                        object[fname] = {}
                        @load_directory(path.join(dirpath, fname), object[fname])
        @check_sentinel()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文