Ionic Storage using async/await

I created a helper file to deal with storage:

import { Storage } from '@ionic/storage';

const ds = new Storage();

export default function () {  
  const initStorage = async function () {
    return await ds.create();
  }  

  const storage = {
    getData: async <T>(key: string): Promise<T> => {
      let newData: any;
console.log(`1 with key "${key}":`, newData);
      await ds.get(key).then( d => {
console.log(`2 with key "${key}":`, d);
        newData = d
      });
console.log(`3 with key "${key}":`, newData);
      return newData;
    },
    saveData: async (key: string, value: any): Promise<void> => {
      await ds.set(key, value);
    },
    clearData: async () => {
      await ds.clear();
    }
  }

  if (ds.driver === null) initStorage();

  return storage;
}

When I call getData, I get the console.log printing as the code runs through. Each time I call getData I would expect it to log 1,2,3 in order as the .get function says it returns a Promise (see below) but it does not.

A snippet from ionic-storeage.js

	    /**
	     * Get the value associated with the given key.
	     * @param key the key to identify this value
	     * @returns Returns a promise with the value of the given key
	     */
	    get(key) {
	        const db = this.assertDb();
	        return db.getItem(key);
	    }

Has anyone else had any issues with async/awaiting with storage? Have I done something wrong that I am just not seeing? Any help on why it is not awaiting would be most appreciated.

Shouldn’t this be

const d = await ds.get(key);
console.log(`2 with key "${key}":`, d);
newData = d

Thanks for the comment @Tommertom I updated and it made no difference.

I believe that both await lines are doing the same thing, one is explicitly dealing with the resolved Promise (using the then) and the other implicitly deals with the resolved value by assigning it to a variable once the promise is returned.

What is still puzzling me is that rather than waiting for the “.get” to return a value it seems to put the call on the stack to be completed at some later point.

Can;t tell either and ChatGPT only complains about not having a try-catch block :slight_smile:

Maybe talk to ChatGPT? :slight_smile:

Hello @peter-app4

I don’t fully understand what are you trying to achieve, but my question is how are you calling the getData() function?
Since it’s a promise, don’t forget the await before it. So it should be something like this:

const data = await storage.getData();

@marioshtika thanks for responding and sorry my question is not clear. I will try and explain it better.

What I am wanting to happen is that every time I call getData it will return the value without putting the call on the stack, which is what I thought the await did, it waited till the promise was either fulfilled or rejected.

What I am seeing is that in the getData the code runs to the await and then continues on else where through the runtime loop and when the promise is eventually fulfilled it will continue back in the await code. This is causing me problems because my code expects the value from storage to have been returned later in the runtime loop but because await code has been put on the stack to be processed later it is failing. [I can refactor my code to take this into consideration but that seems like working around something that should just work].

In a nut shell I thought async/await caused the code to wait until the promise was fulfilled. So when I call the await ds.get(key) to return data from storage it should wait till the value from storage is returned before moving on. Am I misunderstanding what the async/await is supposed to do or how it functions in this instance?

What async/await do is just change the way you write your code. It does not do anything else except handle promises.

For example instead of this:

ds.get(key)
  .then(d => {
    console.log(`2 with key "${key}":`, d);
    newData = d;
  });

You would do this:

const d = await ds.get(key);
console.log(`2 with key "${key}":`, d);
newData = d;

And one other thing if you use await you need to add async to your function which automatically converts your function to a Promise.

So what you are doing right now is getting the promise from storage (ds) and creating a new promise and returning the new one. I really can not think of why you would need to do that.

So you can use the storage’s promise like this

  const storage = {
    getData: (key: string): Promise<T> => {
      return ds.get(key);
    },
    saveData: (key: string, value: any): Promise<void> => {
      return ds.set(key, value);
    },
    clearData: () => {
      return ds.clear();
    }
  }

One more thing, the initStorage() function is a promise, so you might need to wait for that too (if that is what you are trying to achieve).

if (ds.driver === null) await initStorage();

Also, keep in mind that if you use both return and await like this return await ds.create();
you are returning the value not the promise.

If you want to return the promise you can do it like this

return ds.create();

@marioshtika thanks for the response. You have sent me on a journey and I hope I have learnt a bunch about Promises as well as having to re-learn stuff I thought I knew.

Consider the below example. The main thing I was under the impression of was running thisDoesNotWork() would actually work and wait for any sub-processes to run. What I understand now is that I need to have the async keyword at the top level for the await to work (ie run thisWorks(), and further you need to continue with async/awaits to be in all the calls and sub-calls that are made where we need to await a response.

async function doStuff(x) {
    try {
        const res = await runProcesses(x);
        console.log("The result was:" + x);
    } catch (err) {
        console.log("Error:" + err);
   }
}

function thisDoesNotWork() {
    console.log("Start does not work");
    doStuff(3);
    doStuff(10);
    console.log("End does not work");
}

async function thisWorks() {
    console.log("Start does not work");
    await doStuff(3);
    await doStuff(10);
    console.log("End does not work");
}

The other thing I found (as touched on in a previous post) is that while you can combine await with .then it is better not to do that. I found that the suggestion of using, try … catch blocks and assigning the await call to variable laid out nicer and was easier to follow.

Anyone have anything else to add? I hope that I have not missed an important concept or anything else.

This looks perfect now :+1:
And I agree with everything you said.
Well done and keep learning or even send others to learning journies :smiley: by asking questions.