简体   繁体   中英

Modifying global variables inside of POST request

So I'm currently trying to make a simple quiz app for Github's Noops Challenge using their Fizzbot API .

I am storing and assembling the URL of the current question and next question in global variables.

var baseurl = "https://api.noopschallenge.com";
var nextQuestion = "/fizzbot/questions/1";
var url = "";

I have a submit function that sends a POST request to the server and receives the URL of the next question if the answer is correct.

function submit() {
    var answer = document.getElementById("answer").value;

    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {

            var response = JSON.parse(this.responseText);

        this.nextQuestion = response.nextQuestion; fizzbot
        this.url = baseurl + this.nextQuestion;

        console.log("Next Question: " + this.url);  
        }
    };

    xhttp.open("POST", this.url, true);
    xhttp.setRequestHeader("Content-type", "application/json");
    xhttp.send(answer);

}

In addition to my submit button, I have a next button. After submitting a correct answer, the below function still prints https://api.noopschallenge.com/fizzbot/questions/1 to the log.

function next() {
    console.log(this.url);
}

I am pretty sure it has something to do with the asynchronous nature of the POST request, but am not sure what the best way to solve it is. I've posted a more complete snippet for a functioning example of what's happening.

 var question = ""; var baseurl = "https://api.noopschallenge.com"; var nextQuestion = "/fizzbot/questions/1"; var url = ""; function load() { this.url = this.baseurl + this.nextQuestion var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var response = JSON.parse(this.responseText); this.question = response.message; document.getElementById("question").innerHTML = this.question; } }; xhttp.open("GET", this.url, true); xhttp.send(); } function submit() { var answer = document.getElementById("answer").value; var responseObject = {answer: answer} var responseJSON = JSON.stringify(responseObject); var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var response = JSON.parse(this.responseText); this.nextQuestion = response.nextQuestion; this.url = baseurl + this.nextQuestion; document.getElementById("result").innerHTML = response.result; document.getElementById("nextQuestion").innerHTML = response.nextQuestion; console.log("Next Question: " + this.url); } }; xhttp.open("POST", this.url, true); xhttp.setRequestHeader("Content-type", "application/json"); xhttp.send(responseJSON); } function next() { console.log("URL to load: " + this.url); //GET request next question } 
 <!DOCTYPE html> <html> <head> <title>Noops Challenge | FizzBot Bot</title> <script src="Fizzbot.js" type="text/javascript"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> <link rel="stylesheet" href="main.css"> </head> <body onload="load()"> <nav class="navbar navbar-dark bg-dark"> <a class="navbar-brand" href="#"> <img src="https://noopschallenge.com/static/animation/fizzbot/images/img_3.png" width="30" height="30" class="d-inline-block align-top" alt=""> FizzBuzz Quiz </a> <!-- <span class="navbar-brand mb-0 h1">FizzBuzz Quiz</span> --> </nav> <div class="container"> <div class="row" style="margin-top:5%;"> <div class="col-sm-2"></div> <div class="col-sm-8"> <div class="card"> <div class="card-header"> Question <span id="questionNumber">1</span> </div> <div class="card-body"> <h5 id="question" class="card-title">Filler Question</h5><br> Answer: <input type="text" id="answer"> <p class="card-text"><br> Result: <span id="result"></span><br> Next Question: <span id="nextQuestion"></span><br> </p> <button class="btn btn-primary" onclick="submit()" value="">Submit</button> <button class="btn btn-primary" onclick="next()" value="">Next</button> </div> </div> </div> <div class="col-sm-2"></div> </div> </div> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> </body> </html> 

Global variables are stored in the window object. this can refer to window as well as pretty much any other object. Also, not using this might get you a global variable as well.

This will run in the global context:

function load() {
  this.url = this.baseurl + this.nextQuestion

Here this === window , so this.url is built from your global variables.

The context changes here:

  ...
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var response = JSON.parse(this.responseText);
      this.question = response.message;

      document.getElementById("question").innerHTML = this.question;
    }
  };

Here, this refers to the xhttp object. When setting this.question , you're creating a new property on xhttp and using that value to set the <h5 id="question"> text. This works, it just doesn't change your global question variable.

This again uses the global variable:

  xhttp.open("GET", this.url, true);
  ...
}

Something alike happens when using the submit button:

function submit() {
  ...
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var response = JSON.parse(this.responseText);

      this.nextQuestion = response.nextQuestion;
      this.url = baseurl + this.nextQuestion;

Both this.nextQuestion and this.url create properties in this (new!) xhttp object. However, since you didn't put this. in front of baseurl , and there's no local variable with that name, the global variable will be used! So, implicitly you're doing this: xhttp.url = window.baseurl + xhttp.nextQuestion

That's why this will show a nice new full URL:

      console.log("Next Question: " + this.url);
    }
  };

But again, the request itself is still done using the initial URL, since we're leaving the xhttp scope and using the never changed global value here:

  xhttp.open("POST", this.url, true);
  ...
}

... and here:

function next() {
  console.log("URL to load: " + this.url);
  //GET request next question
}

Long story short: use window.url to set the global value inside another scope.

About 'this' on MDN

It seems like it had more to do with actual variable scope than it had to do with the async aspect of the code. Instead of dealing with a bunch of messy this references, I just put most of my globals inside of an object which ended up making more sense anyways.

Using an object with getters and setters I am able to access all of my variables throughout the code.

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