QwikDev / qwik

Instant-loading web apps, without effort
https://qwik.dev
MIT License
20.7k stars 1.29k forks source link

[🐞] Running multiple action$ simultaneously only runs one #3334

Open genki opened 1 year ago

genki commented 1 year ago

Which component is affected?

Qwik City (routing)

Describe the bug

Doing: Trying to run() multiple actions when a button is clicked.

      <button onClick$={() => {
        foo.run();
        bar.run();
      }}>Foo Bar</button>

Expect: These actions have run.

Happened: Randomly selected only one action has run.

Reproduction

https://github.com/genki/qwik-test/tree/no_multi_actions

Steps to reproduce

git clone the the above and npm run dev The reproduction code is in src/router/index.tsx Please note this expects the latest qwik is located ~/project/clone/qwik

System Info

System:
    OS: macOS 13.1
    CPU: (8) arm64 Apple M2
    Memory: 84.31 MB / 24.00 GB
    Shell: 3.5.1 - /opt/homebrew/bin/fish
  Binaries:
    Node: 19.7.0 - /opt/homebrew/bin/node
    Yarn: 1.22.19 - /opt/homebrew/bin/yarn
    npm: 9.5.0 - /opt/homebrew/bin/npm
  Browsers:
    Chrome: 110.0.5481.177
    Firefox: 110.0.1
    Safari: 16.2
  npmPackages:
    @builder.io/qwik: file:~/project/clone/qwik/packages/qwik => 0.21.0
    @builder.io/qwik-city: file:~/project/clone/qwik/packages/qwik-city => 0.5.3
    undici: ^5.20.0 => 5.20.0
    vite: 4.0.4 => 4.0.4

Additional Information

I faced this issue as well at other situation such that several actions run at the same timing. For example, when one signal triggers several useTask$ at once and each of them runs different actions.

genki commented 1 year ago

Here's workaround I found for this issue:

Define the function that serializes execution of actions.

let prevAction = Promise.resolve();                             
export const enqueueAction = async (action, ...args) => {       
  await prevAction;                                  
  return prevAction = action.run(...args);        
};

Then call it instead of run(...) like this:

  // foo.run(args)
  // bar.run(args)
  enqueuAction(foo, args)
  enqueuAction(bar, args)
AleksKislov commented 8 months ago

man, I was going nuts with this issue

anyway, this solution seemed to work for me

useVisibleTask$(async () => {
    await firstAction.submit({ data });
    await secondAction.submit({ data });
  });

which is kinda simpler

gioboa commented 8 months ago

man, I was going nuts with this issue

anyway, this solution seemed to work for me

useVisibleTask$(async () => {
    await firstAction.submit({ data });
    await secondAction.submit({ data });
  });

which is kinda simpler

I think this is a simple and elegant solution. Thanks @AleksKislov for sharing.

nelsonprsousa commented 8 months ago

man, I was going nuts with this issue

anyway, this solution seemed to work for me

useVisibleTask$(async () => {
    await firstAction.submit({ data });
    await secondAction.submit({ data });
  });

which is kinda simpler

But this is not "simultaneously", they are sequential.

I have no context about this discussion, tho.

AleksKislov commented 8 months ago

But this is not "simultaneously", they are sequential.

well, yes, but at least it works for some cases.

But the thing that an action can interrupt other actions is a bummer