4

I have a react-native application, it uses react-navigation. There is a functional component with a handler for button click.

I recently had a problem with async/await. I called async method in a non-async method and it did not work as I expected. I debugged it a little and I found out that the async method is called and does everything it should but after that the changes are lost.

The non-async method looked like this:

const handleDone = () => {
  api.events.removeEventFromCache(eventId);
  navigation.navigate(routes.Events);
};

When the method is called, an object is removed from cache and user is navigated to another screen. api.events.removeEventFromCache(eventId) got called and finished successfully and I even check the cache to see that the object was removed. The thing is that after the navigation.navigate(routes.Events) it is suddenly still in the cache.

Adding async/await keyword solved the problem but I do not really understand why:

const handleDone = async () => {
  await api.events.removeEventFromCache(eventId);
  navigation.navigate(routes.Events);
};

I though it would do everything without waiting for the result but why did the result disappear? It is not a question about the order of executing and waiting for the result. I do not really care about the result, I just want it to be done.

This is the log made without the keywords:

--> in cache now 3
remove the event from cache
navigate to events
cache length before remove 3
--> in cache now 3
cache length set 2
cache length checked 2
--> in cache now 3

A log with the keywords:

--> in cache now 3
remove the event from cache
cache length before remove 3
cache length set 2
cache length checked 2
navigate to events
--> in cache now 2

Yes, there is a difference in execution but my question is about the result in cache.

CrossTheDev
  • 151
  • 1
  • 11
  • 1
    Possible duplicate of [Asynchronous vs synchronous execution, what does it really mean?](https://stackoverflow.com/questions/748175/asynchronous-vs-synchronous-execution-what-does-it-really-mean) – VLAZ Aug 22 '19 at 12:31
  • Async means it does something *at a different time* - asynchronously. So, if you do `asyncRemoveX(); getX()` the second line will still return you whatever `X` is because `asyncRemoveX` is not completed - it will finish at a later point. Which is why `await` exists - as the name suggests it *waits* for the async function to finish, so `await asyncRemoveX()` will be guaranteed to complete by the next line. – VLAZ Aug 22 '19 at 12:35
  • The Async/Await syntax essentially gives you an alternate method to write asynchronous code without callbacks. Your async function translates to something like: `api.events.removeEventFromCache(eventId).then(navigation.navigate())` – Nabeel Mehmood Aug 22 '19 at 12:38
  • It does not explain why the executed changes disappered without the keywords to me. One thing is how the methods are executed and that does not really bother me. What bothers me is that the execution got lost after it finished. (I look into the cache later in some other code long after the execution finished.) – CrossTheDev Aug 22 '19 at 12:41
  • It definitely seems like your navigation code executes before the `removeEventFromCache` method is finished executing. Once the navigation event occurs, the async method halts mid execution. – Nabeel Mehmood Aug 22 '19 at 12:45
  • Does that somehow rollback everything the asnyc method did? Because I logged every step of the method and it finished correctly. – CrossTheDev Aug 22 '19 at 12:48
  • Can you add the logs to your question. Log each line of your api method. And add a log before navigation executes. – Nabeel Mehmood Aug 22 '19 at 12:51
  • 1
    I added the logs. Last lines have different results. – CrossTheDev Aug 22 '19 at 13:11
  • 1
    From what I understand, the navigation method passes a copy of the cache to the navigation event. The copy is created and the previous one is discarded. In the first case, your application copies the cache while it still has the event and sends it off to the navigation function. The copy of the cache that your api method is editing, has your event removed, but that copy is no longer being used. In the second case, your navigation function receives the updated cache. Hope that helps. – Nabeel Mehmood Aug 22 '19 at 13:23

2 Answers2

2

When you log the output before and after navigation, you are logging in 2 different contexts.

To explain this, lets say you have a cache object cache from which you wish to remove the event. The way your code without the keywords executes is as follows:

  • cache is loaded by the api method to be edited
  • navigation method executes and it is going to send a copy of the current cache to the next screen and discard the previous.
  • cache-copy is created and dispatched by the navigation method.
  • You api method is currently still working with the cache object and not cache-copy.
  • cache is edited by the api method but is then discarded as the new screen is now using the cache-copy object.

In the second scenario:

  • The api method receives cache
  • The event is removed from cache
  • The navigation method receives the updated cache and creates cache-copy
  • cache-copy now has the updates list of events

The important thing to note is where and when exactly the cache-copy object is being created. If it is created before the event is removed, the code will work just fine.

Lets say, your navigation method executes the exact instant when the api method has removes the event, your code will run as expected even if async/await isn't used.

Nabeel Mehmood
  • 378
  • 1
  • 5
  • Is that how `AsyncStorage` from `react-native` works? I never thought of it as something that has work copies. That would explain it. To me it was always one place (one copy) that is accessed. – CrossTheDev Aug 22 '19 at 13:41
  • A cache is ideally used to optimize read access times with the database. Depending on what platform you're developing the application on, AsyncStorage uses SQLite or file storage to persist data. A cache however is kept in memory so important data is read directly from the memory rather than disk storage. – Nabeel Mehmood Aug 22 '19 at 14:39
0

async/await is just working as expected. When managing promises, you could have two options:

//Using promises
const handleDone = () => {
  api.events.removeEventFromCache(eventId).then(() => {
    navigation.navigate(routes.Events);
  });//You can manage failure with .catch()
};

and using async/await just as you posted, it waits until the promise is executed, it doesn't stop everything itself. Also, it is a good practice to wrap it inside a try/catch block in case the Promise fails.

The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

That means, when you call api.events.removeEventFromCache(eventId) it won't be completed immediately, so you either have to use one of both options.

Ian Vasco
  • 1,280
  • 13
  • 17