0

我正在阅读这篇文章,如果有人对我清楚,我有一个特别的疑问。

http://debuggable.com/posts/understanding-node-js:4bd98440-45e4-4a9a-8ef7-0f7ecbdd56cb

var fs = require('fs')
  , sys = require('sys');

fs.readFile('treasure-chamber-report.txt', function(report) {
  sys.puts("oh, look at all my money: "+report);
});

fs.writeFile('letter-to-princess.txt', '...', function() {
  sys.puts("can't wait to hear back from her!");
});

您的代码为节点提供了读取和写入文件的两个任务,然后进入睡眠状态。一旦节点完成任务,就会触发它的回调。但同时只能触发一个回调。在该回调完成执行之前,所有其他回调必须排队等待。除此之外,无法保证回调触发的顺序。

“所以我不必担心代码会同时访问相同的数据结构?” 你说对了!这就是 JavaScript 单线程/事件循环设计的全部魅力!

  1. 谁能解释我上面的粗体线。?为什么我们不担心两个不同的程序不会访问该对象。
  2. 当前的线程方式怎么会有问题?
  3. 触发回调的顺序会有问题吗?假设我希望callBack A() 在callBack b() 之前先返回。
4

1 回答 1

6

1)如果您运行的是单线程,则不必担心多线程应用程序带来的问题。这包括 2 个不同的线程试图同时使用同一个对象。例如,想象一下,如果一个线程试图从哈希中读取数据,而另一个线程正在从同一个哈希中删除数据。键/值对可能看起来像是存在于一行代码中,但由于在它到达下一行时的线程化,数据可能不再存在。同样,您不必处理避免这些问题所涉及的所有额外代码和麻烦。

2) 见#1。与其说是权衡,倒不如说是个问题。如今,许多计算机都有多个处理器/内核,因此让程序一次使用多个线程可能是有益的。当您期望线程阻塞时,它也很有用。例如,在另一种多线程语言中,读取文件的内容并在不添加回调的情况下输出它们是很常见的。但是,这意味着线程在文件读取操作完成之前什么都不做(阻塞)。多线程编程也很难正确完成。

3) 你不会得到保证的订单。如果要确保正确的顺序,请等待执行第二个调用,直到第一个调用返回。例如

fs.readFile('treasure-chamber-report.txt', function(report) {
    sys.puts("oh, look at all my money: "+report);

    fs.writeFile('letter-to-princess.txt', '...', function() {
      sys.puts("can't wait to hear back from her!");
    });
});

请注意,这有时会使您陷入通常所说的“回调地狱”

编辑:解决您的意见:

1)即使您正在“等待”NodeJS 来读取文件,但在 NodeJS 中,这是一个非阻塞操作。这意味着方法调用 ( readFile) 会立即返回,甚至在读取文件之前。这是因为它将数据 IO 请求传递给底层操作系统,底层操作系统有自己的线程来处理此类请求。当操作系统完成读取(或写入)时,它会通知原始进程它已准备好接收数据。当操作系统在做这项工作时,NodeJS 可以让它的一个线程在等待时继续做其他工作。这就是你需要回调的原因——你需要一种方法来告诉 NodeJS 当你最终获得数据时下一步该做什么。

2)回调地狱本质上没有什么不好的,除了它很难阅读。有人可能会想象,如果您尝试做的事情包括几个不同的异步过程(例如读取和写入磁盘),您可能会得到如下所示的内容:

var doSomething function(){
    fs.readFile('step1.txt', function(result){
        // do something with the result
        fs.writeFile('step2.txt', function(){
            // okay, step2 is ready, so process that
            fs.readFile('step2.txt', function(result){
                fs.writeFile('step3.txt', function(){
                    //etc, etc
                });
            });
        });
    });
}

您可以看到嵌套会很快变得相当深,并且难以阅读。如果你搜索“JavaScript 回调地狱”,这里和其他地方会有很多讨论这个问题。扁平化事物的一种方法是避免使用内联/匿名函数,并使用命名函数将其扁平化,因此您的回调不会嵌套在编辑器中那么深(尽管从词法的角度来看嵌套仍在发生)。

于 2012-11-13T17:14:23.833 回答