question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Problem with calling actionAsync inside another actionAsync without waiting

See original GitHub issue

I bumped into an issue with actionAsync. I have poked into mobx-utils to have a test to reproduce my problem. Here it is

test("calling promise without task", async () => {
  mobx.configure({ enforceActions: "observed" })
  const values = []
  const x = mobx.observable({ a: 1 })
  mobx.reaction(() => x.a, v => values.push(v), { fireImmediately: true })

  const f1 = actionAsync("f1", async () => {
      // await task(Promise.all([])); // LINE 1
      // await task(Promise.resolve([])); // LINE 2
      // await task(Promise.all([])); // LINE 3
  });

  const f2 = actionAsync('f2', async () => {
    f1(); // LINE 0
    x.a = 2;
    x.a = await task(Promise.resolve(3));
  });

  await f2();
  // expectNoActionsRunning() // this line will fail if I uncomment LINE 1 and LINE 2
  expect(values).toEqual([1, 2, 3])
})

Notice my LINE 0, here, I’m calling an actionAsync without await inside of another actionAsync. This test, as it is right now, passes, but when I start to uncomment LINE 1-3, strange things happen.

Uncommenting LINE 1, I’ll get:

Error: [mobx-utils] invalid 'actionAsync' context when finishing action 'f1'. an action context 
for 'f2' was found instead. did you await inside an 'actionAsync' without using 'task(promise)'? did you forget to await the task?

Uncommenting LINE 1 and LINE 2, I’ll get a failed test:

expect(received).toEqual(expected) // deep equality

    - Expected
    + Received

      Array [
        1,
        2,
    -   3,
      ]

so it means the f2 is finished before await task(Promise.resolve(3))

And lastly, uncommenting LINE 1, LINE 2, and LINE 3 will make the test pass, then really any other line I add after LINE 3 doesn’t matter.

@xaviergonz Hope I can have your input about this. It’s really confusing to me. I think my use case is justified because there would be time when you don’t want to wait for a promise inside of an actionAsync, unless you could give me a suggestion on how to do that gracefully (I can do this with flow). Thank you so much!

EDIT: as for why on earth would I do Promise.all([]) that’s because I was doing a parallel processing like this

Promise.all(this.cars.map(car => new Promise(resolve => setTimeout(() => { 
/* do something here with car */; resolve() }, 0)));

but my this.cars was empty and I saw errors thrown by mobx-utils.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
xaviergonzcommented, Oct 27, 2019

@blacksteed232 Just created a PR that fixes this

1reaction
xaviergonzcommented, Oct 25, 2019

That’s because the requirements for actionAsync to work are:

  1. any awaited promise must be wrapped in task
  2. any child actionAsync must be awaited (and therefore wrapped in task)
  • this means non awaited (dangling) promises, as long as they are not actionAsyncs are ok

In this case what I suggest you to do is to either:

  • not make f1 actionAsync and use runInAction in each parallel code
  • keep using flows for that case
  • (my personal pick) to create the task at the beginning the wait for it at the end e.g.
@actionAsync
async f1() {
  // spawn the parallel actionAsync tasks
  const f2Promise = f2(); // f2 is an action async
  // ...do some other stuff in parallel
  // finally await for the started tasks to finish before ending
  await task(f2Promise) 
}

That way there are no dangling promises and it is neatly controlled by the parent action.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to safely call an async method in C# without await
If you want to get the exception "asynchronously", you could do: MyAsyncMethod(). ContinueWith(t => Console.WriteLine(t.
Read more >
Because this call is not awaited, execution of the current ...
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting ......
Read more >
Redux Fundamentals, Part 6: Async Logic and Data Fetching
Just like with a normal action, we first need to handle a user event in the application, such as a click on a...
Read more >
async function - JavaScript - MDN Web Docs - Mozilla
The async function declaration declares an async function where the await keyword is permitted within the function body. The async and await ......
Read more >
Async Await JavaScript Tutorial – How to Wait for a Function to ...
It's the same with JavaScript: once the code is running, it has its hands full with that code. We call this this kind...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found