facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.28k stars 24.35k forks source link

AsyncStorage not fulfilling promise on Android 7+ #14101

Closed jsdario closed 6 years ago

jsdario commented 7 years ago

Issue

I am using redux-persist in a react native project, that runs just fine in a broad number of devices execept Android 7. I am trying to debug the problem on why my AsyncStorage is nor persisting and found this:

The following code executes inside React Native component lifecycle's

  componentDidMount() {
    attachObservables(store)
    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.

Same problem I had using firebase js library (callbacks never return after the first one). I am now looking for alternatives to workaround the problem.

Works with setImmediate and InteractionManager.runAfterInteractions. Seems that redux-persist uses setImmediate or fallsback to setTimeout.

Any ideas?

Additional Information

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"

    defaultConfig {
        multiDexEnabled true
        applicationId "com.netbeast.yeti"
        minSdkVersion 16
        targetSdkVersion 24
        versionCode 69
        versionName "1.3.91"
        ndk {
            abiFilters "armeabi-v7a", "x86"
        }
    }

In case this is a user level question I opened a question on stackoverflow, no luck yet, https://stackoverflow.com/questions/44114061/asyncstorage-is-not-returning-the-callback?noredirect=1#comment75248695_44114061

dinzo commented 7 years ago

@jsdario any updates on this? were you able to work around or resolve the problem?

jsdario commented 7 years ago

I was not able to fix it, but I cooked a heavy and chunky workaround. I simply reimplemented the AsyncStarage API around https://github.com/itinance/react-native-fs so we could use it as storage in redux-persist.

Here it is the package https://github.com/netbeast/redux-persist-react-native-fs @dinzo

If I learn any more I'll share it. I have to say that we are using a set of advanced Android build tools, which might be the reason. The problem still exists on react-native 0.44 and 0.45.

esamattis commented 7 years ago

I'm seeing this too. The promise returned by AsyncStorage.getItem() just never resolves on Android. iOS is fine. I'm on RN 0.50RC.

yinghang commented 7 years ago

Can confirm that I'm seeing this too. After doing a few bundle reloads, I start getting the Maximum call stack size exceeded error.

RickDT commented 7 years ago

I'm seeing this in RN-0.50.3. Trying to track down what precipitates it, because it doesn't happen all the time.

RickDT commented 6 years ago

After further investigation: in my case I had an AsyncTask in some Java code (a local web server in my case) that was blocking and hence blocking other AsyncTasks (which AsyncStorage relies on). Setting my AsyncTask to execute in parallel to other tasks (via myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) instead of myTask.execute()) fixed the issue.

yinghang commented 6 years ago

I'll take a look and see if that'll solve my issue.

allmaxgit commented 6 years ago

+1 Problem on android in react-native 0.50.3

HumanUndead commented 6 years ago

problem still exists 50.3

jsdario commented 6 years ago

I submitted a Pull Request with the proposed solution by @RickDT, so far it is working for me.

dabit3 commented 6 years ago

Thanks @jsdario .

One hack that may work is something like this:

const resolvedPromisesArray = [
        AsyncStorage.getItem(TOKEN_KEY),
        AsyncStorage.getItem(TOKEN_KEY),
        AsyncStorage.getItem(TOKEN_KEY),
        AsyncStorage.getItem(TOKEN_KEY),
        AsyncStorage.getItem(TOKEN_KEY)
];
this.token = await Promise.race(resolvedPromisesArray);

Because it only seems to be happening sporadically, hitting it multiple times may give you a better chance of one of them resolving.

I know, this is very ugly 🥇

allmaxgit commented 6 years ago

