理解`nodejs`异步文件读取

时间:2015-12-01 09:05:45

标签: node.js

我正在尝试了解nodejs异步行为。考虑

### text file: a.txt ###
1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
{{b.txt}}
3. Donec et mollis dolor.
{{c.txt}}
########################

### text file: b.txt ###
2. Donec a diam lectus. Sed sit amet ipsum mauris.
########################


### text file: c.txt ###
4. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. 
########################

var readFile = function(file) {
    fs.readFile(file, "utf8", function (err, file_content) {
        if (err) console.log("error: " + err);

        file_content.split(/\n/)
            .forEach(function(line) {
                var found = line.match(/\{\{(.*?)\}\}/);
                found ? readFile(found[1]) : console.log(line);
            });
    });
};

我想要的输出是

1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
2. Donec a diam lectus. Sed sit amet ipsum mauris.
3. Donec et mollis dolor.
4. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. 

我得到的输出是

1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
3. Donec et mollis dolor.
2. Donec a diam lectus. Sed sit amet ipsum mauris.
4. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. 

我该如何解决这个问题?完成这项任务的最惯用方法是什么?

更新:我想在此注意,我想要使用readFileSyncasync(至少目前为止)。现在,我想了解使用普通JS执行此操作的正确方法,并且在此过程中,可以更好地理解异步编程。

1 个答案:

答案 0 :(得分:2)

您基本上要求做的是逐行迭代等待异步操作readFile(found[1])完成,然后再继续下一行。由于它是异步操作,因此不会暂停您的Javascript执行。因为它会暂停执行,.forEach()的其余部分继续运行,readFile(found[1])的结果与正在发生的其他事情交织在一起。

像这样的问题的唯一解决方案是对迭代进行更多控制,以便在完成处理当前行之前,下一行的迭代不会继续。您将不得不停止使用.forEach()并进行手动迭代。

以下是关于如何使您的功能与手动迭代一起工作的一般概念:

var readFile = function(file, done) {
    fs.readFile(file, "utf8", function (err, file_content) {
        if (err) {
            console.log("error: ", err);
            done(err);
            return;
        }

        var lines = file_content.split(/\n/);
        var cntr = 0;

        function nextLine() {
            var found, line, more = false;
            while (cntr < lines.length) {
                line = lines[cntr++];
                found = line.match(/\{\{(.*?)\}\}/);
                if (found) {
                    readFile(found[1], function(err) {
                        if (err) {
                            return done(err);
                        }
                        nextLine();
                    });
                    more = true;
                    // break out of the while loop and let this function finish
                    // it will get called again when the async operation completes
                    break;
                } else {
                    console.log(line);
                }
            }
            // if we're all done, then call the callback to 
            // to tell the caller we're done with all async stuff
            if (!more) {
                done(null);
            }
        }
        // start the first iteration
        nextLine();
    });
};

// call it initially like this
readFile("somefile.text", function(err) {
    if (err) console.log(err);
});