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 AsyncStorage not resolving its promises #16905

Closed
wants to merge 4 commits into from

Conversation

jsdario
Copy link
Contributor

@jsdario jsdario commented Nov 21, 2017

Motivation

Projects with other background activities may seen its AsyncStorage problems delayed or never resolved. Here it is an old issue explaining the bug conditions for devices with Android 7+

#14101 (comment)

Test Plan

We can reproduce the error following the steps in issues, which I copy paste here:

The following code executes inside React Native component lifecycle's

  componentDidMount() {
    setInterval(async () => {
      console.log('Inside setInterval')
      const data = await AsyncStorage.getAllKeys()
      console.log('inside the getAllKeys')
      data.forEach(async k => {
        const value = await AsyncStorage.getItem(k)
        console.group(k)
        console.log(value)
        console.groupEnd()
      })
    }, 3000)
  }

Code after 'Inside setInterval' is never called. It only runs once if outside the setInterval. If I call once the code outside the setInterval it appears to run just fine. I also tried callback format vs async / await version but it does not seem to matter.

Release Notes

[ANDROID] [BUGFIX] [ReactAndroid/src/main/java/com/facebook/react/modules/storage/AsyncStorageModule.java] - Fixed AsyncStorage when not resolving its promises for Android 7+

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 21, 2017
@jsdario jsdario changed the title Prevent AsyncStorage from not resolving its promises Fix AsyncStorage not resolving its promises Nov 21, 2017
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@shergin has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot facebook-github-bot added the Import Started This pull request has been imported. This does not imply the PR has been approved. label Nov 27, 2017
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@shergin is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot facebook-github-bot added Import Failed and removed Import Started This pull request has been imported. This does not imply the PR has been approved. labels Nov 27, 2017
@facebook-github-bot
Copy link
Contributor

I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification.

@facebook-github-bot facebook-github-bot added the Import Started This pull request has been imported. This does not imply the PR has been approved. label Nov 28, 2017
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@shergin is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@shergin is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@jsdario
Copy link
Contributor Author

jsdario commented Nov 29, 2017

The same thing happened with other PRs of mine, that have been accepted as well. If I can do something to fix / improve this, let me know!

@mbret
Copy link

mbret commented Dec 13, 2017

Hey @jsdario is there any chance for you to update your PR ? Do you need help or anything ?

@jsdario
Copy link
Contributor Author

jsdario commented Dec 14, 2017

I believe it's ready to merge, I am using this fork on production, no surprises so far > 5000 devices

dannycochran added a commit to dannycochran/react-native that referenced this pull request Dec 14, 2017
Per facebook#16905, AsyncStorage can fail to resolve its promises, causing our app to hang indefinitely at loading screen.

This commit takes the changes from that pull (without cherry-picking, just eye-balling), and commits it to our forked version of 0.51.
@dannycochran dannycochran mentioned this pull request Dec 14, 2017
@dannycochran
Copy link
Contributor

Is it possible to do a release, 0.51.1, with this in there? AsyncStorage being broken likely is blocking a lot of apps, like mine, that assume the promise will always resolve.

@esprehn
Copy link
Contributor

esprehn commented Dec 16, 2017

How does this compile? Did you mean executeOnExecutor? This seems to pass AsyncTask.THREAD_POOL_EXECUTOR into a bunch of stuff that doesn't actually accept an executor (ex SQLiteStatement or AsyncTask#execute).

@RickDT
Copy link

RickDT commented Dec 16, 2017

@esprehn that is why the CircleCI tests are failing. It should be executeOnExecutor. I'm also concerned about arbitrarily changing AsyncStorage from being serial, which guarantees order, to parallel, which would have no such guarantee. For instance, if I use AsyncStorage to save a new value for a key 3 times in a row and then immediately read it back out, there's no guarantee I will get the third value. That would probably cause a lot of knock-on effects for developers.

However, AsyncStorage does have potential deadlocks with other uses of AsyncTasks in the app. In my app, we were able to change to parallel tasks to avoid the deadlock.

Ideally, AsyncStorage's use of AsyncTasks would place the work on it's own serial Executor instead of the default one. I'm not familiar enough with Java to competently implement this or I'd jump in :/ (I just wrote my first copy-pasta Java code about a month ago, when I ran across this issue)

@esprehn
Copy link
Contributor

esprehn commented Dec 18, 2017

@RickDT I did that by copying the SerialExecutor [1] into a forked version of AsyncStorageModule (and friends).

It's alarming that something is blocking the shared SERIAL_EXECUTOR though. It doesn't seem like it's been identified in the bug, does anyone know what it is?

[1] https://android.googlesource.com/platform/frameworks/base.git/+/1488a3a19d4681a41fb45570c15e14d99db1cb66/core/java/android/os/AsyncTask.java#237

@RickDT
Copy link

RickDT commented Dec 19, 2017

I believe this happens due to other Java code in the app, outside of React Native, behaving badly and blocking the shared SERIAL_EXECUTOR.

The OP claims "Projects with other background activities...", which is certainly where we saw it in our codebase.

The test case in the OP may be failing due to a bug in the test case. I created a new RN app and pasted the test code and indeed did not get the inner log. However, that's because nothing had been saved in AsyncStorage. The updated test case does work as expected with AsyncStorage:

async componentDidMount() {
  const intervalMillis = 3000;

  //save something first
  await AsyncStorage.setItem("test1", "this is a good test");  

  setInterval(async () => {
    console.log("Inside setInterval");
    const data = await AsyncStorage.getAllKeys();
    console.log("inside the getAllKeys");
    data.forEach(async k => {
      const value = await AsyncStorage.getItem(k);
      console.group(k);
      console.log(value);
      console.groupEnd();
    });
  }, intervalMillis);
}

This will run successfully over many iterations, and if you set intervalMillis to 0 in order to really test the theory, it also runs continuously without blocking.

I think that putting AsyncStorage on a non-shared serial executor will solve the problem for most cases. Other cases may be another bug altogether. If not, we need a true failing test case. If needed, I can whip up a test project that demonstrates blocking from outside of RN in a misbehaving AsyncTask.

@julestruong
Copy link

i believe it is fixed in v51

@sunnylqm
Copy link
Contributor

What is the status of this PR now? @shergin

@dannycochran
Copy link
Contributor

With your code snippet, @RickDT, and remote debugging running, if I refresh the default react-native app example app fast enough and often enough, eventually the first call to AsyncStorage will hang indefinitely and never resolve. If I turn off remote debugging, the call will never hang.

Are others experiencing this problem with remote debugging turned off?

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@shergin is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@dannycochran
Copy link
Contributor

As far as I can tell, the change in this commit does not actually work.

We've been using the following change in production for the past 4 months and it works:
#18522

@RickDT
Copy link

RickDT commented Mar 23, 2018

The solution from @dannycochran has two advantages: staying with Sync execution and not relying on shared system executors. I would much prefer it get merged.

I think the root of the problem is that the shared system executors are getting out of whack when restarting the React Native application without restarting the underlying Android Main Activity.

And (sorry to repeat myself), arbitrarily swapping to parallel execution could definitely result in unanticipated knock-on effects for some app developers.

@slegaitis

This comment has been minimized.

@slegaitis
Copy link

slegaitis commented Apr 11, 2018

I figured a sort of reason why this is happening. If You click CTRL + S or CMD + S to save the file twice this will trigere, Live Reload. Sometimes if you click ctrl + s or cmd + s once Live Reload doesn't work, and if you click it second time almost immediately after first attempt, I'm able to replicate the issue.

Workaround that works for me for now is to close the app, go to Simulator/ Real device menu and open it again. Then AsyncStorage works again.

@thinhtrh

This comment has been minimized.

@nstanwar

This comment has been minimized.

@Pastromhaug
Copy link

I still have this problem in 0.55.4

@Gennady77

This comment has been minimized.

@tntdarken
Copy link

I think I have found a possible solution to this problem. I hope it helps:

Component:
componentWillMount(){
this.storageClass.getStorage().then( retorno => this.setState(retorno));
}

storageClass:
async getStorage(){
data = await AsyncStorage.getAllKeys();
return AsyncStorage.multiGet(data).then( data => {
let retorno = {};
data.forEach( async x => {
key = x[0];
value = x[1];
retorno[key] = value;
});
return retorno;
});
}

@leakim34
Copy link

leakim34 commented Jun 8, 2018

Hi,

same for me here and very frustrating because asyncstorage is as major dep of Apollo Client when we want to cache the app.

Please we need a fix for this ... opened since May 2K17 !

Thanks everyone

@leandrohsilveira
Copy link

As @slegaitis said, it is something related to Live Reload option.
It works when the app is opened with the Live Reload disabled.

Fortunately, it also works with "Reload" option and the VSCode Extension called React Native Tools has a shortcut to use this option using CTRL + Shift + P > React-Native: Reload App.

@bstst
Copy link

bstst commented Jun 19, 2018

It does not work with remote debugging enabled though, no matter if Live Reload is enabled.

