-1

I watched a tutorial, where I was told that I should use async methods to access localStorage (in an Ionic Angular project, don't know if this is important or if it affects the programm if I export it as an mobile application with capacitor)

I have this example:

async store(storageKey: string, value: any) {

    const encryptedValue = btoa(escape(JSON.stringify(value)));

    await Storage.set({
      key: storageKey,
      value: encryptedValue
    });
  }

but isn't that the same as just

store(storageKey: string, value: any) {

    const encryptedValue = btoa(escape(JSON.stringify(value)));
    localStorage.setItem(storageKey, encryptedValue);
}

Is there a reason for using the more complicate async-method? Because working with it is really annoying.

EDIT: The Storage is a capacitor plugin:

import { Plugins } from '@capacitor/core'

const { Storage } = Plugins;

That also converts the code to use it on mobile devices that use different storage-Systems. Thats what the capacitor docs say:

The Storage API provides a key-value store for simple data. Mobile OS's may periodically clear data set in window.localStorage, so this API should be used instead of window.localStorage. This API will fall back to using localStorage when running as a Progressive Web App. On iOS this plugin will use UserDefaults and on Android SharedPreferences. Stored data is cleared if the app is uninstalled.

https://capacitorjs.com/docs/apis/storage

Probably you need async methods because of that.

Continuing question: I need data from one of those async methods and I always had the problem with an error because the data has not loaded so far. Due to some experimenting I found this solution:

ngOnInit() {
    console.log("ONINIT IS CALLED");
    
    this.authService.userData$.subscribe(
      res => {
        console.log(res);
        if(!res) {
          return;
        }
        const payload = atob(res.split(".")[1]);
        const parsed_payload = JSON.parse(payload);
        const userID = parsed_payload.subject;
        this.catchService.get_catches(userID).subscribe(
          res => {
            this.catches = res;
            console.log(this.catches);
          },
          err => {
            this.toastService.presentToast('Fänge konnten nicht geladen werden.')
          }
        )
      },
      err => {
        this.toastService.presentToast('Fänge konnten nicht geladen werden, userID konnte nicht geladen werden.');
      }
    )
  }

If I leave out the

if(!res) {
   return;
}

it does not work, so my question is: What does this 'return' does. I always thought that a method would terminate if there is a return-statement. But if I look at the outputs i get this sequence:

console.log("ONINIT IS CALLED");
console.log(res); (That is an empty String)
console.log(res); (That is the value I need)
console.log(this.catches); (My data)
  • localStorage is just an object where you store key/values pairs. async is used for http requests or working with streams, observables. I don't see a reason to use it in this case. – AlleXyS Sep 07 '20 at 20:55
  • 1
    Does this answer your question? [Is HTML5 localStorage asynchronous?](https://stackoverflow.com/questions/20231163/is-html5-localstorage-asynchronous) – ale Sep 07 '20 at 21:05
  • First time I've ever heard that suggested. Does accessing LocalStorage in ionic take particularly long or something? Is this a performance thing? You could wrap it in a setTimeout to not block the thread. – hyperdrive Sep 07 '20 at 21:08
  • @alessandro I saw that question before but since I was suggested to use async in my tutorial I wanted to ask, if there might be a difference when using capacitor. See edit above please. – UnknownInnocent Sep 07 '20 at 21:24
  • @hyperdrive I edited the question, this might be the reason, why async is needed – UnknownInnocent Sep 07 '20 at 21:30
  • @UnknownInnocent If that's the storage API they expose then, ya. You'll have to use it like that. – hyperdrive Sep 07 '20 at 21:37

2 Answers2

1

You are confusing localStorage with Capacitor's Storage plugin.

All Capacitor plugins are async, because the API's that both Google and Apple provide in their respective WebViews only allow async communication between the javascript part of the app and the native part of the app, and Storage plugin uses native API's to store the data instead of using localStorage.

If you don't like the plugin or don't want to store your data in native storages then just use localStorage, but it can be wiped by the OS in certain cases.

jcesarmobile
  • 51,328
  • 11
  • 132
  • 176
-1

Note: this API is not meant for high-performance data storage applications.

This is the note just below the text that you copied from the docs page.

The biggest reason for using async calls for capacitor storage is that this operation is a bit slow. The application will have to wait for the data to be retrieved or written to the storage, thus impacting the overall performance of your apps.

Let's say your app wants to write to storage, if your code waits on the writing process to complete, it is going to waste a lot of precious computation time.

On the other hand, read tasks can be critical for the app flow, in which case, you can harness the power of async/await, promises, to match your requirement.

The reason for not using window.localStorage directly is exactly what the docs say. This plugin switches the storage type based on the platform, thus preventing the clearing of stored data by the OS.

EDIT -

async getObject(keyVal:string) {
    let strObject = await Storage.get({ key: keyVal });
    if (strObject) {
      return JSON.parse(strObject.value)
    }
    return null
} 

You can then use this method with await.

let token = await this.getObject('token');
//proceed with your tasks here

Or

this.getObject('token').then(data=>{
    let token = data;
    //proceed with your tasks here
})
Chetan Bansal
  • 1,794
  • 14
  • 18
  • And lets say, I need one item from the storage, how would I freeze all other methods until I received this item from the storage? – UnknownInnocent Sep 08 '20 at 10:43
  • Solution 1: If I do It like that I get an error, that await is only allowed in async methods and I can't mark my onInit as async Solution 2: I already tried something like that but it didn't work properly. I added some things to my question that worked but that I don't really understand – UnknownInnocent Sep 08 '20 at 11:11
  • Just a suggestion to your solution.. Wherever possible, use promises instead of subscriptions. Subscriptions lead to memory leaks if they are not unsubscribed in ngOnDestroy.. instead of using `.subscribe()` you can use `.toPromise().then()` – Chetan Bansal Sep 09 '20 at 05:42
  • But in the end, Subscriptions and promises do the same thing? – UnknownInnocent Sep 09 '20 at 14:18
  • In a way yes.. Subscriptions is like a stream of values.. whereas promises provides value once.. Read this for better understanding of both https://medium.com/javascript-everyday/javascript-theory-promise-vs-observable-d3087bc1239a – Chetan Bansal Sep 09 '20 at 14:45
  • If I change `.subscibe(....)` to `.toPromise().then(...)` the res-value is empty and the code inside then is not executed – UnknownInnocent Sep 09 '20 at 16:01