0

I'am trying to gather some data from my Chrome Extension Web SQL database with following code:

 function getHistory(year, month, day, callback){
  getDayIdByYMD(year, month, day, function(dysId){
    var sql = "SELECT DISTINCT HT_URL FROM HISTORIES WHERE HT_DYS_ID = ?;";

    db.transaction(function(tx){
      tx.executeSql(sql, [dysId], function(tx, results){
        var array = [];
        $.each(results.rows, function(index, element){
          var url = element['HT_URL'];
          array.push(url);
        });
        internalGetHistory(array, dysId, callback);
      });
    });
  });
}

function internalGetHistory(array, dysId, callback){
  var returnArray = [];
  $.each(array, function(index, url){
    getSingleHistoryArray(dysId, url, function(hist){
      var tmp = { Url : url, History : hist};
      returnArray.push(tmp);
    });
  });

  callback(returnArray);
}


function getSingleHistoryArray(dysId, url, callback){
  var sql = "SELECT HT_FROM, HT_TIME FROM HISTORIES WHERE HT_DYS_ID = ? AND HT_URL = ?;";
  db.transaction(function(tx){
    tx.executeSql(sql, [dysId, url], function(tx, results){
      var array = [];
      $.each(results.rows, function(index, element){
        var obj = {
          From : element['HT_FROM'],
          Time : element['HT_TIME']
        };
        array.push(obj);
        array.push(2);
      });
      callback(array);
    });
  });
}

function printHt(){
  getHistory(2016,7 - 1,28, function(o){
    console.log(o);
  })
}

But my problem is that function internalGetHistory does not return array. It's always empty. First sql query returns not empet array of urls. The next one - getSingleHistoryArray should return array of time and timespan corresponding to that url. There is no problem with connection to my database or database entries. I think that this question concrens javascript and callback functions. Thanks for your help!

Mr.Nimelo
  • 316
  • 4
  • 17
  • The problem here, is you have nested callbacks within internalGetHistory, so before the getSingleHistoryArray callback executes on each item callback(returnArray) is executed. – httpNick Jul 28 '16 at 18:05
  • 2
    Further reading about Asynchronous: http://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron – Haibara Ai Jul 29 '16 at 00:53

2 Answers2

0

The calls to getSingleHistoryArray are in a function that gets called once on each element of the input array asynchronously. The rest of the calling function keeps executing in the mean time so

var returnArray = [];

is followed immediately by

callback(returnArray);

and the function exits with the initialised but unpopulated array.

Change

function internalGetHistory(array, dysId, callback){
  var returnArray = [];
  $.each(array, function(index, url){
    getSingleHistoryArray(dysId, url, function(hist){
      var tmp = { Url : url, History : hist};
      returnArray.push(tmp);
    });
  });

  callback(returnArray);
}

to

function internalGetHistory(array, dysId, callback){
  var returnArray = [];
  $.each(array, function(index, url){
    getSingleHistoryArray(dysId, url, function(hist){
      var tmp = { Url : url, History : hist};
      returnArray.push(tmp);
      if(returnArray.length == array.length) {
        callback(returnArray);
      }
    });
  });
}
Encaitar
  • 398
  • 5
  • 16
0

The problem currently is getSingleHistoryArray() is an async function and the callback for within each $.each is not being executed by the time internalGetHistory returns. Here is a way to do it with promises: Change:

function getSingleHistoryArray(dysId, url, callback){
  var sql = "SELECT HT_FROM, HT_TIME FROM HISTORIES WHERE HT_DYS_ID = ? AND HT_URL = ?;";
  db.transaction(function(tx){
    tx.executeSql(sql, [dysId, url], function(tx, results){
      var array = [];
      $.each(results.rows, function(index, element){
        var obj = {
          From : element['HT_FROM'],
          Time : element['HT_TIME']
        };
        array.push(obj);
        array.push(2);
      });
      callback(array);
    });
  });
}

to:

function getSingleHistoryArray(dysId, url){
  var sql = "SELECT HT_FROM, HT_TIME FROM HISTORIES WHERE HT_DYS_ID = ? AND HT_URL = ?;";
  db.transaction(function(tx){
    tx.executeSql(sql, [dysId, url], function(tx, results){
      var array = [];
      $.each(results.rows, function(index, element){
        var obj = {
          From : element['HT_FROM'],
          Time : element['HT_TIME']
        };
        array.push(obj);
        array.push(2);
      });
      return new Promise((res, rej) => res(array));
    });
  });
}

and change:

function internalGetHistory(array, dysId, callback){
  var returnArray = [];
  $.each(array, function(index, url){
    getSingleHistoryArray(dysId, url, function(hist){
      var tmp = { Url : url, History : hist};
      returnArray.push(tmp);
    });
  });

  callback(returnArray);
}

to:

function internalGetHistory(array, dysId, callback){
  var returnArray = [];
  var promises = [];
  $.each(array, function(index, url){
      promises.push(getSingleHistoryArray(dysId, url));        
  });

  Promises.all(promises).then((arr) => {
    // do something with returned array.
  });
}
httpNick
  • 2,524
  • 1
  • 22
  • 34
  • It looks good for me but i am not using nodejs. Can you provide any link with sources to Promises.js? – Mr.Nimelo Jul 28 '16 at 18:30
  • Depending if on what ecmascript spec you are using, it is built into the language https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise – httpNick Jul 28 '16 at 18:39