hramos pushed a commit to hramos/react-native that referenced this pull request Jun 28, 2018
Summary:
This patch is a bit of a hack job, but I'd argue it's necessary to dramatically improve the dev UX on Android devices. Somewhere in react-native, there's a shared SerialExecutor which AsyncStorage uses that is getting blocked, causing remote debugging to occasionally hang indefinitely for folks making AsyncStorage requests. This is frustrating from a dev UX perspective, and has persisted across several versions as far back as RN 0.44, and still remains on RN 0.54.

The issue seems to only happen on Android > 7+, which is likely because the ThreadPoolExecutor behavior changed in this version:
https://stackoverflow.com/questions/9654148/android-asynctask-threads-limits

Fixes facebook#14101

We've been using this patch in production for the past 4 months on our team by overriding the AsyncStorage native module. We use AsyncStorage extensively for offline data and caching.

You can test by compiling this commit version into a test react native repository that is set to build from source:

```sh
git clone https://github.com/dannycochran/react-native rnAsyncStorage
cd rnAsyncStorage
git checkout asyncStorage
cd ..
git clone https://github.com/dannycochran/asyncStorageTest
yarn install
cp -r ../rnAsyncStorage node_modules/react-native
react-native run-android
```

No documentation change is required.

facebook#16905

[Android] [BUGFIX] [AsyncStorage] - Fix AsyncStorage causing remote debugger to hang indefinitely.
Closes facebook#18522

Differential Revision: D8624088

fbshipit-source-id: 2a599fbca270a4826063cd93be8eaf796f34f57a
@haggholm
Copy link

Any news on this? It looks to me like the CI test was failing for reasons unrelated to the PR; maybe it just needs to re-run? It would be great to see this in the next RN release…

facebook-github-bot pushed a commit that referenced this pull request Jul 30, 2018
Summary:
This patch is a bit of a hack job, but I'd argue it's necessary to dramatically improve the dev UX on Android devices. Somewhere in react-native, there's a shared SerialExecutor which AsyncStorage uses that is getting blocked, causing remote debugging to occasionally hang indefinitely for folks making AsyncStorage requests. This is frustrating from a dev UX perspective, and has persisted across several versions as far back as RN 0.44, and still remains on RN 0.54.

The issue seems to only happen on Android > 7+, which is likely because the ThreadPoolExecutor behavior changed in this version:
https://stackoverflow.com/questions/9654148/android-asynctask-threads-limits

Fixes #14101

We've been using this patch in production for the past 4 months on our team by overriding the AsyncStorage native module. We use AsyncStorage extensively for offline data and caching.

You can test by compiling this commit version into a test react native repository that is set to build from source:

```sh
git clone https://github.com/dannycochran/react-native rnAsyncStorage
cd rnAsyncStorage
git checkout asyncStorage
cd ..
git clone https://github.com/dannycochran/asyncStorageTest
yarn install
cp -r ../rnAsyncStorage node_modules/react-native
react-native run-android
```

No documentation change is required.

#16905

[Android] [BUGFIX] [AsyncStorage] - Fix AsyncStorage causing remote debugger to hang indefinitely.
Pull Request resolved: #18522

Differential Revision: D8624088

Pulled By: hramos

fbshipit-source-id: a1d2e3458d98467845cb34ac73f2aafaaa15ace2
darabi pushed a commit to darabi/react-native that referenced this pull request Aug 9, 2018
Summary:
This patch is a bit of a hack job, but I'd argue it's necessary to dramatically improve the dev UX on Android devices. Somewhere in react-native, there's a shared SerialExecutor which AsyncStorage uses that is getting blocked, causing remote debugging to occasionally hang indefinitely for folks making AsyncStorage requests. This is frustrating from a dev UX perspective, and has persisted across several versions as far back as RN 0.44, and still remains on RN 0.54.

The issue seems to only happen on Android > 7+, which is likely because the ThreadPoolExecutor behavior changed in this version:
https://stackoverflow.com/questions/9654148/android-asynctask-threads-limits

Fixes facebook#14101

We've been using this patch in production for the past 4 months on our team by overriding the AsyncStorage native module. We use AsyncStorage extensively for offline data and caching.

You can test by compiling this commit version into a test react native repository that is set to build from source:

```sh
git clone https://github.com/dannycochran/react-native rnAsyncStorage
cd rnAsyncStorage
git checkout asyncStorage
cd ..
git clone https://github.com/dannycochran/asyncStorageTest
yarn install
cp -r ../rnAsyncStorage node_modules/react-native
react-native run-android
```

No documentation change is required.

facebook#16905

[Android] [BUGFIX] [AsyncStorage] - Fix AsyncStorage causing remote debugger to hang indefinitely.
Pull Request resolved: facebook#18522

Differential Revision: D8624088

Pulled By: hramos

