Promises in promises

So i’m having a real hard time understanding how synchronous functions work in ionic2/angular.
Here is what i am trying to do. I am using the native sqlite library. When my function to request data from the database is called it has to be certain to open the database and that the 2 tables are created. I’ve created 1 function to do that

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite';

import 'rxjs/add/operator/map';

@Injectable()
export class ScanDatabaseProvider {
	public database: SQLiteObject;

	constructor(public http: Http, public sqlite: SQLite) {
		this.openDatabase();
	}

	openDatabase(){
		this.sqlite.create({
			name: 'data.db',
			location: 'default'
		})
			.then((db: SQLiteObject) => {
		
				var sql = "CREATE TABLE  IF NOT EXISTS `leads` ( `local_lead_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remote_lead_id` INTEGER, `uploaded` INTEGER DEFAULT 0, `name` TEXT, `email` TEXT, `age` INTEGER, `phone_number` TEXT, `address` TEXT )";
				db.executeSql(sql, {})
				.then(() => console.log('leads table created'))
				.catch(e => console.log(e));
	
			
				sql = "CREATE TABLE IF NOT EXISTS `scans` (`scan_id`	INTEGER PRIMARY KEY AUTOINCREMENT,`lead_id`	INTEGER,`measurement`	INTEGER,`scan_date`	TEXT,`scanner_id`	INTEGER	);";			
				db.executeSql(sql, {})
					.then(() => console.log('scans table created'))
					.catch(e => console.log(e));
		
				this.database = db;
			})
		}

a second function also exists that calls the first before selecting the data out of the database

	getHistory(lead_id){
		this.openDatabase(); 
			this.database.executeSql("SELECT * FROM scans", []).then((data) => {
				console.log(JSON.stringify(data.rows));
				return data;
			}, (error) => {
					console.log("ERROR: " + JSON.stringify(error));
			})
	}

The problem is that the opening of the openDatabase() function may not finish before getHistory() is called and in the process the database isn’t instantiated. So reading online introduces me to the idea of promises. So i figured i’d have a promise resolved in the openDatabase() function and then use “then” within my getHistory() function. My problem appears to be that openDatabase() can’t return a resolve since it has to wait for the sqlite call to finish. In other words a promise within a promise. here is the code i’ve tried which bombs because of one or two reasons.

	openDatabase(): Promise<void>{
		this.sqlite.create({
			name: 'data.db',
			location: 'default'
		})
			.then((db: SQLiteObject) => {
		
				var sql = "CREATE TABLE  IF NOT EXISTS `leads` ( `local_lead_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remote_lead_id` INTEGER, `uploaded` INTEGER DEFAULT 0, `name` TEXT, `email` TEXT, `age` INTEGER, `phone_number` TEXT, `address` TEXT )";
				db.executeSql(sql, {})
				.then(() => console.log('leads table created'))
				.catch(e => console.log(e));
	
			
				sql = "CREATE TABLE IF NOT EXISTS `scans` (`scan_id`	INTEGER PRIMARY KEY AUTOINCREMENT,`lead_id`	INTEGER,`measurement`	INTEGER,`scan_date`	TEXT,`scanner_id`	INTEGER	);";			
				db.executeSql(sql, {})
					.then(() => console.log('scans table created'))
					.catch(e => console.log(e));
		
				this.database = db;
				return Promise.resolve();
			})
		}

