Assigning HTTP responses to a global constant in JavaScript: a step-by-step guide

My latest challenge involves writing client-side javascript that can perform several tasks asynchronously:

  1. Retrieve a text file from the server and send the response to a callback function
  2. Utilize the callback function to parse the response into an array
  3. Store that array in a constant within the global scope

The reason for wanting the response data stored as a constant globally is so I can easily access the array by clicking on the UI without having to repeatedly send the same HTTP request with each click. However, after attempting this for a few days, I'm beginning to think that step 3 may be unachievable. Here is the code snippet I am using to handle sending the http request and processing the response.

"use strict"
// getText sends an HTTP GET request to a specified URL and then
// passes the received text response to a callback function for parsing.
function getText(url, callBack) {
    const rawFile = new XMLHttpRequest()
    rawFile.open("GET", url)
    rawFile.onreadystatechange = ()=>{
            if (rawFile.readyState === 4) {
                   if (rawFile.status === 200 || rawFile.status === 0) {
                           callBack(rawFile.responseText)
                   }
            }
    }
    rawFile.send(null)
}

This next section of the code shows how the text is parsed and assigned to a global variable. While it currently functions correctly, there exists a potential issue:

let wordBank
function textParserCallback(text) {
    wordBank = text
            .split("\r\n")
            .filter((string)=>{
                    if (string.length > 0) return string
            })
}
getText("textFile.txt", textParserCallback)

Any function has the ability to assign values to wordBank, even though wordBank will never require any changes or updates. This is poor design and could lead to bugs. My attempt to address this involved organizing the response handler logic into a revealing module:

function myModule() {
    let wordBank
    function textParserCallback(text) {
        wordBank = text
                .split("\r\n")
                .filter((string)=>{
                        if (string.length > 0) return string
                })
    }
    getText("textFile.txt", textParserCallback)

    // the return statement fires before textParserCallback completes
    return wordBank
}
const globalWordBank = myModule() // undefined

Unfortunately, the return statement executes prematurely.

I have reviewed How do I return the response from an asynchronous call? and other related queries which were resolved by handling the response immediately following the corresponding request. However, as mentioned earlier, I wish to avoid making repeated requests for the same unchanging data whenever I need to update the UI. Additionally, consolidating all my text parsing, DOM listening, and UI updating code into one lengthy callback makes the code difficult to read and debug. The function name "getText" no longer adequately describes its functionality.

If solving this challenge proves impossible, I could always compile the text file into an array and manually insert it into my program. Yet, a part of me fears encountering a similar problem in the future, albeit in a different context where the data size exceeds what can be stored in the program. Hence, I am reaching out here in hopes of finding a solution or verifying whether my current objective is indeed unattainable.

Answer №1

It's essential to wait for the asynchronous request to complete before accessing the data (which you already know). To create a global constant at that point, you can utilize Object.defineProperty:

Object.defineProperty(window, "globalDataBank", {
    value: yourValueHere
});

This constant will be non-writable and non-configurable by default.

However, this approach necessitates holding off any code reliant on it until the request finishes (as part of an initialization sequence), or adding extensive checks to verify its existence:

if (typeof globalDataBank !== "undefined") {
    // Safe to use
} else {
    // Not available yet
}

Implementing such a bootstrap process is not uncommon.

If you prefer skipping the bootstrapping step and allowing other code to run without worrying about data availability, consider using a promise with the global as the promise:

const globalDataBank = new Promise((resolve, reject) => {
    // ...ajax call to fetch the data and resolve or reject
});

or alternatively

const globalDataBank = fetch(/*...*/)
    .then(res => res.text())
    .then(text => text.split("\r\n").filter(string => string.length));

As fetch returns a promise. (Note that I simplified the filter part for you, removing unnecessary conditions and return statements from the callback.)

Advantages of using promises include:

  1. Promises can only be settled once, ensuring consistency and avoiding accidental changes.

  2. All interactions with the data are uniform:

    globalDataBank.then(data => {
        // ...process the data...
    });
    

    By always handling data access through the promise, potential unavailability is managed effectively.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Javascript: Insert a class declaration post loading of the page

When working with native web components, it is necessary to be able to add class definitions after the page has loaded, especially when new types of components are dynamically added to the DOM. EDIT: Here's a brief explanation: In order to define we ...

What could be the reason behind the malfunctioning of my three.js lighting system?