@jsdario waiting to merge your PR :) Thanks 👍 Update: But this does not completely solve the problem :(

jsdario commented 6 years ago

How not? I am using it in production, can you further describe the issue @allmaxgit ?

RoBYCoNTe commented 6 years ago

I'm having this problem too. First time I execute the app code, AsyncStorage works fine, after few calls (4-5), getItem never call promise resolve function.

UberMC commented 6 years ago

Might work once after freshly rebooting device. I have to find an alternative AsyncStorage doesn't work for me, which sucks because saving things on device is so important.

?FIXED Updating to 0.51 appeared to fix this I was on 0.5.03

But After reload android emulator with 'rr' it appears to break and I have to do react-native run-android again.

logovaser commented 6 years ago

@UberMC I'm still having this problem on 0.51

manjeets12 commented 6 years ago

any update on this, facing same issue with RN 0.50.0

julestruong commented 6 years ago

same, did anyone figure a solution out ?

logovaser commented 6 years ago

@julestruong I face this issue on Ubuntu on ejected project. Expo project is fine, projects on Windows also doesn't have any issues. Seems like something is blocking the debugger port for AsyncStorage, hard to say exactly. Hope this will help you.

Fortidude commented 6 years ago

Faced the same issue on emulator android 7 and device with android 7. RN 0.50

hm... @RickDT where should I inject you code with myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)

I dont make any other async task hm...

RickDT commented 6 years ago

@Fortidude it will probably be in a 3rd party dependency, like an npm module that uses native Java code. Or Java code that you've introduced into the project.

tuomashatakka commented 6 years ago

One way to solve this for now is to have a completely separate interface for the development environment's storage where the async storage fails to function.

That's what I did – I got tired of figuring on solving the problem so I wrote a simple wrapper for the storage. The storage uses idb when the app is run in the development mode and the AsyncStorage if the remote debugger is disabled or running in production mode.

