11

In the great book i'm reading now NodeJs design patterns I see the following example:

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);
        });
    }
}

then:

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

and usage of it:

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

The author says that if the item is in cache the behaviour is synchronous and asynchronous if its not in cache. I'm ok with that. he then continues to say that we should be either sync or async. I'm ok with that.

What I don't understand is that if I take the asynchronous path then when this line var reader1 = createFileReader('data.txt'); is executed can't the asynchronous file read finish already and thus the listener won't be registered in the following line which tries to register it?

4

4 回答 4

1

异步读取操作在事件循环的当前滴答之后才会调用其回调或开始发出事件,因此注册事件侦听器的同步代码将首先运行。

于 2016-04-22T14:07:58.847 回答
1

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

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

于 2016-04-22T14:03:16.330 回答
0

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

但在接下来的段落中,我将解释这种同步/异步函数在使用时“可能”产生的潜在错误(因此它也无法通过)。

总而言之,发生在使用示例中的是:

在事件周期 1 中:

reader1 已创建,导致“data.txt”尚未缓存,它将在其他事件周期 N 中异步响应。

订阅了一些回调以供 reader1 准备就绪。并将在周期 N 上调用。

在事件周期 N 中:读取“data.txt”并通知和缓存,因此调用 reader1 订阅的回调。

在事件周期 X 中(但 X >= 1,但 X 可能在 N 之前或之后):(可能是超时,或其他异步路径计划) reader2 是为同一个文件“data.txt”创建的

如果出现以下情况会发生什么: X === 1 :该错误可能以未提及的方式表达,导致 data.txt 结果将尝试缓存两次,第一次读取越快,将获胜。但是 reader2 会在异步响应准备好之前注册它的回调,所以它们会被调用。

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

X > N :该错误将按照书中的说明表达:

您创建 reader2(它的响应已被缓存),调用 onDataReady 导致数据被缓存(但您尚未订阅任何订阅者),然后您使用 onDataReady 订阅回调,但这不会被调用再次。

X === N:嗯,这是一个边缘情况,如果 reader2 部分首先运行将通过与 X === 1 相同的结果,但是,如果在不一致读取的“data.txt”准备部分之后运行,则会发生与 X > N 时相同

于 2017-03-10T13:55:30.687 回答
-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,它们类似于“稍后”执行的“异步”函数。在 Javascript 中,这些没有明确声明。因此,这些“延迟”执行的代码可能难以调试。

于 2017-06-29T15:10:08.453 回答