Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native AsyncStorage returns promise instead of value

I understand this is a very common problem in RN and I am still trying to understand the very possible advantage of returning a promise when loading data from a property file instead of just returning the value, which makes chaining requests very cumbersome...but anyway. Here is what I have right now, which is a wrapper from the AsyncStorage RN implementation:

multiGet = async (key) => {
    var value = null;
    try {
      value = await AsyncStorage.multiGet(key).then(
        (values) => {
          value = values;
          console.log('Then: ',values);
        });
    } catch (error) {
      console.log('Error: ',error);
    }
    console.log('Final: ',value);
    return value;
    }

At this point, value gets undefined. In my main code I have this:

var filter = this._getFilter();
console.log('returned filter:',filter);

The _getFilter function is the one using the AsyncStorage wrapper but the 'returned filter' is logging before the first function so it is not waiting for the returned values before continue, so I get an undefined value.

At first, I thought that just by using the async/await the AsyncStorage wold return a value instead of a promise but after testing, the value I get from:

value = await AsyncStorage.getItem('key') 

is STILL a promise, so I have to use then() to read the value.

Basically the order that I am seeing in the logs is:

_getFilter
returned value: undefined
Then: value: here I get the correct value from the keys but the code already passed and I don't have the correct value in the variable

I have no clue what is going on or how to handle this correctly. This is supposed to be very simple and common use case.

I would love to solve this without using a third party module.

Thanks

SOLUTION Edit: After understanding a little more about the concepts of async/await and callbacks, I finally have a code that works. I don't like it, because it makes the code very hard to read. I might need to refactor it to use promises but for now, it works. Here are some snippets in case someone finds the same issue:

this._getFilter(body,this._filterSearchCallback,callback);

Note: I am sending the body through the chain because I am "completing" the information as I pass the functions. The second parameter is the first callback that actually makes a fetch query and the third callback is the return of the fetch function.

_getFilter(body,callback,returnCallback){

      {...}

      this._sh.multiGet(keysBanks).then(
        (banks) => {
          filter.banks = banks;
          console.log(banks);
          this._sh.multiGet(keysCards).then(
            (cards) => {
              console.log(cards);
              filter.credit_cards = cards;
              callback(body,filter,returnCallback);
            });
        }
      );

    }

Here basically I am chaining a couple of gets because I need several values from the store. This is the part I dont really like. _sh is my StorageHelper which is a wrapper to the AsyncStorage, nothing fancy.

multiGet = async (key) => {
    const value = await AsyncStorage.multiGet(key);
    return value;
    }

Then my very last callback that actually makes the fetch and send the JSON response to the main screen in react native:

_filterSearchCallback(body,filter,callback){

      body.filter = filter;

      return fetch(apiUrl, {method: 'post', body: JSON.stringify(body)})
      .then((response) => response.json())
      .then((responseJson) => {
        callback(responseJson);
      })
      .catch((error) => {
        console.error(error);
        callback(responseJson);
      });
    }

I will improve this and make it cleaner but for now, it works. Hope it helps others too.

like image 502
sebastianf182 Avatar asked Mar 16 '17 19:03

sebastianf182


2 Answers

Once upon a time, i was having the same problem so what I did I will share with you here.

Basically, your execution is moving forward without taking any value i.e undefined what you are getting right now so there are 3-4 ways to get out of this:

1) async await 2) callback

1) We will start with the callback which is use by most of the people.

We will use your code to implement this:

    _getFilter(key,callback)
{
    multiGet = (key) => {
        var collect;
        try {
          var value = AsyncStorage.multiGet(key).then(
            (values) => {
           //   value = values;
              console.log('Then: ',values);
             callback(values)
            });
        } catch (error) {
          console.log('Error: ',error);
        }
        console.log('Final: ',value);

        }
}

    this._getFilter(key,function(filter){
      console.log('returned filter:',filter);
    });

2)async/await

If you are using await alone then you would get an error, to use await inside a function you have to declare the async function by setting async keyword before the function name.

 async _getFilter(key)
{

multiGet = async (key) => {
    var value,collect;
    try {
      value = await AsyncStorage.multiGet(key).then(
        (values) => {
          collect= values;
          console.log('Then: ',values);
        });
    } catch (error) {
      console.log('Error: ',error);
    }
    console.log('Final: ',value);
    return collect;
    }

//calling the async function

this._getFilter(key).then((filter)=>{
if(filter!=null)
console.log('returned filter:',filter)
else
console.log('error')
})

Hope this would clear your concepts and help you with other react native developers.I have seen lots of people struggling with this thing so today I got the chance to clear your doubts.

Cheers :)

like image 58
Codesingh Avatar answered Oct 19 '22 23:10

Codesingh


the thing is await turns the promise into a value, you don't need to use .then(). Try the following:

const keys = await AsyncStorage.getAllKeys()
const values = await AsyncStorage.multiGet(keys)

// at this point `values` will have data for all keys 
like image 39
dhorelik Avatar answered Oct 20 '22 00:10

dhorelik