0

The following code does not really do what I want.

function doIt () {
  return new Promise (function (resolve, reject) {
    var promises = [];
    db.transaction(function(tx1){
      tx1.executeSql(function(tx2, rs) {
        for (var i = i; i < N; i++) {
          promises.push(db.transaction(function(tx3){
            ...
          }));
        }
      });
    });
    Promise.all(promises).then(resolve);
  });
}

Now it does not work, because Promise.all() gets executed, before all promises are in the array, at least I think that's correct.

Is there a elegant way to guarantee that all these promises are finished, before doIt ends?

Odin
  • 677
  • 3
  • 15
  • 1
    `before all promises are in the array`. no. the call to `push` and the call to `all` are sequential, as they run in the same function. it looks to me that this should work. – njzk2 Aug 18 '15 at 20:08
  • The db.transaction is not sequential. I did alerts after the loop and after the transactions finish, and the alert from after the loop comes first. – Odin Aug 18 '15 at 20:11
  • and it shouldn't be, otherwise there wouldn't be much point in using a promise. But that's not a problem, since you hold the reference to the promise. Log the size of the array before you call `Promise.all`, I am confident that all the promises are there. – njzk2 Aug 18 '15 at 20:14
  • 1
    Also, you say `it does not work`. may be you would like to expand on that? – njzk2 Aug 18 '15 at 20:15
  • As I said, "does not work" means it continues before everything is finished. I just tried it and the size is 0 while it should be 4. I just tested if the promises are added eventually, and indeed they are added later. – Odin Aug 18 '15 at 20:23
  • 2
    regardless of promises, the code you showed puts `N` items in the `promises` array. I start to suspect that this code is significantly different from the code you actually run. – njzk2 Aug 18 '15 at 20:24
  • Yes, the code in this post is simplified, but it should do the same, since the asynchronity of the inner function (dbtransaction) is the relevant thing, I think. The original contains an outer sqltransaction (select) which then contains a loop which does a post request for every entry of the selects result which then contains another loop that does an insert-query for every entry of the result set of its corresponding post request. – Odin Aug 18 '15 at 20:33
  • It would get more elegant if you'd [avoid the promise constructor antipattern](http://stackoverflow.com/q/23803743/1048572) – Bergi Aug 18 '15 at 20:40
  • I have edited my code, maybe the problem is because of the outer sql query, so I added that. – Odin Aug 18 '15 at 20:50
  • quite different, indeed. It is clear in this code that you cannot wait for promises that are not yet created, unless you make the promises you have wait on the promises they create. – njzk2 Aug 18 '15 at 21:17
  • @Odin: Do you expect `db.transaction` or `tx1.executeSql` to call their callback multiple times? If yes, then that's your problem, if no, then you should promisify those separately and move the `var promises = []` right in front of the loop. – Bergi Aug 18 '15 at 22:02
  • They call their callbacks once, how do I promisify them? I don't get what you mean. Oh... I did a mistake in the last edit of the post, the Promise.all(...) should be on another place, I'll fix it right now. – Odin Aug 18 '15 at 22:07

1 Answers1

0

You can just move where the Promise.all() is located so that it's right AFTER the for loop has finished populating the array:

function doIt () {
  return new Promise (function (resolve, reject) {
    var promises = [];
    db.transaction(function(tx1){
      tx1.executeSql(function(tx2, rs) {
        for (var i = i; i < N; i++) {
          promises.push(db.transaction(function(tx3){
            ...
          }));
        }
        Promise.all(promises).then(resolve);
      });
    });

  });
}

FYI, mixing promises and callbacks can be confusing and makes consistent error handling particularly difficult. Does tx1.executeSql() already return a promise? If so, you can do something cleaner using only promises that are already created by your database functions like this:

function doIt() {
    return db.transaction.then(function(tx1) {
        return tx1.executeSql().then(function(tx2, rs) {
            var promises = [];
            for (var i = i; i < N; i++) {
                promises.push(db.transaction().then(function(tx3) {
                    ...
                }));
            }
            return Promise.all(promises).then(resolve);
        });
    });
}

This returns promises from .then() handlers to auto-chain promises together.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • It seems this is going into the right direction, but db.transaction.then(f) won't execute f at all, since f can't come after the transaction because it is a part of the transaction. – Odin Aug 19 '15 at 06:24
  • @Odin - what database library are you using? – jfriend00 Aug 19 '15 at 22:14