Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: replace series function used to queue async callbacks #11485

Merged
merged 6 commits into from Sep 4, 2023

Conversation

karlsander
Copy link
Contributor

@karlsander karlsander commented Jul 20, 2023

Motivation

This function is used to ensure that all state change callbacks to sync the local browser history state run in series. The functions are async because history.go is not a synchronous function.

I noticed that in chrome, the URL was updating to the previous url instead of the new one in some navigations and tracked it down to the fact that the history.go navigation takes longer than in other browsers. This function is supposed to mitigate that, but I believe it has a bug. The function will not properly return early, because the finally clause will still always run and immediately reset the isRunning state. Even after fixing this, the function didn't quite work right for queuing more than two callbacks.

I have replaced the function with a simpler implementation that does the same task.

Test plan

Describe the steps to test this change so that a reviewer can verify it. Provide screenshots or videos if the change affects UI.

The change must pass lint, typescript and tests.

@github-actions
Copy link

Hey @karlsander! Thanks for opening your first pull request in this repo. If you haven't already, make sure to read our contribution guidelines.

@netlify
Copy link

netlify bot commented Jul 20, 2023

Deploy Preview for react-navigation-example ready!

Name Link
🔨 Latest commit ae9956d
🔍 Latest deploy log https://app.netlify.com/sites/react-navigation-example/deploys/64f5a1a19ab9080008e13350
😎 Deploy Preview https://deploy-preview-11485--react-navigation-example.netlify.app/
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@karlsander karlsander changed the title replace series function used to queue async callbacks fix: replace series function used to queue async callbacks Jul 20, 2023
Copy link
Member

@satya164 satya164 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR

@satya164
Copy link
Member

This change seems to break unit tests. Can you take a look?

@karlsander
Copy link
Contributor Author

I'm not sure exactly, but the test is failing on main for me in the same way. I think it is addressed here? #11509

@mountiny
Copy link

@karlsander Would you be able to merge main to your branch? The tests are passing on main

@karlsander
Copy link
Contributor Author

I can see they succeed on the branch check CI, but when I clone react-navigation/react-navigation@main and run yarn && yarn test, they still fail in the same way.

@adamgrzybowski
Copy link
Contributor

Hey, @karlsander I've tried wrapping expect with waitFor and tests seem to work fine. Here's how it looks. https://gist.github.com/adamgrzybowski/7839818fb1a9728052bab0a1d84464e4

There's one thing I'm not 100% sure of. I had to remove is.useFakeTimers() but the tests still passed.

@karlsander
Copy link
Contributor Author

The tests do technically pass, but I think only because the failure is not captured correctly, because I still get this in the logs

 node:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^

JestAssertionError: expect(received).toBe(expected) // Object.is equality