Feel free to use the wrapper I wrote (available on https://gitlab.com/snippets/1690090). I haven't had a single problem with the storage ever since I enabled this on my most recent RN project.

The only downside is that Reactotron fails to show the storage contents automatically since AsyncStorage isn't used.

Graren commented 6 years ago

How is progress on this issue? I'm seeing the same error on removing multiple keys and waiting with promise.all, it just never resolves

MedinaGitHub commented 6 years ago

I'm having this problem too.

julestruong commented 6 years ago

for me, i has been solved with 0.51

albertpb commented 6 years ago

I'm having the same problem, using react native 0.51. I tried with multiSet and setItem both never resolve.

vandervidi commented 6 years ago

Facing the same issue in 0.50.3. Any solutions?

redpanda-bit commented 6 years ago

"react-native": "^0.52.0" facing the same issue.

jsdario commented 6 years ago

You guys could check if my PR works for you? So far I believe it does work, it has been tested and commented by others, but not yet merged. Please, comment back if it works.

16905

redpanda-bit commented 6 years ago

Interesting enough, I gave it a last shot and found a workaround.

  1. Quit my android simulator
  2. Ran a new build using Android Studio
  3. Ran npm start on my terminal
  4. Reloaded project on simulator (RR)
  5. Magic.

Also:

Haven't been able to replicate issue at the moment.

react-native: 0.52.0 redux-persist: 5.4.0

edit: The issue is replicated when running a few reloads on hot reload. Running a new build using android studio fixes it. Works fine so far without hot reload or live reload.

cihadturhan commented 6 years ago

I'm having this issue too. Async Storage is broken after a few hot module reload or menu button-> refresh. If I swipe and kill the app and start again, everything works normally.

I think this won't be an issue in production because these two options is only available in development mode.

jakewtaylor commented 6 years ago

I'm having this problem in 0.51, #16905 didn't fix for me. Any other solutions?

dorshay6 commented 6 years ago

+1 on 0.51

SSTPIERRE2 commented 6 years ago

+1 on 0.50.3

This is a major show stopper! Need a solution!

jsdario commented 6 years ago

Please guys, avoid +1 comments. Rather use the reactions feature. We all get notified but it doesn't add up to the conversation or opened pull requests.

It may also help if the icebox tag changes to bug or other with higher relevance.

Graren commented 6 years ago

I'd like to comment that I fixed what was happening at it was a dumb fault of mine, I'm on 0.50.3 I was using the promise.all to delete and save keys, but in one use case, a particular key was null and I hadn't noticed. Async storage does not complain, but IOS dictionaries and android dbs do, since you can't save a key with a null value just like that.

cihadturhan commented 6 years ago

Unfortunately, this is not an issue for me. I check for both key and value, they are all string. Also, null key should raise an exception if it doesn’t.

hanselsen commented 6 years ago

Please help. This is also happening at my code

  AsyncStorage.getItem('loggedInUser').then(item => {
    debugger;
    /*not here*/
  }).catch(e => {
    debugger;
    /*and not here*/
  });
smaccoun commented 6 years ago

Can anyone comment here if they are seeing this in production releases? I'm getting nervous about using this in our production app. I'm getting the same non-deterministic behavior, where it tends to work for a while on first load then just stops working.

I think the following are useful questions to diagnose this. I also provided what I personally am currently seeing

  1. Does this happen only on Android Emulator? Anyone tested extensively on physical devices? What version of Android?

_Seen on release versions and debug. Tested with release version on Samsung Galaxy Tab A (Android 5.1)

Also consistently seen Emulators with Nexus 5x (API 25, Android 7) and Nexus 7 tablet (API 27, Android 7)_

  1. Does this occur on getItem/setItem... both?

I have seen the behavior on both.

  1. What is the surrounding context AsyncStorage is being called in

Have tried in both Redux thunks and sagas

  1. Working solutions so far?

Rebuilding android (react-native run android) always fixes the issue for at least a while

costagolub commented 6 years ago

I use 'redux-persist'. I've tested on a physical device Android 6+ and encounter the same problem as on the emulators. It works but after some time spent around the application stops working normally. In my case, rehydate action never calls after this, the only one solution on the emulator just to kill and re-start.

jakewtaylor commented 6 years ago

I've just compiled a release APK, and it looks like this bug is still happening. My app is hanging when trying to store data. It appears to be successfully storing the data, but the promise isn't being fulfilled.

We have a login screen in our app, that tries to store a JWT using AsyncStorage, the login screen never completes, but if I close the app and launch it again, it manages to load the JWT and the main screen starts, so it looks like it manages to store the data, but can't complete the promise.

rt2zz commented 6 years ago

considering the ongoing issues with AsyncStorage on android (low size limit, not fulfilling promises in debug mode) I think the recommendation for the time being is to use fs storage adapters.

There are quite a few options, and there use is not limited to redux-persist (perhaps these packages should be renamed to something more generic like storage-adapter-X

The only limitation here is they do not have the merge and multi methods of AsyncStorage

OtacilioN commented 6 years ago

Facing the same issue in react-native: 0.50.4

lancedolan commented 6 years ago

@jakewtaylor

I've just compiled a release APK, and it looks like this bug is still happening.

So this problem exists in production releases as well?!?

Up until I read your comment, I was under the impression this only impacts the dev environment and we'd be safe to produce production releases for the app market. If what you're saying is true, AsyncStrorage is no longer a valid solution for us and we need to bring in a separate product, like Realm or something.

Can you please confirm that this is what you meant?

ajthyng commented 6 years ago

@lancedolan I just switched to Realm, read this before switching: https://github.com/realm/realm-js/issues/491

lancedolan commented 6 years ago

UGH thanks for the heads up...

How does a RN dev do local storage in a way that works consistently and reliably in Dev and Production environments for both Android and IOS???

RickDT commented 6 years ago

Does anyone have a failing test app or test case for this bug?

cihadturhan commented 6 years ago

We have a team of testers and one of them reported he doesn’t retrieve a data saved from device storage and I hope it’s because of a bug in my code. If it’s related to the issue discussed here and happens in production, we are totally screwed. I’ll investigate it further.

lancedolan commented 6 years ago

It feels intermittent to me, which is usually the case when something is not intermittent and you simply haven't discovered the cause yet. When I have reliable steps to reproduce I'll post 'em up.

My experience debugging the issue was that the promise is simply never resolved and a breakpoint in the code in the promise callback is never hit. No errors, totally silent failure. I lost 2.5 hours last night chasing it around until I noticed that restarting the app ( just swipe it close from within emulator and restart) solves it.

In the meantime, I'm hiding the AsyncStorage usage behind an interface so I can jump ship the moment I find a reliable local storage solution if this isn't solved.

lordcatalien commented 6 years ago

Seconded. Everything @lancedolan said I can repeat in my code.