Search code examples
javascriptasynchronousasync-awaitspawn

NODEJS: Results page is displayed before search is finished


I want to query 3 databases and display the results in a results page, but this page is displayed before the query is finished.

How can I display the results page ONLY after the 3 queries are finished?

I have tried promises, callback functions, async/await, and python's spawnSync, but nothing is working.

Any help will be greatly appreciated.

Thanks!

Here is the code:

//Import libraries
const express = require('express');
const path = require('path');
const ejs = require('ejs');
const bodyParser = require('body-parser');
const app = express();


//Load forms
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));

//Load static elements
app.use(express.static(__dirname + '/public'));

//Body Parser Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

app.get('/', function(req,res){
    res.render('search.ejs');
});

app.post('/results', function(req,res){
    var products = req.body.products;
    var countries = req.body.countries;
    price_data = lookup('prices', products, countries);
    description_data = lookup('descriptions', products, countries);
    other_data = lookup('other', products, countries);
    table_data = get_table_data(price_data, description_data, other_data);
    res.render('results.ejs', {'table_data': table_data});


    //THIS IS THE PROBLEM. THIS PAGE IS RENDERED BEFORE THE 3 LOOKUPS ARE FINALIZED SO IT DISPLAYS NO SEARCH RESULTS



});

function lookup(type, products, countries) {
    var spawn = require('child_process').spawn;
    var py = spawn('python', [type + '_lookup.py']);
    var data = [products, countries];
    var python_output_string ='';
    py.stdin.write(JSON.stringify(data));
    py.stdin.end();
    py.stdout.on('data', function(data) {
        python_output_string = python_output_string + data;
    });
    py.stdout.on('end', function() {
        console.log(python_output_string);
        return python_output_string;
    });
}

Solution

  • You could try returning a promise in the lookup method and wait on them, something like ( I omitted non-related code ):

    app.post('/results', async (req, res) => {
      // ...
    
      price_data = await lookup('prices', products, countries);
      description_data = await lookup('descriptions', products, countries);
      other_data = await lookup('other', products, countries);
    
      res.render('results.ejs', { table_data: table_data });
    });
    
    function lookup(type, products, countries) {
      // ...
    
      return new Promise((res, rej) => {
        py.stdout.on('end', function() {
          res(python_output_string);
        });
      });
    }
    

    However, I would prefer a Promise.all approach:

    app.post('/results', async (req, res) => {
      // ...
    
      Promise.all(
        lookup('prices', products, countries),
        lookup('descriptions', products, countries),
        lookup('other', products, countries)
      )
        .then(values => get_table_data(...values)) // [price_data, description_data, other_data]
        .then(table_data => res.render('results.ejs', { table_data }));
    });