0

I'm curerntly playing with Typescript and ionic and also new to async-await on javascript. I'm trying to get a good undestanding of promises but I can't figure out how to wrap methods which call multiple promises to return a promise. I'll try to elaborate better:

Basically, I have an object which can create a SQLite database, and when the create() method is invoked, it would return a Promise with the actual database object once it has been created.

Then, when the promise resolves and the DB object is returned, I need to use it to execute some statements in a transaction to create all the tables, and a new promise is returned when I invoke the execution of all the statements in the transaction.

Then when the transaction is finished and everything went good, I need to assign the database object to a class property and set a flag indicating database is created and ready to go.

So I thought it would be a good idea to wrap this database initialization stuff in a method called createDatabase() or something like that, which returns a Promise<SQLiteObject>, where SQLiteObject represents the database. This method would be called on initialization and should return the SQLiteObject representing the database once everything went OK, or throw an error which I would log in the .catch() method of the Promise.

I have a basic understanding of promises and how to use then() and catch() methods but I'm a bit confused when I have to create the database, then do something else when the promise resolves, and when everything is done, return a Promise containing the DB object, which is an instance of the class SQLiteObject.


CODE
Below is my current Typescript code. It's not valid typescript as I don't know how to return the Promise<SQLiteObject> from the async function.

async createDatabase(): Promise<SQLiteObject> {

        this.sqlite.create({
            name: this.dbName,
            location: this.dbLocation
        }).then( (db: SQLiteObject) => {
            // Insert all tables.
            let createTableParam: string = `CREATE TABLE IF NOT EXISTS param (name PRIMARY KEY NOT NULL, value TEXT)`;
            let createTableNews: string = `CREATE TABLE IF NOT EXISTS news (id PRIMARY KEY NOT NULL,title TEXT,
                content TEXT, image TEXT, date DATE)`;

            db.transaction(tx => {
                    tx.executeSql(createTableParam);
                    tx.executeSql(createTableNews);
                    // Add here more tables to create if needed
                }
            )
                .then( () => {
                    console.log('Tables were created');
                    this.isActive = true;
                })
                .catch(error => {
                    console.log(`Error creating tables - ${error}`);
                });
        }).catch(
            error => console.log(`Error at SQLite initialization - ${error}`)
        );
    }

RESEARCH SO FAR

JorgeGRC
  • 1,032
  • 2
  • 18
  • 37

2 Answers2

2

You used async, so that means you can use await inside the function any time you have a Promise and write the code almost as though it were synchronous.

async createDatabase(): Promise<SQLiteObject> {
    let db: SQLiteObject;
    try {
      db = await this.sqlite.create({
        name: this.dbName,
        location: this.dbLocation
      });
    } catch(error) {
        console.log(`Error at SQLite initialization - ${error}`);
        return;
    );
    // Insert all tables.
    let createTableParam: string = `CREATE TABLE IF NOT EXISTS param (name PRIMARY KEY NOT NULL, value TEXT)`;
    let createTableNews: string = `CREATE TABLE IF NOT EXISTS news (id PRIMARY KEY NOT NULL,title TEXT,
        content TEXT, image TEXT, date DATE)`;

     try {
       await db.transaction(tx => {
                tx.executeSql(createTableParam);
                tx.executeSql(createTableNews);
                // Add here more tables to create if needed
            }
          );

       console.log('Tables were created');
       this.isActive = true;
       return db;
     catch(error) {
         console.log(`Error creating tables - ${error}`);
     });
}

Without the await you need to be sure to return that initial promise.

return this.sqlite.create({...

and then again further down you can return the db object:

       this.isActive = true;
       return db;

Also you should avoid nesting the .then() handlers: when you get another promise just return it from the first handler and chain another .then on the end:

createDatabase(): Promise<SQLiteObject> {
    let database: SQLiteObject = null;
    return this.sqlite.create({
        name: this.dbName,
        location: this.dbLocation
    })
    .catch(error => console.log(`Error at SQLite initialization - ${error}`))
    .then( (db: SQLiteObject) => {
        // Insert all tables.
        let createTableParam: string = `CREATE TABLE IF NOT EXISTS param (name PRIMARY KEY NOT NULL, value TEXT)`;
        let createTableNews: string = `CREATE TABLE IF NOT EXISTS news (id PRIMARY KEY NOT NULL,title TEXT,
            content TEXT, image TEXT, date DATE)`;
        database = db;
        return db.transaction(tx => {
                tx.executeSql(createTableParam);
                tx.executeSql(createTableNews);
                // Add here more tables to create if needed
            }
        );
    })
    .then( () => {
        console.log('Tables were created');
        this.isActive = true;
        return database;
    })
    .catch(error => console.log(`Error creating tables - ${error}`));
Duncan
  • 92,073
  • 11
  • 122
  • 156
  • Thanks for your time Ducan, is it a bad idea to use async yet? I read that on Typescript 2.1 it's supported and it will transpile to ES5 so I guess that there isn't any problem... Speaking about async, would it be a mess if I didn't use an async function and had to rely on managing `Promises` directly? – JorgeGRC Jun 28 '17 at 11:46
  • I presume you meant `let db; try {` not `try { const db` as the second will be an error when you use db later. – Gerrit0 Jun 28 '17 at 12:01
  • Good catch, I was refactoring on the fly there and added the `try{}` after I had already done the code inside it. – Duncan Jun 28 '17 at 12:54
  • @JorgeGRC I added how the code might look without async. The transpilation to ES5 works for async but the code it produces can be quite verbose, so it's really just a choice to make whether you want verbose and possibly slower code executing in return for cleaner code at compile time. – Duncan Jun 28 '17 at 13:04
  • Oh I see now, it's much clearer not going into the kind of "promise hell", thanks for this detail and guidelines, appreciate it so much – JorgeGRC Jun 28 '17 at 13:44
2

It appears you're not resolving the promise.

A promise must be resolved or rejected so a async function is capable of responding with a value.

From TypeScript Deep Dive:

const promise = new Promise((resolve, reject) => {
    resolve(123);
});
promise.then((res) => {
    console.log('I get called:', res === 123); // I get called: true
});
promise.catch((err) => {
    // This is never called
});

So in my opinion you should create a promise, and when the DB is created and everything resolve it, if there's a problem with the DB creation, reject it.

Remember you can chain promises, so you could chain them as you create your database.

From TypeScript Deep Dive:

The chain-ability of promises is the heart of the benefit that promises provide. Once you have a promise, from that point on, you use the then function to create a chain of promises.

Check this URL for more info about promises.

halfer
  • 19,824
  • 17
  • 99
  • 186
Henry Ollarves
  • 509
  • 7
  • 18
  • Thanks for your time Henry, this answer is indeed also helpful but I'm accepting Duncan's one as it dives deep into detail. I do understand now what you wanted to point out creating a new promise and resolving when the database was created. Thanks – JorgeGRC Jun 28 '17 at 13:46
  • 1
    Just wanted to help :). I'm glad is a bit more clear to you now. Have a good day! – Henry Ollarves Jun 28 '17 at 13:47