1

For my class, my professor wants us to create a simple node.js web site that reads a text file containing a fully qualified URL and then use the URL to retrieve JSON data from a remote web site and finally save the retrieved JSON to another text file. He wants the URL stored in a global variable.

I've done this, with a hardcoded URL, the one that's supposed to be read from the file. That runs fine and does what I want. My problem is when I try to create a global variable for the URL, it always returns undefined. I know it's a timing issue because the file is not being read asynchronously, but I can't figure out how to fix it.

'use strict';
// load the built-in http module
let http = require('http');
// load the add-in express module
let express = require("express");
// load the add-in xmlhttprequest and create XMLHttpRequest object
let XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
// create app object from express() constructor
let app = express();
// create a port number
let port = process.env.PORT || 3000;
let fs = require('fs');

// create a "get" event handler function
// request and response object references (req, res)
function getJSON() {
    app.get("/", function (reqNode, resNode) {
        var test = new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('GET', 'http://gd2.mlb.com/components/game/mlb/year_2017/month_07/day_08/master_scoreboard.json');
            req.onload = function () {
                if (req.status === 200) {
                    resolve(req.responseText);
                }
                else {
                    reject(req.statusText);
                }
            }
            req.onerror = function () {
                reject("network error");
            };

            req.send();
        });

        test.then(
            function (response) {
                // display in browser on success
                resNode.send('JSON data saved to file.');
            },
            function (error) {
                console.error("Request failed: ", error);
            }
        ),
            test.then(
                function (response) {
                    // write the response to json.txt
                    fs.writeFile('json.txt', response, function (err) {
                        if (err) {
                            console.log(err);
                        }
                        else {
                            console.log('JSON data saved to file.');
                        }
                    });
                },
                function (error) {
                    console.log(error);
                }
            ),
            test.catch(
                error => console.log(error)
            )
    });
}

(function () {
    // create a server object
    let server = http.createServer(app);

    // listen on specified port for get requests
    server.listen(port);
    // call getJSON function
    getJSON();
})();

This code works with a hardcoded URL, but I need a function to store it in a global variable instead.

Dependencies:

"dependencies": {
    "express": "^4.17.1",
    "xmlhttprequest": "^1.8.0"
  }

And, the async/await that I tried. The 'url' variable returns as undefined, so this doesn't work. There are no errors returned, but localhost fails to load.

'use strict';
// load the built-in http module
let http = require('http');
// load the add-in express module
let express = require("express");
// load the add-in xmlhttprequest and create XMLHttpRequest object
let XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
// create app object from express() constructor
let app = express();
// create a port number
let port = process.env.PORT || 3000;
let fs = require('fs');

function readTxt() {
    fs.readFile('urls.txt', { encoding: 'utf-8' }, function (err, data) {
        if (err) {
            console.log('Error reading file: ' + err)
        }
        else {
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                    resolve(data);
                }, 3000)
            });
        }
    });
}


// create a "get" event handler function
// request and response object references (req, res)
async function getJSON() {
    let url = await readTxt();
    console.log(url);
    app.get("/", function (reqNode, resNode) {
        var test = new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('GET', url);
            req.onload = function () {
                if (req.status === 200) {
                    resolve(req.responseText);
                }
                else {
                    reject(req.statusText);
                }
            };

            req.onerror = function () {
                reject("network error");
            };

            req.send();
        });

        test.then(
            function (response) {
                // display in browser on success
                resNode.send('JSON data saved to file.');
            },
            function (error) {
                console.error("Request failed: ", error);
            }
        ),
            test.then(
                function (response) {
                    // write the response to json.txt
                    fs.writeFile('json.txt', response, function (err) {
                        if (err) {
                            console.log(err);
                        }
                        else {
                            console.log('JSON data saved to file.');
                        }
                    });
                },
                function (error) {
                    console.log(error);
                }
            ),
            test.catch(
                error => console.log(error)
            )
    });
}

(async function () {
    // create a server object
    let server = http.createServer(app);

    // listen on specified port for get requests
    server.listen(port);
    // call getJSON function
    getJSON();
})();

I don't believe this is a duplicate. I looked at other questions, I tried using async/await(code above), as answered to this question: https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call. That would likely work, but I'm having trouble getting it set up properly. Thanks in advance!

1 Answers1

1

Your implementation of readTxt function is wrong. Here's the correct implementation of it. I have added comments to describe how to use promises.

function readTxt() {
    // return a promise from the function
    return new Promise(function (resolve, reject) {
      // do the asynchronous operation
      fs.readFile('urls.txt', { encoding: 'utf-8' }, function (err, data) {
        if (err) {
            console.log('Error reading file: ' + err)
            // reject the promise in case of error
            reject(err);
        }
        else {
            setTimeout(function () {
                // resolve the promise after successfull execution of asynchronous code
                resolve(data);
            }, 3000)
        }
    });
}
tbking
  • 8,796
  • 2
  • 20
  • 33
  • Okay, I thought this worked and then realized, I never removed the hardcoded URL. When ran, the URL is read correctly (can tell from console), but now I get an error404 saying " GET http://localhost:3000/ 404 (Not Found) ". – bender_matt Dec 02 '20 at 20:06
  • Looks like the url here is "localhost:3000" or maybe `undefined` so it depends on what you want the url to be. But that would be another question. Questions on stackoverflow are encouraged to be specific to be made easier to answer and provide value to future readers. – tbking Dec 02 '20 at 20:12
  • You should delete this comment then, as it is not the correct answer. I wasn't having problems with that before using the code you provided. – bender_matt Dec 02 '20 at 20:16
  • Well, to answer my own question, just had to define host as '127.0.0.1' and add it to the server.listen parameter. Then it worked. – bender_matt Dec 02 '20 at 20:22
  • Your question was regarding you not being able to read the file asynchronously. And as you mentioned, the code provided in the answer allows reading the file. So how is it not the correct answer? – tbking Dec 02 '20 at 21:09
  • Because the code you provided created another error, technically the right answer, just not a complete one. – bender_matt Dec 03 '20 at 20:21
  • Like I mentioned earlier, SO questions are not meant to be used as one stop solution for all the problems in one's application. The question was regarding reading file asynchronously which was solved. Not being able to send the file data using an API call is out of scope for this question. – tbking Dec 04 '20 at 15:55