React Redux源函数ensureCanMutateNextListeners?

时间:2016-03-27 17:17:17

标签: reactjs redux

如何理解函数ensureCanMutateNextListeners? https://github.com/reactjs/redux/blob/master/src/createStore.js

3 个答案:

答案 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()listenerredux的用户提供,字面上可以是任何函数 - 包括subscribe(或函数调用store.subscribe)。最后,这是一个实际的例子:

const store = createStore(someReducer);

function doSubscribe() {
  store.subscribe(doSubscribe);
}
doSubscribe();    

现在发货时会发生什么?假设未使用ensureCanMutateNextListenerssubscribe实际上将监听器推送到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作为一般解决方案。在内部调度中,对ensureCanMutateNextListenerssubscribe的任何间接调用(只有两个公共函数unsubscribe允许您修改侦听器)将改变redux的副本 - 产生更可预测的行为。在上面的示例中,使用currentListeners时,ensureCanMutateNextListeners会在调用时向doSubscribe副本添加一个添加项,{{1}中的循环保持不受影响和完整,没有任何问题。

答案 2 :(得分:0)

这是一个很好的问题。在这里打电话给@gaearon