在 Node 中.js释放 zalgo 的设计模式为什么异步路径是一致的
In Node.js design patterns unleashing zalgo why is the asynchronous path consistent?
在我现在正在阅读的一本好书中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');
时,异步文件读取是否已经完成,因此侦听器不会在尝试注册它的下一行中注册?
JavaScript 永远不会中断函数来运行不同的函数。
"文件已被读取"处理程序将排队,直到 JavaScript 事件循环空闲。
异步读取操作不会调用其回调或开始发出事件,直到事件循环的当前时钟周期之后,因此注册事件侦听器的同步代码将首先运行。
是的,我在阅读本书的这一部分时也有同样的感觉。"不一致的阅读看起来不错"
但是在接下来的段落中,我将解释这种同步/异步函数在使用时"可能"产生的潜在错误(因此它也无法通过)。
作为总结,在使用样本中发生的是:
在事件周期 1 中:
reader1 被创建,因为"data.txt"尚未缓存,它将在其他事件周期 N 中异步响应。
某些回调是为 Reader1 准备而订阅的。并将在循环 N 上调用。
在事件周期 N 中:读取"data.txt",并对此进行通知和缓存,因此调用 reader1 订阅的回调。
在事件周期 X 中(但 X>= 1,但 X 可能在 N 之前或之后):(可能是超时,或其他异步路径调度)为同一文件"data.txt"创建 reader2
如果出现以下情况会怎样:X === 1:该错误可能会以未提及的方式表达,导致数据.txt结果将尝试缓存两次,第一次读取,速度越快,将获胜。但是 reader2 会在异步响应准备就绪之前注册其回调,因此它们将被调用。
X> 1 和 X
X> N :该错误将按照书中的说明表达:
你创建了 reader2(它的响应已经被缓存了),onDataReady 被调用,因为数据被缓存了(但你还没有订阅任何订阅者),之后你订阅了 onDataReady 的回调,但这不会再次调用。
X === N:嗯,这是一个边缘情况,如果 reader2 部分首先运行将传递与 X === 1 相同的内容,但是,如果在不一致读取的"data.txt"就绪部分之后运行,那么将发生与 X> N 时相同的情况
这个例子对我理解这个概念更有帮助
const fs = require('fs');
const cache = {};
function inconsistentRead(filename, callback) {
if (cache[filename]) {
console.log("load from cache")
callback(cache[filename]);
} else {
fs.readFile(filename, 'utf8', function (err, data) {
cache[filename] = data;
callback(data);
});
}
}
function createFileReader(filename) {
const listeners = [];
inconsistentRead(filename, function (value) {
console.log("inconsistentRead CB")
listeners.forEach(function (listener) {
listener(value);
});
});
return {
onDataReady: function (listener) {
console.log("onDataReady")
listeners.push(listener);
}
};
}
const reader1 = createFileReader('./data.txt');
reader1.onDataReady(function (data) {
console.log('First call data: ' + data);
})
setTimeout(function () {
const reader2 = createFileReader('./data.txt');
reader2.onDataReady(function (data) {
console.log('Second call data: ' + data);
})
}, 100)
输出:
╰─ node zalgo.js
onDataReady
inconsistentRead CB
First call data: :-)
load from cache
inconsistentRead CB
onDataReady
当调用异步时,onDataReady
处理程序在读取文件之前设置,在异步中,迭代在onDataReady
设置侦听器之前完成
我认为这个问题也可以用一个更简单的例子来说明:
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 中,你有纯函数与 monad,它们类似于"稍后"执行的"异步"函数。在Javascript中,这些没有明确声明。因此,这些"延迟"执行的代码可能难以调试。
- Javascript:使用绝对路径设置img src
- 如何在d3.js中返回路径的y坐标
- esri javascript异步打印
- JavaScript异步问题
- 复制图像路径以单击它
- 从模块内部访问Express装载路径
- $translateProvider.useStaticFilesLoader的Angular Translate异步定
- 异步facebook功能
- JS文件的路径正在消失
- 异步并行错误
- 在Redux中,我应该在哪里编写复杂的异步流
- 使用onclick绘制SVG路径
- jpm的默认Firefox路径没有'不起作用
- 如何使用jquery将base64图像路径转换为真实路径
- 可以用'吗/'在相对路径中
- 角度异步http自动完成
- 如何从SeleniumWebdriver获取异步Javascript响应
- 在 Node 中.js释放 zalgo 的设计模式为什么异步路径是一致的
- 对于非异步路径返回什么
- 在异步加载的css上用正则表达式替换相对路径