bender_matt
bender_matt

Reputation: 11

Read File Asynchronously in Node.js

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!

Upvotes: 1

Views: 1602

Answers (1)

tbking
tbking

Reputation: 9116

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)
        }
    });
}

Upvotes: 1

Related Questions