fbshipit-source-id: a1d2e3458d98467845cb34ac73f2aafaaa15ace2
darabi pushed a commit to darabi/react-native that referenced this pull request Aug 10, 2018
Summary:
This patch is a bit of a hack job, but I'd argue it's necessary to dramatically improve the dev UX on Android devices. Somewhere in react-native, there's a shared SerialExecutor which AsyncStorage uses that is getting blocked, causing remote debugging to occasionally hang indefinitely for folks making AsyncStorage requests. This is frustrating from a dev UX perspective, and has persisted across several versions as far back as RN 0.44, and still remains on RN 0.54.

The issue seems to only happen on Android > 7+, which is likely because the ThreadPoolExecutor behavior changed in this version:
https://stackoverflow.com/questions/9654148/android-asynctask-threads-limits

Fixes facebook#14101

We've been using this patch in production for the past 4 months on our team by overriding the AsyncStorage native module. We use AsyncStorage extensively for offline data and caching.

You can test by compiling this commit version into a test react native repository that is set to build from source:

```sh
git clone https://github.com/dannycochran/react-native rnAsyncStorage
cd rnAsyncStorage
git checkout asyncStorage
cd ..
git clone https://github.com/dannycochran/asyncStorageTest
yarn install
cp -r ../rnAsyncStorage node_modules/react-native
react-native run-android
```

No documentation change is required.

facebook#16905

[Android] [BUGFIX] [AsyncStorage] - Fix AsyncStorage causing remote debugger to hang indefinitely.
Pull Request resolved: facebook#18522

Differential Revision: D8624088

Pulled By: hramos

fbshipit-source-id: a1d2e3458d98467845cb34ac73f2aafaaa15ace2
@ITsolution-git
Copy link

Hello.
When I use debugger, asyncstorage promise seems to be resolved.
But it does not work without debugging, and it does not work on testflight deployed.
The asyncstorage promise never resolves, and even the app freezes and abort after about 5-10 seconds.

I placed an loader till the promise resolves but loader is not rendered and seems like the app refused to render anymore.

Please help anyone.
Thanks

@jsdario
Copy link
Contributor Author

jsdario commented Nov 21, 2018

Closing as the bug is solved and this PR is over a year old

@jsdario jsdario closed this Nov 21, 2018
@hramos hramos added Merged This PR has been merged. and removed Import Failed labels Mar 8, 2019
@react-native-bot react-native-bot removed the Import Started This pull request has been imported. This does not imply the PR has been approved. label Mar 8, 2019
@Waqas-Jani
Copy link

i have the same issue .. asynstroage not working on Android version 24+(7+).. any one has solutaion??

@ollyfg
Copy link

ollyfg commented Aug 30, 2019

@Waqas-Jani Can you tell us what version of React Native you are running? Are you using the older built-in AsyncStorage or the newer community one (@react-native-community/async-storage)? Are you seeing any related errors, either through JS debugging or logCat?

Either way, this bug has been solved and you might be seeing a different one. I would recommend opening a new issue on https://github.com/react-native-community/async-storage

Corey-Peyton added a commit to Corey-Peyton/async-storage that referenced this pull request Jul 14, 2021
Summary:
This patch is a bit of a hack job, but I'd argue it's necessary to dramatically improve the dev UX on Android devices. Somewhere in react-native, there's a shared SerialExecutor which AsyncStorage uses that is getting blocked, causing remote debugging to occasionally hang indefinitely for folks making AsyncStorage requests. This is frustrating from a dev UX perspective, and has persisted across several versions as far back as RN 0.44, and still remains on RN 0.54.

The issue seems to only happen on Android > 7+, which is likely because the ThreadPoolExecutor behavior changed in this version:
https://stackoverflow.com/questions/9654148/android-asynctask-threads-limits

Fixes #14101

We've been using this patch in production for the past 4 months on our team by overriding the AsyncStorage native module. We use AsyncStorage extensively for offline data and caching.

You can test by compiling this commit version into a test react native repository that is set to build from source:

```sh
git clone https://github.com/dannycochran/react-native rnAsyncStorage
cd rnAsyncStorage
git checkout asyncStorage
cd ..
git clone https://github.com/dannycochran/asyncStorageTest
yarn install
cp -r ../rnAsyncStorage node_modules/react-native
react-native run-android
```

No documentation change is required.

facebook/react-native#16905

[Android] [BUGFIX] [AsyncStorage] - Fix AsyncStorage causing remote debugger to hang indefinitely.
Pull Request resolved: facebook/react-native#18522

Differential Revision: D8624088

Pulled By: hramos

fbshipit-source-id: a1d2e3458d98467845cb34ac73f2aafaaa15ace2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. Ran Commands One of our bots successfully processed a command.
Projects
None yet
Development

Successfully merging this pull request may close these issues.