Expected: "/feed"
Received: "/"
    at Object.<anonymous> (/Users/karl/code/react-navigation/packages/native/src/__tests__/NavigationContainer.test.tsx:107:28)
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (/Users/karl/code/react-navigation/node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
    at _next (/Users/karl/code/react-navigation/node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:9)
    at /Users/karl/code/react-navigation/node_modules/@babel/runtime/helpers/asyncToGenerator.js:27:7
    at new Promise (<anonymous>)
    at Object.<anonymous> (/Users/karl/code/react-navigation/node_modules/@babel/runtime/helpers/asyncToGenerator.js:19:12)
    at Promise.then.completed (/Users/karl/code/react-navigation/node_modules/jest-circus/build/utils.js:289:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (/Users/karl/code/react-navigation/node_modules/jest-circus/build/utils.js:222:10)
    at _callCircusTest (/Users/karl/code/react-navigation/node_modules/jest-circus/build/run.js:248:40)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at _runTest (/Users/karl/code/react-navigation/node_modules/jest-circus/build/run.js:184:3)
    at _runTestsForDescribeBlock (/Users/karl/code/react-navigation/node_modules/jest-circus/build/run.js:86:9)
    at run (/Users/karl/code/react-navigation/node_modules/jest-circus/build/run.js:26:3)
    at runAndTransformResultsToJestFormat (/Users/karl/code/react-navigation/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21)
    at jestAdapter (/Users/karl/code/react-navigation/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
    at runTestInternal (/Users/karl/code/react-navigation/node_modules/jest-runner/build/runTest.js:367:16)
    at runTest (/Users/karl/code/react-navigation/node_modules/jest-runner/build/runTest.js:444:34)
    at Object.worker (/Users/karl/code/react-navigation/node_modules/jest-runner/build/testWorker.js:106:12) {
  matcherResult: {
    actual: '/',
    expected: '/feed',
    message: '\x1B[2mexpect(\x1B[22m\x1B[31mreceived\x1B[39m\x1B[2m).\x1B[22mtoBe\x1B[2m(\x1B[22m\x1B[32mexpected\x1B[39m\x1B[2m) // Object.is equality\x1B[22m\n' +
      '\n' +
      'Expected: \x1B[32m"/\x1B[7mfeed\x1B[27m"\x1B[39m\n' +
      'Received: \x1B[31m"/"\x1B[39m',
    name: 'toBe',
    pass: false
  }
}

I think waitFor also needs to be awaited before the next act, and in that case, the failure remains the same as ever. Maybe this code is actually broken, not the tests.

@adamgrzybowski
Copy link
Contributor

@karlsander Hey I created a PR to test new tests for the series without distracting you if something went wrong 😄

I've used await and it seems to work fine. I've also checked with the debugger and the value for window.location.pathname matches the expected value.

BTW we used your series implementation as a patch in our app and it seems to solve one problem that we had so I think you did a good job with this solution

BTW_2.0 the old implementation of series is probably broken because of the try {...} finally {...} blocks. If we remove them the result is the same as with your simpler implementation (the problem in our app is fixed and the tests fail)

The finally block is executed even if you return in try and that breaks the queue. At least that's my guess

@karlsander
Copy link
Contributor Author

BTW_2.0 the old implementation of series is probably broken because of the try {...} finally {...} blocks. If we remove them the result is the same as with your simpler implementation (the problem in our app is fixed and the tests fail)

I don't remember exactly, but I think in that case two async functions at the same time are queued correctly, but not 3+ (a situation that doesn't really happen in this usage anyway).

@karlsander
Copy link
Contributor Author

@satya164 all looks green now, but I cannot merge because I don't have access

@codecov-commenter
Copy link

codecov-commenter commented Aug 31, 2023

Codecov Report

Patch coverage: 100.00% and project coverage change: +0.06% 🎉

Comparison is base (64734e7) 75.60% compared to head (03fe15b) 75.66%.

❗ Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the GitHub App Integration for your organization. Read more.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #11485      +/-   ##
==========================================
+ Coverage   75.60%   75.66%   +0.06%     
==========================================
  Files         194      194              
  Lines        5783     5773      -10     
  Branches     2275     2273       -2     
==========================================
- Hits         4372     4368       -4     
+ Misses       1364     1358       -6     
  Partials       47       47              
Files Changed Coverage Δ
packages/native/src/useLinking.tsx 86.12% <100.00%> (+1.42%) ⬆️

... and 1 file with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@osdnk
Copy link
Member

osdnk commented Sep 2, 2023

@adamgrzybowski @karlsander I guess that is safe to go?

@mountiny
Copy link

mountiny commented Sep 3, 2023

I think so, we are using it in our App without any issues found so far 🤞 https://github.com/Expensify/App/pull/25989/files

@karlsander
Copy link
Contributor Author

We are also using the patch with success, would appreciate if we can get this merged.

@osdnk osdnk enabled auto-merge (squash) September 4, 2023 09:21
@osdnk osdnk merged commit d8dc693 into react-navigation:main Sep 4, 2023
8 checks passed
satya164 pushed a commit that referenced this pull request Sep 25, 2023
**Motivation**

This function is used to ensure that all state change callbacks to sync
the local browser history state run in series. The functions are async
because `history.go` is not a synchronous function.

I noticed that in chrome, the URL was updating to the previous url
instead of the new one in some navigations and tracked it down to the
fact that the `history.go` navigation takes longer than in other
browsers. This function is supposed to mitigate that, but I believe it
has a bug. The function will not properly return early, because the
finally clause will still always run and immediately reset the
`isRunning` state. Even after fixing this, the function didn't quite
work right for queuing more than two callbacks.

I have replaced the function with a simpler implementation that does the
same task.

**Test plan**

Describe the **steps to test this change** so that a reviewer can verify
it. Provide screenshots or videos if the change affects UI.

The change must pass lint, typescript and tests.

---------

Co-authored-by: Michał Osadnik <micosa97@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants