Ionic Storage and initial data loading / local data

Hey,

I am using the Ionic storage with a provider.

If my storage is empty (i.e. first time app starts), I would like to populate it with initially stored data from my Ionic storage.

What is the best approach for that?

I was thinking to perform something like this:

StorageProvider.ts:

  initData(){
    return new Promise(resolve => {
      this.storage.set(this.storageKey, [some, dummy, data]);
      resolve(this.storage.get(this.storageKey));
      });
  } 
  
  getStoredData(){
    return new Promise(resolve => {
      this.storage.get(this.storageKey).then((data) => {
        resolve(data);
      });
    })
  }

MainPage.ts:
  ionViewWillEnter() {
     this.myData = this.myDataProvider.getStoredData().then(data => { 
       if (data == null) {
           this.myDataProvider.initData();
           return (this.myDataProvider.getStoredData().then(data => data));
        } else {
          return data;
       }
    }

Any issues with that?

Other than that, should I fetch the data from a simple variable (i.e. array object), or should I make a http.get on a locally stored json file?

Thanks in advance!

This is an example of the explicit Promise construction antipattern. If you see a blog tutorial recommend this, it’s almost guaranteed that they don’t understand how to use Promises.

Compare it to:

initData(key: SomeType, value: AnotherType): Promise<any> {
  return this.storage.set(key, value);
}

Much cleaner.

1 Like

Thanks for the tip!

But will this also return the data which I just stored, like in my suggested example? Because I simply want to achieve the following:

  • check if my Ionic Collection already exists
    • if it does exist, return it
    • if it does not exist, fill it with local dummy data and return it

Edit:
also, is there actually a difference between these two:


  initData1(){
    return new Promise(resolve => {
        resolve(this.storage.set(this.key, 'bla'));
      });
  } 

  initData2(): Promise<any> {
    return this.storage.set(this.key, 'bla');
  }

Yes. A big one. Start here: https://stackoverflow.com/a/23803744

1 Like

Thanks for that!

I still struggle with the initial issue though, as I don’t know how to properly init my Ionic storage and handle the promises. Could someone maybe provide me an example link/application/book which I could use for data initializing and promises? Just the basic approach for “if Ionic Storage exists, return it, if not, create a new one with local data and return it”

Let’s say you want to use a string value if it exists in storage.

const valueIfNothingInStorage: string = 'foo';
initialize(key: string): Promise<string> {
  return this.storage.get(key)
             .then((initialGet: string) => { if (initialGet) { return initialGet }
                                             else { return this.valueIfNothingInStorage }
                                             }
}

This is exactly the thing, when my initialGet does not exist, I do not want to return an already stored value, I want to execute another promise (a promise to first set the data, and then return it).

This means:

const valueIfNothingInStorage: string = 'foo';
initialize(key: string): Promise<string> {
 return this.storage.get(key)
            .then((initialGet: string) => {
                 if (initialGet) {
                      return initialGet }
                 else {
                      //set the storage with init data and return it
                      this.storage.set(key, 'someData');
                      return this.storaget.get(key);
                 }
            }
}

I think the above does not work like that for two reasons:

  • the this.storage.set is an actual promise
  • the return this.storage.get(key) returns a promise while it should return a value.
initialize(key: string, valueIfNothingInStorage: string): Promise<string> {
    return this.storage.get(key)
               .then((initialGet: string) => { if (initialGet) {return initialGet}
                                               else {return this.storage.set(key, valueIfNothingInStorage)
                                                                        .then(_ => valueIfNothingInStorage)}
                                             }
                    );
  }

Chain the Promises. (Which is exactly what you can’t do if you use the antipattern.)

1 Like

Thanks again, I’m starting to think that async/await is a more feasable approach for my case.

The issues I have with conditionals seem to be solved somewhat cleaner, see chapter 3.

async/await is Promises for people who don’t like to type their variables. Slightly cynical of me, but I think that’s a pretty good assessment. At least it was in my case. I tried async/await and went back to Promises because I found myself getting into bad habits. If you want a different opinion, you could look at @Judgewest2000’s code repos. He uses async/await successfully.

1 Like

Thanks once more! But small question: Isn’t that a promise within a promise, and as such undesired as promised should be concatenated via .then and not inside their function?

No. There’s no ambiguity in

promise1.then(promise2).then(promise3).

There is potential ambiguity in

promise1.then(promise2.then(promise3.then(promise4)).then(promise5). <-- never do this

which is not the same as
promise1.then(promise2).then(promise3).then(promise4).then(promise5) <-- do this

The nesting can lead to errors that are essentially impossible to debug.

    .then((initialGet: string) => { 
        if (initialGet) {return initialGet}
          else {
        return this.storage.set(key, valueIfNothingInStorage)
     .then(_ => valueIfNothingInStorage)}               }    );

But in the else part, we would return another promise, which is actually inside the promise?:confused:

This is a good example of when it’s time to either consider using Promise.all or reconsider your initial strategy

This function is not asynchronous.

_ => valueIfNothingInStorage

So i’m not nesting asynchronous functions.
promise1.then(_ => promise2.then(something synchronous))
is fine. From an async perspective, it’s the same as
promise1.then(_ => promise2)
which of course is ok.

Mhh but the this.storage.set is asnyc, and it’s inside the .then, but I guess it’s alright as it’s actually returning the promise?

I don’t understand your question. The then method is not a Promise. The then method returns a Promise. It isn’t wrong to put a Promise inside a then method.

I think you ought to spend some time reading the Promise API.

Sorry I got confused with all the brackets.

I have one last question:

I understand that this is not allowed:

promise1.then(promise2.then(promise3)).then(promise4)

But is the following construct allowed? Meaning, is it ok to nest the thens inside when there is just one “chain”?

promise1.then(promise2.then(promise3.then(promise4))