在Node.jsdevise模式中释放zalgo为什么asynchronouspath一致?

在我正在阅读的伟大的书籍NodeJs design patterns我看到下面的例子:

 var fs = require('fs'); var cache = {}; function inconsistentRead(filename, callback) { if (cache[filename]) { //invoked synchronously callback(cache[filename]); } else { //asynchronous function fs.readFile(filename, 'utf8', function(err, data) { cache[filename] = data; callback(data); }); } } 

然后:

 function createFileReader(filename) { var listeners = []; inconsistentRead(filename, function(value) { listeners.forEach(function(listener) { listener(value); }); }); return { onDataReady: function(listener) { listeners.push(listener); } }; } 

和使用它:

 var reader1 = createFileReader('data.txt'); reader1.onDataReady(function(data) { console.log('First call data: ' + data); 

作者说,如果项目在caching中,则行为是同步的,如果它不在caching中,则是asynchronous的。 我没关系。 他然后继续说,我们应该是同步或asynchronous。 我没关系。

我不明白的是,如果我采取asynchronouspath,那么当这行var reader1 = createFileReader('data.txt'); 被执行不能asynchronous文件读取已完成,因此听众将不会被注册在下面的行中,试图注册它?

JavaScript永远不会中断一个函数来运行不同的function。

“文件已被读取”处理程序将排队,直到JavaScript事件循环是空闲的。

asynchronous读取操作不会调用其callback函数,或者直到事件循环的当前滴答开始发出事件,因此注册事件侦听器的同步代码将首先运行。

是的,在阅读本书的这一部分时,我也有同感。 “不一致的看起来不错”

但在接下来的段落中,我将解释这种同步/asynchronous函数在使用时可能产生的潜在错误(所以它不能通过)。

作为总结,在使用的样本中发生的情况是:

在事件周期1:

reader1被创build,导致“data.txt”没有被caching,它会在其他事件周期中响应asynchronous。

一些callback订阅reader1准备就绪。 并且将在周期N上被调用。

在事件周期N中:读取“data.txt”,并通知和caching,以此来调用reader1订阅的callback函数。

在事件循环X中(但X> = 1,但X可能在N之前或之后):(可能是超时或其他asynchronouspath调度)reader2是为同一个文件“data.txt”创build的

如果发生了什么情况:X === 1:这个bug可能以一种没有提到的方式表示,导致data.txt结果会尝试caching两次,第一次读取的速度越快,就会获胜。 但是reader2将在asynchronous响应准备好之前注册它的callback,所以它们将被调用。

X> 1 AND X <N:与X === 1相同

X> N:错误将按照书中的解释进行expression:

你创buildreader2(响应已经被caching了),onDataReady被调用,导致数据被caching(但是你不订阅任何订阅者),然后你用onDataReady订阅callback函数,但这不会被调用再次。

X === N:好的,这是一个边缘情况,如果reader2部分先运行,就会像X === 1一样传递,但是,如果在不一致的ReadRead的“data.txt”就绪部分之后运行,那么会发生与X> N时相同

我认为这个问题也可以用一个简单的例子来说明:

 let gvar = 0; let add = (x, y, callback) => { callback(x + y + gvar) } add(3,3, console.log); gvar = 3 

在这种情况下,在add立即调用callback函数,所以之后gvar的改变没有影响: console.log(3+3+0)

另一方面,如果我们添加asynchronous

 let add2 = (x, y, callback) => { setImmediate(()=>{callback(x + y + gvar)})} add2(3, 3, console.log); gvar = 300 

因为执行顺序, gvar=300在asynchronous调用setImmediate之前运行,所以结果变成console.log( 3 + 3 + 300)

在Haskell中,你有纯函数与monad,这与“稍后”执行的“asynchronous”函数类似。 在Javascript中,这些没有明确声明。 所以这些“延迟”执行的代码可能很难debugging。