I am struggling to identify the issue with this code snippet (http://jsfiddle.net/resistdesign/s6npL/). Despite referencing the documentation and some examples, the lights don't seem to be functioning as expected. var camera, scene, renderer, geometr ...

Unable to retrieve form values from a $modalInstance

When launching a $modalInstance, the user will need to select an option from dynamically loaded radio inputs and return the chosen value. To open the $modalInstance, this function is used: $scope.openAddFriendGroup = function () { var addFriendGroupMod ...

showing a notification after directing the user to a different webpage

I have been working on integrating HTML with PHP and MySql. Once the user performs certain operations on the database, my system redirects them back to the original page to display the updated table. This part is already completed. Additionally, I want to ...

Guide to incorporating jQuery into an ASP.NET webpage and styling GridView column layouts

After reading numerous articles and questions on formatting gridview rows using jQuery, I decided to give it a try for the first time in an ASP.NET page. Although I managed to write the following code, it seems to have no effect on the gridview. Can anyon ...

Enhance your code with TinyMCE scripting

Currently, I am exploring methods to incorporate an external script into tinymce. The specific script in question is p7EHCscript which is designed to ensure two divs have equal heights based on the larger div's height. I aim to integrate the p7EHC sc ...

React form is experiencing difficulties in updating input field values

I am currently working on dynamically creating input fields in a React project. However, I am facing an issue where the value of the input field does not update after being edited and still displays the previous value. const Content = input => { con ...

Struggling with routing in Node.js while working on REST API development via HTTP

I am facing an issue while trying to complete a MEAN project. The client side is already done, but I am having trouble with the server side when attempting to make a new insertion (which is carried out using HTTP Post). Below, I will demonstrate how I hav ...

What steps can I take to ensure that my server is easily accessible to all users?

I have successfully created a chat server using Node.JS and it is currently running on my LocalHost: 127.0.0.1. However, I want to make this chat server accessible to anyone, not just me. Can you guide me on how to transfer this Chat server to my live serv ...

Problem with inserting data into MongoDB

I am facing a challenge with my Mongo DB structure, which is structured as follows: db.users.find().pretty(); { "_id" : ObjectId("52b42b148ffa91f7ebbe8ebc"), "username" : "test", "password" : "test", "party" : [ "4988", "50 ...

Is processing a page upon UnLoad necessary?

How can I make a PHP file process when the user clicks a link, goes back, or exits a page? This is important for saving user information. If I use jQuery's unload function, how can I trigger the PHP file to load and process? jQuery(window).bind("unlo ...

Concealing two out of three div elements inside a specified id container using jQuery

I have encountered an issue with my structure, which looks like this: http://jsfiddle.net/NEFhF/3/ This is how it should work: When I click on the "id contacts", the "field body" and the "field foot" should be hidden. However, the id and the field head b ...

Tips for submitting an Ajax Form with identical Name attributes?

One part of my form consists of input fields with the same 'Name' values that will be stored as an Array. I am attempting to send these values via AJAX to PHP for updating my database. The challenge I'm facing is figuring out how to send t ...

Need help setting up automatic audio playback on your Android device?

I'm aware that autoplay of audio is not supported on Android devices. However, I recently found a website that successfully autoplays music on an Android device: Can someone explain how this is being achieved? ...

What could be causing my click event to fail to register after sorting a WebGrid with a click?

Running into an issue with my webgrid and search button. It works perfectly if I search first, updating the grid with results. But when I click on a header to sort the grid, the search button stops working. Can't seem to figure out how to solve this d ...

It appears that protractor-flake is programmed to re-run all tests instead of just the ones that have failed

Running tests using the latest version of "[email protected]", encountering failures during testing but the tests are rerunning again. No custom reporter used except Allure reporting. Below is the command used for running: protractor-flake --max-at ...

Something seems off with the way WebGL is rendering shapes

I am encountering an issue with my code that is supposed to display parsed radar data on a Mapbox GL JS map using WebGL. While I have successfully managed to render the general radar shape on the map, the rendering is incorrect. Instead of drawing rectangl ...

Update the Callbacks to trigger every time a user scrolls to a different section while using fullPage.js

I've added a fadeOut effect to certain sections, but I'm having trouble making them reappear after scrolling a second time. My knowledge of JavaScript is limited, so I'm unsure about the correct solution. How can I achieve this? $(documen ...

Testing React components with Jest by mocking unnecessary components

Consider a scenario where we have the Page component defined as: const Page = () => <> <Topbar /> <Drawer /> <Content /> </> When writing an integration test to check interactions within the Drawer and Con ...

Error: Unable to access the 'fromJSON' property of an undefined object

I am attempting to showcase menu data once the ajax call is completed from the specific page: <head> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"> ...