1

I am trying to do a nested query with MySql, put the result inside a variable and send over http, but the program always run console.log("test 2:"+rpsData); before the query finish. I already tried this, but still getting the same problem.

const express = require('express')  
const app = express()

const mysql = require('mysql');
const Connection = require('mysql/lib/Connection');
const Promise = require('bluebird');

Promise.promisifyAll([
    Connection
]);

const connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : 'root123',
  database : 'mygallery'
});
app.get('/posts', function(request, response) {
    var rpsData;
  connection.connectAsync()
  .then(function() {
    connection.query('SELECT * FROM post WHERE approved = 1', function(err, rows, fields) {
      if (err) throw err;
      rpsData = rows;
      for (var i in rows) {
        connection.query('SELECT * FROM image WHERE postCode = ?', [rpsData[i].postCode], function(err, rows, fields) {
          if (err) throw err;
          rpsData[i].image = rows;
          console.log("test 1:"+rpsData);
        });
      }
    });
  })
  .then(function() {
    response.send(rpsData);
    console.log("test 2:"+rpsData);
  })
  .catch(function(error) {
    console.error('Connection error.', error);
  });
});
Community
  • 1
  • 1
shadow00
  • 245
  • 3
  • 14

1 Answers1

1

What's happening here is you're not tying all of the pieces of async code to the promise chain. Once we convert it to do so this should work.

First lets wrap calls to connection.query to return a promise. We then have to return that generated promise to attach it to the outer promises chain.

If you don't return a promise, it won't know that it has to wait for your code to finish executing and will move forward with the next .then() statement on the outside promise (from connection.connectAsync);

You need to apply the same treatment to the inner query.

Sample code:

app.get('/posts', function(request, response) {
  connection.connectAsync()
  .then(function() {
    return new Promise(function(resolve, reject) {
      connection.query('SELECT * FROM post WHERE approved = 1', function(err, rows, fields) {
        if (err) reject(err);
        resolve(rows.reduce(function(accumulator, current) {
          return accumulator.then(function(rpsData){
            return new Promise(function(resolve, reject){
              connection.query('SELECT * FROM image WHERE postCode = ?', [current.postCode], function(err, rows, fields) {
                if (err) reject(err);
                current.image = rows;
                console.log("test 1:"+rpsData);
                resolve(rpsData);
              });
            });
          });
        }, Promise.resolve(rows)));
      });
    });
  })
  .then(function(rpsData) {
    response.send(rpsData);
    console.log("test 2:"+rpsData);
  })
  .catch(function(error) {
    console.error('Connection error.', error);
  });
});

I'm queueing the internal promises using the technique I describe here

Community
  • 1
  • 1
Paarth
  • 9,687
  • 4
  • 27
  • 36
  • Thanks for your answer! Now the `response.send(rpsData)` is sending the message over HTTP, but without the data from the second query. Should I put another `return new Promise(function(resolve,reject) { ... })` in the second query? – shadow00 May 10 '16 at 18:50
  • @shadow00 whoops! I made a stupid mistake. Give me just a minute to fix it. – Paarth May 10 '16 at 18:51
  • @shadow00 try that – Paarth May 10 '16 at 18:54
  • `resolve(rpsData)` is not waiting the second query finish. – shadow00 May 10 '16 at 19:05
  • Is there a way to only call resolve after every query is finished? – shadow00 May 10 '16 at 19:11
  • @shadow00, does this do it? – Paarth May 10 '16 at 19:22
  • No, `response.send(rpsData)` is sending only the first query result without the images from the second query. – shadow00 May 10 '16 at 19:39
  • In your last change, Test 2 still going first, but undefined. – shadow00 May 10 '16 at 19:44
  • @shadow00 I dropped a return. Updated. – Paarth May 10 '16 at 19:48
  • Error in the second query. `Connection error. [TypeError: Cannot read property 'postCode' of undefined]`. Changed `[current[i].postCode]` to `[current[0].postCode]` just to test without `for`. – shadow00 May 10 '16 at 19:53
  • @shadow00 this is hard without having types or being able to test :). Updated again. Apologies for the long chain. Want to pick up in chat if this doesn't work? – Paarth May 10 '16 at 19:56
  • IT WORKED!!! Now I need time to figure out what you did. HAHA. Thanks for your help! – shadow00 May 10 '16 at 20:04
  • LOL my pleasure @shadow00. This is really two techniques, one is how to promisify a function, the other is how to queue multiple async calls together (which I linked to at the bottom). If you need help please feel free to reach out and we can talk on stack overflow chat. Sorry it took so long! – Paarth May 10 '16 at 20:07
  • How we can talk in chat? I have just 2 hours of experience in stack overflow. – shadow00 May 10 '16 at 20:13
  • Huh. I think you can't because your account is too new. If you have clarification questions feel free to post here or post a new question (tag me if you like) – Paarth May 10 '16 at 20:22
  • Is it possible to do something like `query1.then(query2.then(query3().then(send.result())))` or `query1.then([query2, query3, query4].then(send.result()))` – shadow00 May 10 '16 at 20:51
  • Yes. That's actually what my reduce is doing, this is how you can do it if you don't know how many query items you have. – Paarth May 10 '16 at 20:55