	getHistory(lead_id){
		this.openDatabase().then(() => 
			this.database.executeSql("SELECT * FROM scans", []).then((data) => {
				console.log(JSON.stringify(data.rows));
				return data;
			}, (error) => {
					console.log("ERROR: " + JSON.stringify(error));
			})
		);
	}

in order for this to work i have to return a resolve from with the main openDatabase() function and not in it’s child “then”. This obviously means it isn’t waiting for the child to finish. I know i’m probably explaining this poorly since i don’t really understand what is going on completely. Help me please. any explanations i find online all seem to go at this in different approaches so i’m really confused.

I should add that the error i get with the latest code in app is "Uncaught (in promise): TypeError: undefined not an object(evaluating ‘_this.database.executeSql’)

Which i gather is because the database object hasn’t been initialized yet.

Here is my taxonomy of animals that deal with asynchronous code. As you’ve realized, openDatabase() is the third type and must return a Promise as you have declared it to. The crucial next step is to instinctively write return as the very first word of it, which will guide you naturally to write the rest. getHistory() is also going to be of type three, so its return value also needs to be changed to a Promise, and it must also again begin with return.

1 Like

F*$#@$%@ Crap… I can’t believe it was that simple, i spent soooooo much time trying to get that work when all i really needed to do is return the first object. If i understand it returns a promise that is then chained .then().then() after it is returned. I really hate myself now. Thank you so much, you are a life saver. Now i just have to figure out why my select statement just returns an object with the number of rows found and none of the actual selected data. Thanks again. The ionic documentation on the nativesql library sucks.
-Dylan

Are you basing that off of your console output? (I.e. your console.log line)

If so, to actually get the data you’ll need to use the item function, e.g.
let item = data.item(desiredRowIndex);

Since item is a function it naturally won’t be included in JSON.stringify output.

Exactly, and after shooting myself in the foot over and over again when I was learning promises with q, I eventually settled on that "always begin with return" rule. It’s not always easy to do, especially when you’re learning the RxJS transformation operators, but it puts you in the right frame of mind and won’t let you fall into the pit like you were in where you are trying to return a value from inside a then block or attempt to return a synchronous value out of an asynchronous event (which is impossible by definition, but it’s seductive and everybody tries to do it at least once, including me).

Well it looks like i had to use the “data.rows.item()” function.
console.log in the terminal just isn’t good enough i guess. Using the console in safari helped me see the actual object. Where the hell is the documentation on the sqlite module that explains the object returns, the documentation on the ionic site just lists the primary functions and not their returns, unless i’m blind and can’t see what right in front of me.

Thanks for that extra off-topic help. I think i’m good for a while.

Whoops, my bad. I was going off of memory.

I think for fuller documentation you’ll have to click on the Cordova plugin link, which will take you to the respective plugins GitHub page.

It’s worse than you think, ha. There’s a recognized standard for how Promises operate, called Promise/A+. TypeScript’s Promise and PromiseLike assume that objects conform to this. However, many Cordova plugins that return a “Promise” don’t return something that is compliant with standards. I don’t know about the sqlite plugin specifically, but basically when working with Cordova plugins, assume that the Promise is held together with bubble gum and paperclips. For example, it’s good practice to send only one request for a Promise at a time, instead of sending a bunch and resolving them all at once with Promise.all.

So there’s two levels of debugging. First, make sure your Promise logic is correct on its face. Then make sure the plugin and you are communicating consistent with documentation, because the plugin might not be.

Theoretically, this sort of thing is precisely what ionic-native is intended to smooth over.

Wow, didn’t even notice the cordova GitHub link, that is way better, i wish i had noticed that earlier.
Thanks.

Now my problem is that they are run out of order. see below

	getHistory(lead_id): Promise<void>{
		return this.openDatabase().then(() => {
			this.database.executeSql("SELECT * FROM scans", []).then(data => {
				console.log(data.rows.item(0)); //returns first fow in table - 3rd  return
				console.log("HERE"); // returns proper string - 4th  return
				Promise.resolve();
				return data;
				// return data.rows;
			}, (error) => {
				console.log("ERROR: " + JSON.stringify(error));
			});

		});
	}

now i call this function that makes a call to the above function:

	this.history = this.scandatabase.getHistory(this.navParams.get("lead_id")).then(data => {
        console.log("returned data");//returns proper string - 1st return
        console.log(data);// returns undefined - 2nd return
      }) ;
        console.log(this.history);
  1. the second function is being returned before the first function finishes. (i thought the whole point of that is to run one wait for the return “then” run the next.
  2. I cant seem to figure out how to pass the results of the first function to the “then” of the second.

desired workflow:

function 2 calls function1 waits for response before trying to access passed back from function 1

Actual workflow:

function 2 calls function1 it evaluates function 1 then, function 2 “then” finishes and processes its own “then” … no data is exchanged.


The whole point of this is to have a database provider that handles all of the queries and the users make calls to the database provide and handle the return. async is very frustrating(although very nice for somethings) coming from other synchronous languages.
I would expect then would be chained in order

what am i misunderstanding.

It looks like you’re not returning the result of your this.database.executeSql call.

So it should look something like this:

getHistory(lead_id): Promise<void>{
    return this.openDatabase().then(() => {
        return this.database.executeSql("SELECT * FROM scans", []); //Notice the return here
    }).then(data => {
        console.log(data.rows.item(0)); //returns first fow in table - 3rd  return
        console.log("HERE"); // returns proper string - 4th  return
        return data;
        // return data.rows;
    }, (error) => {
        console.log("ERROR: " + JSON.stringify(error));
    });
}

I also removed the Promise.Resolve(); call as it’s unnecessary, and I also reduced the nesting of .then chains to help reduce the overall complexity of the method.

1 Like

I think i got it. i needed to return the

return this.database.executeSql

in the first function. now it looks like this

type or paste code here
getHistory(lead_id): Promise<void>{
	return this.openDatabase().then(() => {
		return this.database.executeSql("SELECT * FROM scans", []).then((data) => {
			console.log(data.rows.item(0));
			console.log("HERE");
			// let item = data.item(0);
			// console.log("Selected:" + JSON.stringify(item));
			return data;
			// return data.rows;
		}, (error) => {
			console.log("ERROR: " + JSON.stringify(error));
			return false;
		});

	})

There is a relatively subtle inconsistency requiring that explicit second return. If the then block is on a single line, there is an implicit return. So the first two of these act identically, but the third one doesn’t do what you would probably expect it to:

return frobulate().then((foo) => cromulate(foo));
return frobulate().then((foo) => {
  return cromulate(foo);
});
return frobulate().then((foo) => {
  cromulate(foo);
});

In that third case, the return value of cromulate() gets silently swallowed.