我也在读这一章,希望对你有帮助!
首先我们看一下crow-tech.js中的wherewhere方法声明
everywhere(f) {
for (let node of Object.values(this.nodes)) f(node);
}
所有这一切都是它接受一个回调函数f
,然后对于我们网络中的每个节点/巢,我们调用f
,将每个节点/巢作为参数传递。
现在,看看这个无处不在的调用:
everywhere(nest => {
nest.state.connections = new Map;
nest.state.connections.set(nest.name, nest.neighbors);
broadcastConnections(nest, nest.name);
});
下面的匿名函数实际上是作为 f 传递给任何地方的。
nest => {
nest.state.connections = new Map;
nest.state.connections.set(nest.name, nest.neighbors);
broadcastConnections(nest, nest.name);
}
所以我们说,对于每个节点/巢,我们要实例化一个新的 Map 并将其设置为其状态的连接属性。此外,我们将首先在该 Map 中添加一个条目,键是巢的名称,值是它的直接邻居。
现在,让我们谈谈broadcastConnections(nest, nest.name)
function broadcastConnections(nest, name, exceptFor = null) {
for (let neighbor of nest.neighbors) {
if (neighbor == exceptFor) continue;
request(nest, neighbor, "connections", {
name,
neighbors: nest.state.connections.get(name),
});
}
}
我们可以看到我们正在遍历我们的每个巢的直接邻居并在下面调用这个请求函数:
function request(nest, target, type, content) {
return new Promise((resolve, reject) => {
let done = false;
function attempt(n) {
nest.send(target, type, content, (failed, value) => {
done = true;
if (failed) reject(failed);
else resolve(value);
});
setTimeout(() => {
if (done) return;
else if (n < 3) attempt(n + 1);
else reject(new Timeout("Timed out"));
}, 250);
}
attempt(1);
});
}
然后,此请求函数从 crow-tech.js 文件调用节点/巢的发送方法
send(to, type, message, callback) {
let toNode = this[$network].nodes[to];
if (!toNode || !this.neighbors.includes(to))
return callback(new Error(`${to} is not reachable from ${this.name}`));
let handler = this[$network].types[type];
if (!handler) return callback(new Error("Unknown request type " + type));
if (Math.random() > 0.03)
setTimeout(() => {
try {
handler(toNode, ser(message), this.name, (error, response) => {
setTimeout(() => callback(error, ser(response)), 10);
});
} catch (e) {
callback(e);
}
}, 10 + Math.floor(Math.random() * 10));
}
在 send 方法中,你可以看到我们最终在调用setTimeout()
,这个函数基本上会将一个函数放入事件或消息队列中。本文提供了有关事件循环的更多信息:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
在我们的例子中,send 方法中的处理函数就是由这个函数定义的:
requestType("connections", (nest, { name, neighbors }, source) => {
let connections = nest.state.connections;
if (JSON.stringify(connections.get(name)) === JSON.stringify(neighbors))
return;
connections.set(name, neighbors);
broadcastConnections(nest, name, source);
});
更具体地说,处理函数是这个匿名函数表达式:
(nest, { name, neighbors }, source) => {
let connections = nest.state.connections;
if (JSON.stringify(connections.get(name)) === JSON.stringify(neighbors))
return;
connections.set(name, neighbors);
broadcastConnections(nest, name, source);
}
为了最终将所有内容联系在一起,连接永远不会为空的原因是因为调用处理程序的 send 方法中的匿名函数实际上位于事件或消息队列中。事件或消息队列中的函数只会在当前事件循环堆栈为空时执行。
为每个节点/嵌套调用的这两行都已经在堆栈上:
nest.state.connections = new Map;
nest.state.connections.set(nest.name, nest.neighbors);
这就是为什么在调用“连接”类型的处理程序时,每个节点/嵌套的连接都是一个有效的 Map 实例,至少有 1 个条目。