简体   繁体   中英

Constructing json response. NodeJS send empty response

I have this code where I want to send a response containing data that I get by requesting the database multiple times. I don't understand why it sends an empty response.

var express = require('express'),
router = express.Router(),
database = require('../database');

router.get('/', function(req, res, next){

  res.writeHead(200, {"Content-Type": "application/json"});

    var ttt;
    var yyy;

    database.getTagType().then(function(data) {
        ttt = "pfff";
    });

    database.getSpecie().then(function(data) {
        yyy = "akkhhh";
    });

  var json = JSON.stringify({
    anObject: ttt, 
    anArray: yyy, 
  });
  res.end(json);

});

module.exports = router;

The problem is in asynchronous nature of Promise.then . You see, JSON.stringify and res.end is called before both of the promises will be resolved. To send the response only when all the data are fetched you have to use Promise.all method.

Here is an example of how it could be done:

router.get('/', function(req, res, next){
    var promises = [];

    promises.push(database.getTagType().then(function(data){
        return "pfff";
    }));

    promises.push(database.getSpecie().then(function(data) {
        return "akkhhh";
    }));

    Promise.all(promises).then(function(values) {
        // Actually, express can stringify response for us. Also it adds
        // correct "Content-Type" header.
        res.json({
            anObject: values[0], 
            anArray: values[1]
        });
    }).catch(function(error) {
        // Something went wrong. Let the error middleware deal with the problem.
        next(error);
        // Instead we can just send an error response, like so:
        // res.status(500).json({error: error.toString()});
    });
});

The database calls are asynchronous. They are returning promises and you are appending the then functions, but the way javascript runs, the function is calling getTagType and getSpecie and then sending the response with res.end() before the promises resolve and the db call is over.

You need to make sure you wait for all the promises to resolve before replying with the response, this essentially calls for nesting the then() functions.

Like so:

router.get('/', function(req, res, next){

  res.writeHead(200, {"Content-Type": "application/json"});

    var tag = database.getTagType();
    // `tag` is now a promise

    var specie = database.getSpecie();
    // `specie` is a promise

    Promise.all([tag, specie]).then(function(values) {
    // this code is executed once both promises have resolved, the response has come back from the database

        var json = JSON.stringify({
            tag: values[0],
            specie: values[1]
        )};

        res.end(json);
    });
});

This function will return immediately, but won't call res.end() until the database calls are completed.

This code gets cleaner once async/await is added to the language :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM