8

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.

sebastianf182
  • 9,844
  • 3
  • 34
  • 66

2 Answers2

14

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 :)

Codesingh
  • 3,316
  • 1
  • 11
  • 18
  • 2
    well, you actually confirmed my doubts. I was really really hoping to not having to deal with of all that just to get a property file value but...I guess that is the way to go. I'll try this out and confirm if this works. – sebastianf182 Mar 16 '17 at 20:13
  • Happy to help you friend :) just go for it – Codesingh Mar 16 '17 at 20:14
  • Try callback one also so that you get rough idea about both the concepts – Codesingh Mar 16 '17 at 20:15
  • Alright I made it work but I am not going to win an award for the most readable code. Still don't get it. I'll post my solution in the question and award you the answer, that could help others as well. – sebastianf182 Mar 16 '17 at 20:34
  • Fine it worked for you but where are you still face the problem? – Codesingh Mar 17 '17 at 17:09
  • No, I don't have the problem anymore. I made a several chain of callbacks and that worked. – sebastianf182 Mar 19 '17 at 02:09
7

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 
dhorelik
  • 1,477
  • 11
  • 14
  • 2
    That is my understanding too BUT to use await, I need to make my function async, which makes the await pointless as now I return a promise again. And to handle the now async function, I need to handle it with then() in the main method. – sebastianf182 Mar 16 '17 at 19:58
  • 1
    @sfratini, isn't this kind of the point of *Async*Storage? to make access asynchronous? – Joseph Nields Mar 16 '17 at 20:04
  • @JosephNields Yes, what I meant is that, the await makes you wait for the response from the AsyncStorage, making the call synchronous. But, to use await I need to make the calling function asynchronous, therefore handling the promise somewhere. I just want the code to stop until having a response and not having to chain everything with then() since sometimes, is more cumbersome and makes the code harder to maintain. – sebastianf182 Mar 16 '17 at 20:10
  • @sfratini why don't you make the function which has to handle the async function async as well? This way you basically `await multiGet` and go on with the rest of the code – dhorelik Mar 16 '17 at 20:37
  • As soon as I do that, the calling function becomes async and does not stop, so I have to handle the promise there too :) Like I said, somewhere you have to deal with the promise. I just posted a solution that works, which I dont like, because is ugly but well. – sebastianf182 Mar 16 '17 at 20:42