在Node.js设计模式中释放zalgo为什么异步路径是一致的?

时间:2016-04-22 13:59:32

标签: javascript node.js asynchronous zalgo

在我正在阅读的伟大着作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 在下一行注册,试图注册吗?

4 个答案:

答案 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>时相同的部分。 Ñ