如何避免循环依赖,而又不与编译器进行斗争?

时间:2019-07-11 13:50:02

标签: javascript reactive-programming svelte

我正在阅读该文档,在调整了示例代码之后,我设法让编译器对我这样的循环依赖性大叫:

<script>
    let count = 0;
    $: double = count * 2;

    $: if (double >= 20) {
        alert(`count is dangerously high!`);
        count = 9;
    }

    function handleClick() {
        count += 1;
    }
</script>

<button on:click={handleClick}>
    Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

我询问了不和谐的解决方法,人们建议我应该像这样对编译器隐藏依赖项:

<script>
    let count = 0;
    $: double = count * 2;

    function resetCount() {
        count = 9;
    }

    $: if (double >= 20) {
        alert(`count is dangerously high!`);
        resetCount();
    }

    function handleClick() {
        count += 1;
    }
</script>

<button on:click={handleClick}>
    Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

它有效,但是我有几个问题:

  1. 对我来说,与编译器打交道听起来不正确,还有其他更好的方法来解决此问题吗?
  2. 一个更普遍的问题是,循环依赖性是否经常发生在编写大量精简代码的人身上?是正常的还是通常预示着设计不好?

谢谢。

2 个答案:

答案 0 :(得分:2)

@morphyish排序的答案提供了解决方法,因为正如他们所说:

  

反应性语句无法触发自身

但是,我认为这有点技术性,并且仍将提供的解决方案在概念上视为具有循环依赖性:我们仍然有count -> double -> count -> ...

并且由于我们通过将语句合并到单个反应式块中来绕过了这种周期性依赖关系警告,因此我们实际上还引入了一个错误:

enter image description here

之所以会发生此错误,是因为在反应式块的开头,double的值设置为10 * 2 = 20,然后在if-内将count设置为9。语句,但是double不会重新设置为9 * 2 = 18,因为反应块不会再次触发。

在这方面,我的建议是,类似的情况是重新评估您的依赖关系,以消除这些周期:

double = count * 2;

^因此,double取决于count,这很容易。

if (double >= 20) {
  alert('count is dangerously high!');
  count = 9;
}

^乍一看,我们的计数重置逻辑似乎取决于double,但由于我们已经确定double取决于count,因此该逻辑确实取决于在count上,不是 double

因此,我认为最好的解决方案是修改条件以匹配实际的依赖项:

<script>
    let count = 0;
    $: double = count * 2;

    $: if (count >= 10) {
        alert(`count is dangerously high!`);
        count = 9;
    }

    function handleClick() {
        count += 1;
    }
</script>

<button on:click={handleClick}>
    Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

答案 1 :(得分:1)

您可以通过稍微不同地组织代码来解决此问题:

<script>
    let count = 0;
    let double;
    $: {
       double = count * 2;
       if (double >= 20) {
        alert(`count is dangerously high!`);
        count = 9;
       }
    }

    function handleClick() {
        count += 1;
    }
</script>

<button on:click={handleClick}>
    Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

您可以使用{}将反应性语句分组在一起,但要注意一点:Svelte不会像否则那样自动编写变量声明。

我以前从未遇到过此问题,但是就您而言,这两个语句似乎都依赖于count的更新,尽管第二个语句是间接的。因此,将它们实际分组为单个语句是很有意义的。

它也解决了您的问题,因为反应性语句无法触发自身。

但是,这意味着如果您还想更新double,则需要明确进行。