Node.js и файловая система: это условие гонки? - PullRequest
6 голосов
/ 10 июля 2011

У меня есть следующий код внутри класса. (Это coffeescript - и это для утилиты couchdb! - но это действительно вопрос node.js). Я пытаюсь сделать что-то The Node Way, используя Node 0.49, и это означает использование асинхронных вызовов для операций файловой системы. Сначала я вырывал волосы, потому что 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() немедленно вернулся, он помчался вокруг и представил следующему имени файла в массиве вызов функции? Или у меня есть какое-то недопонимание о постоянстве ценностей в данной области?

1 Ответ

8 голосов
/ 10 июля 2011

Нет, то, что вы видите, ожидается. Одна вещь, которую вы должны помнить, это то, что 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...