Leahcim Leahcim - 2 months ago 25
Node.js Question

Promise will not return

The second part of the Promise below (inside the

then
) is never run. When I run the database query without using the Promise(in a node script that I run
node myscript.js
it returns the data but the console never returns the prompt--the console just hangs and I have to send an interrupt manually. Therefore, when I put it inside a Promise, I think the Promise doesn't know that the database query is complete even though it seems to have returned all the data, therefore the second part of the Promise isn't running ( I think). If that's the problem, how do I write the database query so that it doesn't hang and the Promise can run to completion?

const sqlite = require('/usr/local/lib/node_modules/sqlite3');
const express = require('/usr/local/lib/node_modules/express')
const promise = require('/usr/local/lib/node_modules/promise')

app.get('/', (request, res) => {

var res = [];

function getData() {
return new Promise(function(resolve, reject) {
db.each('SELECT column_a, column_b FROM trips group by column_a', (e, rows) => {

var d = {
a: rows['column_a'],
b: rows['column_b']
}


res.push(d)
});
});

}
getData().then(function(data) {
console.log("never run....", res, data) //never run
});

})

Answer

You need to resolve a promise by calling one of the functions it provides in the callback through its constructor.

const promise = new Promise((resolve, reject) => {
  // you must call resolve() or reject() here
  // otherwise the promise never resolves
});

Otherwise it will always stay in Pending state and never call the callbacks(s) you pass into then.

promise.then(() => {
  // this never gets called if we don't resolve() or reject()
});

Here's a good read on SO Docs.

Additionally, promises allow you to resolve with values so there's usually no need to maintain global variables, you can just pass results through.

Finally, the callback in db.each will be called once for each row, so you would need to handle that by resolving the promise after all rows have been obtained

Here's how you could write your code:

function getData() {
  const data = [];
  return new Promise((resolve, reject) => {
    db.each('SELECT column_a, column_b FROM trips group by column_a', (e, row) => {
      if (e) {
        // error reading a row, reject the Promise immediately
        // optionally you could accumulate errors here in a similar manner to rows
        reject(e); 
        return;
      } 

      // success reading a row, store the row result
      data.push({
        a: row['column_a'],
        b: row['column_b']
      });

    }, (e, rowCount) => { // the complete handler called when the operation is done, see docs: https://github.com/mapbox/node-sqlite3/wiki/API#databaseeachsql-param--callback-complete

      if (e) { 
        // operation finished, there was an error
        reject(e);
        return;
      }

      // operation succeeded, resolve with rows
      resolve(data);
    });
  });
}

app.get('/', (request, res) => {  

  getData().then((data) => {
    // here `data` is an array of row objects
  }, (e) => {
    console.error(`Database error: ${e}`);
  });
});

Side Note

Not sure why you are redeclaring the parameter res as an [], but there's no need for doing var res = []. Since you already have res, you can just say res = [] to point res to a new array. Of course that will overwrite the response object so I assume that you're doing it just for the purposes of this example. If not, you should probably create a new variable.

Comments