0

I'm not sure how to iterate over an array and send each item a promise that is chained. I'm trying to define DBSchema.executeMigrations down below (details in comments there).

Assume that migrations will grow over time. So, it's just 3 args now, but could be 100 next year. I need this to iterate.

export class DBConnection {
  exec(query): Promise<any> {
    let _this = this;
    return new Promise((resolve) => {
      _this.get().then((db) => {
        db.executeSql(query, {}).then(
          (result) => {
            resolve(result);
          },
          (err) => {
            console.log('Unable to execute query: ' + err.message);
          }
        );
      });
    });
  }
}


export class DBSchema {

  constructor(public db_connection: DBConnection){};

  static migrations = [
    "CREATE TABLE IF NOT EXISTS events(id INTEGER PRIMARY KEY NOT NULL, title STRING)",
    "CREATE TABLE IF NOT EXISTS news(id INTEGER PRIMARY KEY NOT NULL, title STRING)",
    "CREATE TABLE IF NOT EXISTS whatever(id INTEGER PRIMARY KEY NOT NULL, title STRING)"
  ];

  executeMigrations(): any {
    // send each migration to db_connection.exec
    // do it in order
    // return a promise that resolves when all of them are done
  }
}
jsharpe
  • 2,546
  • 3
  • 26
  • 42

1 Answers1

1

An easy to way to let promises run and resolve in a sequential order is to iterate them with Array.prototype.reduce(). That way, you can start the iteration with an immediately resolved promise, and chain the execution order of the promises with the next value which will come from the array.

Your exec() function is implemented with the so called deferred anti-pattern/explicit promise construction pattern which is considered bad practice and should be avoided. There is no need to create a new promise while you instead should return the one from _this.get().

Refactoring your function to return the existing promise instead would make it look like this

 exec(query): Promise<any> {
    let _this = this;
    return _this.get().then((db) => {
        return db.executeSql(query, {})
            .then((res) => { return res }) //not really necessary but to make it more clear
            .catch((err) => {
               console.log('Unable to execute query: ' + err.message);
          }
        );
      });
  }

and by using Array.prototype.reduce() you can let all the queries run in sequence and wait for each other with a function like this

  executeMigrations(): Promise<any> {
      return DBSchema.migrations.reduce((previousValue: Promise<string>, currentValue: string): Promise<any> => {
          return previousValue.then(() => {
              return this.db_connection.exec(currentValue);
          });
      }, Promise.resolve());
  }

The promise returned from executeMigrations() is resolved when all the "inner" promises are resolved, making it easy for you to continue with other stuff with a regular .then(), just like executeMigrations().then(...).

Community
  • 1
  • 1
Daniel B
  • 8,770
  • 5
  • 43
  • 76
  • Cheers dude, thanks for the quick and awesome response... What part of the 'then' line in exec() is unnecessary? – jsharpe Jan 31 '17 at 22:23
  • You're welcome! If you think about it, the value `res` is returned/resolved from `db.executeSql()`, so there is no need to add an extra `.then()` to just re-return the result. I included it in the code just to illustrate that the result is actually returned. Have a look at [this jsfiddle](https://jsfiddle.net/0sL1ffa4/) that demonstrates it! – Daniel B Jan 31 '17 at 22:33