Skip to content Skip to sidebar Skip to footer

Promise Will Not Resolve

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

Solution 1:

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

const promise = newPromise((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()
});

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:

functiongetData() {
  const data = [];
  returnnewPromise((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 rowsreject(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-completeif (e) { 
        // operation finished, there was an errorreject(e);
        return;
      }

      // operation succeeded, resolve with rowsresolve(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.

Solution 2:

You've declared a Promise which means you're responsible for calling one of resolve or rejectonce and once only.

Here's a cleaned up example:

app.get('/', (request, res) => {  
  var res = [ ];

  newPromise((resolve, reject) => {
    db.each('SELECT column_a, column_b FROM trips group by column_a', (e, row) => {
      if (e) {
        reject(e);

        return;
      }

      res.push({
          a: row['column_a'],
          b: row['column_b']
      });
    }, (err) => {
      if (err) {
        returnreject(err);
      }

      resolve(res);
    });
  }).then((data) => {
    console.log("Running ", res, data)//never run
  }
});

If your database layer supports promises that usually makes this sort of code a lot less messy since you can simply chain that in there.

Edit: Since the Sqlite3 API is bizarrely non-standard and the each function has two callbacks you need to handle each row with the first, then the completion handler with the second.

If you design an API like this you're doing it wrong. Don't.

Solution 3:

Several points :

  • resolve/reject must be called, otherwise a new Promise() will remain forever "pending".
  • always promisify at the lowest level possible, ie promisify db.each() not getData(). This gives you a testable, reusable utility and more comprehensible application code.
  • db.each() is a challenge to promisify because it has two possible sources of error; one in its iteration callback and one in its complete callback.
  • the sqlite3 documentation does not state what happens if an iteration error occurs but presumably the iteration continues, otherwise the error would simply appear as a completion error?

Here's a couple of ways to promisify :

1. First iteration error or completion error causes promise rejection - iteration errors are not exposed to your application code.

// Promisification
db.eachAsync = function(sql, iterationCallback) {
    returnnewPromise(function(resolve, reject) {
        db.each(sql, (iterationError, row) => {
            if(iterationError) {
                reject(iterationError);
            } else {
                iterationCallback(row);
            }
        }, (completionError, n) => {
            if(completionError) {
                reject(completionError);
            } else {
                resolve(n); // the number of retrieved rows.
            }
        });
    });
};

// Application
app.get('/', (request, response) => {
    functiongetData() {
        var res = [];
        return db.eachAsync('SELECT column_a, column_b FROM trips group by column_a', (row) => {
            res.push({
                a: row['column_a'],
                b: row['column_b']
            });
        }).then(n => res);
    }
    getData().then(results => {
        console.log(results);
    }).catch(error => {
        console.log(error);
    });
});

2. Only a completion error causes promise rejection - iteration errors are exposed to your application code

// Promisification
db.eachAsync = function(sql, iterationCallback) {
    returnnewPromise(function(resolve, reject) {
        db.each(sql, iterationCallback, (completionError, n) => {
            if(completionError) {
                reject(completionError);
            } else {
                resolve(n); // the number of retrieved rows.
            }
        });
    });
};

// Application
app.get('/', (request, response) => {
    functiongetData() {
        var res = [];
        return db.eachAsync('SELECT column_a, column_b FROM trips group by column_a', (iterationError, row) => {
            // You can choose what to do on iterationError.// Here, nulls are injected in place of values from the db,// but you might choose not to push anything.
            res.push({
                a: iterationError ? null : row['column_a'],
                b: iterationError ? null : row['column_b']
            });
        }).then(n => res);
    }
    getData().then(results => {
        console.log(results);
    }).catch(error => {
        console.log(error);
    });
});

(2) is the better approach because exposing iteration errors affords you more flexibility. For example, you could choose to promisify with (2), and emulate (1) in your application :

// Application
app.get('/', (request, response) => {
    functiongetData() {
        var res = [];
        var e = null;
        return db.eachAsync('SELECT column_a, column_b FROM trips group by column_a', (iterationError, row) => {
            if(iterationError && !e) {
                // remember the first iteration error
                e = iterationError; 
            } else {
                // push only on success
                res.push({
                    a: row['column_a'],
                    b: row['column_b']
                });
            }
        }).then(n => {
            if(e) {
                throw e;
            } else {
                return res;
            }
        });
    }
    getData().then(results => {
        console.log(results);
    }).catch(error => {
        console.log(error);
    });
});

With (1), by rejecting on first iteration error rather than exposing iteration errors, the same flexibility is not available. (1) could not fully emulate (2).

Fortunately, the preferred approach (2) is the same as would be obtained with Bluebird's .promisify() method :

Promise.promisify(db.each); 

Post a Comment for "Promise Will Not Resolve"