如何理解函数ensureCanMutateNextListeners? https://github.com/reactjs/redux/blob/master/src/createStore.js
答案 0 :(得分:2)
发布从Aaron Powell's blog post here
获得的相关部分 subscribe
redux
函数的示例实现
let subscriptions = [];
const subscribe = function (fn) {
if (typeof fn !== 'function') {
throw Error('The provided listener must be a function');
}
var subscribed = true;
// This line is what we are interested in
subscriptions.push(fn);
return function () {
if (!subscribed) {
return;
}
var index = subscriptions.indexOf(fn);
subscriptions.splice(index, 1);
subscribed = false;
};
};
对于每次执行的dispatch
,我们必须通知每个subscriber
。
来自博客
现在,您可能想要添加到订阅功能的一件事是订阅者集合上的变异保持。在调度运行时,您希望收集侦听器的原因不应该发生变化。
继续......
所以这里我们有一个函数 ensureCanMutateNextListeners ,当被调用时,检查两个数组是否是相同的数组引用,如果是,则使用slice来克隆currentSubscriptions,以便在我们修改nextSubscriptions数组时(添加或删除侦听器)不会影响当前正在运行的任何调度管道。这里的目标是确保调度使用的侦听器是时间点,以便在调度开始时。
答案 1 :(得分:1)
这个问题在很长一段时间内一直困扰着我,虽然上面的答案和博客文章提供了核心思想,但并没有给出一个具体的例子。
我的思考过程是:javaScript是单线程的,任何函数都运行到完成行为 - 所以说subscribe
必须担心当前的调度操作是否真的有意义继续如果我们在函数subscribe
内,它实际上不可能也在dispatch
内:因为subscribe
的正文内部dispatch
被调用,直接或{间接的影响。
据说我刚刚意识到上述逻辑中的一个缺陷,并且认为我在这里分享。解释是在subscribe
内部,dispatch
将不会运行,但反之则不然。 subscribe
(和unsubscribe
)可以在dispatch
内运行。这是dispatch
:
function dispatch(action) {
// ...
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
看起来很无辜吧?请注意短线:listener()
。 listener
由redux
的用户提供,字面上可以是任何函数 - 包括subscribe
(或函数调用store.subscribe
)。最后,这是一个实际的例子:
const store = createStore(someReducer);
function doSubscribe() {
store.subscribe(doSubscribe);
}
doSubscribe();
现在发货时会发生什么?假设未使用ensureCanMutateNextListeners
,subscribe
实际上将监听器推送到currentListeners
。
store.dispatch(action);
将遍历所有侦听器:
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
最初只有一位听众:listeners === [doSubscribe]
,所以我们致电listener[0
],又叫doSubscribe
,调用store.subscribe
并推送函数{{1 {}为doSubscribe
,现在listeners
有两个项目!所以循环将调用第二个函数listeners
,这会添加另一个listeners[1]
,现在我们有三个项目...因为listener
订阅自己,循环将继续,并永远运行!当然,防止这种特殊无限循环的修复可以是:
doSubscribe
但即使仅使用const currentListenersCount = listeners.length;
for (let i = 0; i < currentListenersCount; i++) {
const listener = listeners[i]
listener()
}
公共API,也可能有其他情况会以某种listener
以意外方式改变侦听器数组。
因此,引入redux
作为一般解决方案。在内部调度中,对ensureCanMutateNextListeners
和subscribe
的任何间接调用(只有两个公共函数unsubscribe
允许您修改侦听器)将改变redux
的副本 - 产生更可预测的行为。在上面的示例中,使用currentListeners
时,ensureCanMutateNextListeners
会在调用时向doSubscribe
的副本添加一个添加项,{{1}中的循环保持不受影响和完整,没有任何问题。
答案 2 :(得分:0)
这是一个很好的问题。在这里打电话给@gaearon