在我正在阅读的伟大着作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);
作者说,如果项目在缓存中,则行为是同步的,如果不在缓存中则是异步的。我没关系。然后他继续说我们应该同步或异步。我没关系。
我不明白的是,如果我采用异步路径,那么当执行此行var reader1 = createFileReader('data.txt');
时,异步文件读取已经完成,因此监听器赢了&# 39; t 在下一行注册,试图注册吗?
答案 0 :(得分:1)
JavaScript永远不会中断函数来运行不同的函数。
“已读取文件”处理程序将排队,直到JavaScript事件循环空闲。
答案 1 :(得分:1)
异步读取操作不会调用其回调或开始发出事件,直到事件循环的当前标记之后,因此注册事件监听器的同步代码将首先运行。
答案 2 :(得分:1)
我认为问题也可以通过一个更简单的例子来说明:
let gvar = 0;
let add = (x, y, callback) => { callback(x + y + gvar) }
add(3,3, console.log); gvar = 3
在这种情况下,我会在callback
内立即调用add
,因此之后gvar
的更改无效:console.log(3+3+0)
另一方面,如果我们异步添加
let add2 = (x, y, callback) => { setImmediate(()=>{callback(x + y + gvar)})}
add2(3, 3, console.log); gvar = 300
由于执行顺序gvar=300
在异步调用setImmediate
之前运行,因此结果变为console.log( 3 + 3 + 300)
在Haskell中,你有纯函数vs monad,它类似于" async"被执行的函数"稍后"。在Javascript中,这些未明确声明。所以这些"延迟"执行代码可能很难调试。
答案 3 :(得分:0)
是的,在阅读本书的这一部分时,我也有同样的感受。 " inconsistentRead看起来不错"
但是在接下来的段落中,我将解释这种同步/异步功能的潜在错误"可以"使用时产生(因此也无法通过)。
总结一下,在使用样本中发生的是:
在事件周期1中:
读者1被创建,原因" data.txt"尚未缓存,它将在其他事件周期N中响应异步。
为reader1准备就绪订阅了一些回调。并将在周期N上调用。
在事件周期N中: " data.txt中"读取并通知和缓存,因此调用reader1订阅的回调。
在事件周期X中(但X> = 1,但X可能在N之前或之后):(可能是超时或其他异步路径调度此) reader2是为同一个文件" data.txt"
创建的如果: X === 1:错误可以用一种没有提到的方式表达,导致data.txt结果将尝试缓存两次,第一次读取,越快,就会赢。但是,在异步响应准备就绪之前,reader2将注册其回调,因此它们将被调用。
X> 1和X< N:与X === 1
相同X> N:该错误将如书中所解释的那样表达:
你创建了reader2(它的响应已被缓存),onDataReady被调用因为数据被缓存(但你还没有订阅任何订阅者),之后你用onDataReady订阅了回调,但是这不会再被调用。
X === N:嗯,这是一个边缘情况,如果首先运行的reader2部分将与X === 1相同,但是,如果在" data.txt"之后运行inconistentRead的就绪部分将发生与X>时相同的部分。 Ñ