0

我正在尝试为我的苗条应用程序建立一个队列。基本上,我的服务器发送消息,它们需要按顺序执行,每个动作都等待最后一个完成。我目前使用的模型是这样的:我建立了一个商店来存储我的 actionQueue

import { writable, derived } from "svelte/store";
import type { Action } from "../types/Actions";

const initialState: Action[] = [];

let id = 0;

function createActionQueue() {
  const { subscribe, update } = writable<Action[]>(initialState);

  return {
    subscribe,
    add: (payload: Action) => {
      update((queue) => [...queue, { id: id++, ...payload }]);
    },
    next: () => {
      update((queue) => {
        queue.shift();
        return queue;
      });
    },
  };
}

export const actionQueue = createActionQueue();
export const nextAction = derived(
  actionQueue,
  ($actionQueue) => $actionQueue[0]
);

在需要监听 actionQueue 的组件上,我这样做

  $: doNextAction($nextAction);

  function doNextAction(nextAction: Action) {
    if (nextAction) {
      if (nextAction.type === "someType") {
        doSomething();
    actionQueue.next();
      } else if (nextAction.type === "anotherType") {
        doSomethingElse();
    actionQueue.next();
      }
    }
  }

但有时,actionQueue.next() 不会触发 $nextAction 的更新。我通过在随机组件上使用此命令验证了这一点:

$: console.log('nextAction',$nextAction);

我也尝试过直接使用 $actionQueue[0] ,但仍然不太奏效。我错过了什么?

4

2 回答 2

1

它是循环依赖。

->用来表示“一个变化触发另一个”,然后 dep 图是:

actionQueue.add() -> $nextAction -> doNextAction($nextAction) 
-> actionQueue.next() -> $nextAction -> doNextAction($nextAction) -> ...

如您所见,$: doNextAction($nextAction)基本上会触发自身。为了防止无限循环,svelte 运行时只会触发一次并停在那里。因此,您看到的行为。

我整理了一个更简单的演示来说明这个过程。

<script>
let counter = 0
function add() { counter += 1 }
function next(x) { counter = x * 2 }
$: next(counter)
</script>

<h1>counter: {counter}!</h1>
<button on:click={add}>+1</button>

基本上add()映射到 youractionQueue.add()next()映射到doNextAction().

虽然next()确实发生了变化counter,但不会引发新一轮的$: next(counter).

解决方案

如果你想绕过这个限制(它实际上是保护),你可以欺骗 svelte 运行时通过使用setTimeout.

function doNextAction(nextAction: Action) {
  if (nextAction) {
    if (nextAction.type === 'someType') {
      doSomething()
      // the trick:
      setTimeout(() => actionQueue.next())
    } else if (nextAction.type === 'anotherType') {
      doSomethingElse()
      // the trick:
      setTimeout(() => actionQueue.next())
    }
  }
}
于 2021-03-26T07:00:09.737 回答
0

我认为问题在于actionQueue.next()您的情况下该语句不可访问,因为该属性nextAction.type 未定义,因此即使您将新项目添加到队列中,控制台始终会打印相同的第一个元素

我创建了一个 REPL,它似乎运行顺利,您可以查看此链接

于 2021-03-24T10:20:07.623 回答