Having trouble with JavaScript's XMLHttpRequest returning an 'undefined' string on the first run of my script. I need to find a way to ensure that it loads correctly during

Before we dive in, a quick disclaimer: My knowledge of JS is limited, and I typically learn on the go when creating scripts. However, my usual method didn't work this time.

Currently, I'm working on a small JS script that will function as a bookmarklet. The goal is to utilize the Tinder API to display profile pictures of users who have liked them - a feature usually available with Gold membership.

Here's what the current script looks like:

var stringz;
var xhr = new XMLHttpRequest();
var tokez = localStorage.getItem("TinderWeb/APIToken");
var url = "https://api.gotinder.com/v2/fast-match/teasers?locale=en";
xhr.withCredentials = true;
xhr.open("GET", url);
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("content-type", "application/json; charset=utf-8");
xhr.setRequestHeader("x-auth-token", tokez);
xhr.setRequestHeader("tinder-version", "2.35.0");
xhr.setRequestHeader("platform", "web");
xhr.send();
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        stringz = xhr.responseText;
        return stringz;
    }
};
//Converting the xhr response into a JSON string
var jasonstring = JSON.parse(stringz);
//Extracting the URLs
var jasonstrung = jasonstring.data.results.map(x => x.user.photos.map(y => y.url));
//Formatting the URLs into a nicely structured JSON string
var jason = JSON.stringify(jasonstrung, null, 4);
//Checking the output
console.log(jason);

I perform both JSON.parse and JSON.stringify because the data returned by xhr appears as a text string formatted like JSON, but it isn't actually JSON. Therefore, I need to parse it to extract the desired elements and then format them for better readability.

When running this script in the Chrome Dev Console for the first time, it throws an error:

VM5418:1 Uncaught SyntaxError: Unexpected token u in JSON at position 0
    at JSON.parse (<anonymous>)
    at <anonymous>:18:24

The reason for this error could be that 'stringz' is still undefined when JSON.parse attempts to process it.

However, after the script finishes executing, typing console.log(stringz) reveals the expected string. If the script is run twice, the final dataset is printed out as desired:

[
    [
        "https://preview.gotinder.com/5ea6601a4a11120100e84f58/original_65b52a4a-e2b2-4fdb-a9e6-cb16cf4f91c6.jpeg"
    ],
    // More URLs here...
]

Is there a way to make this script work seamlessly in one go (like a bookmarklet)? Unfortunately, using setTimeout doesn't seem to resolve the issue, possibly due to the delay in populating 'stringz' before applying JSON.parse.

Thank you!

Answer №1

The issue arises from the fact that XHR functions asynchronously: it sends a request and receives a response later, while other lines of code continue to execute in the meantime.

To properly transform your JSON string, you need to ensure that the transformation occurs only after the response has been received. This means placing your code within the xhr.onreadystatechange function (I have commented out some parts for demonstration purposes):

var jsonString;
var xhr = new XMLHttpRequest();
var url = "https://jsonplaceholder.typicode.com/posts";

xhr.open("GET", url);

xhr.onreadystatechange = function() {
  if (this.readyState == 4 && this.status == 200) {
    // response handling
    jsonString = this.responseText;

    // initiate JSON transformation upon response arrival
    jsonTransform(jsonString)
    return jsonString;
  }
};

xhr.send();

var synchronous = 'this will be logged before response arrives'

console.log(synchronous)

function jsonTransform(jsonString) {
  var jsonObject = JSON.parse(jsonString);
  const jsonFormatted = JSON.stringify(jsonObject)
  console.log(jsonFormatted);
}

Alternative Approach

Consider using fetch() instead of xhr which simplifies the process by providing a Promise-based syntax:

More information on fetch():

const url = "https://jsonplaceholder.typicode.com/posts";

fetch(url)
  .then(resp => {
    return resp.json()
  })
  .then(json => {
    console.log(json)
  })
  .catch(err => {
    console.log(err)
  })

You can utilize fetch() along with the user-friendly async-await syntax for a more synchronous-feeling code structure:

const url = "https://jsonplaceholder.typicode.com/posts";

async function fetchAPI(url) {
  try {
    const response = await fetch(url)
    const json = await response.json()
    console.log(json)
  } catch (err) {
    console.log(err)
  }
}

fetchAPI(url)

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

Converting JSON data into Dart format

As a newcomer to Flutter, I am seeking guidance on how to display the values of productA and 220 in the console. Below is a JSON file along with a Dart file. { "status": true, "message": "Data returned successfull", &quo ...

What are some ways to create a multi-select event?

Check out my HTML code: <select id="specific_choice14" multiple="multiple"> <option value="Shampoing">Shampoing</option> <option value="Après Shampoing">Après Shampoing</option> <option value="Mask"> ...

Strategies for persisting data in React using local storage to ensure information is retained after page refresh

I am attempting to store searched recipes data in local storage so that when the user refreshes, the data from the last searched recipe will still be available. I have tried saving the recipes data to local storage when a new search request is made and se ...

What is the reason for typescript's lack of a "function" type?

As a newcomer to TypeScript, I'm puzzled as to why I am unable to define an object like this: const obj: { property1: string property2: boolean property3: function } It seems that the only workaround is to use: const obj: { property1: strin ...

SQL: Retain data from convoluted JSON nested column containing escape characters

Looking for a solution to handle JSON data with escaped values in some rows of a table. id obj 1 {"is_from_shopping_bag":true,"products":[{"price":{"amount":"18.00","currency":"USD& ...

Can you explain the JSON serialization that is likely to occur for a 'oneof' protobuf field?

Consider a .proto file with the following structure: syntax = "proto3"; message Foo { ... } message Bar { ... } message Msg { string baz = 1; oneof some_union { Foo foo = 2; Bar bar = 3; } } How should a message of this type be ser ...

Exploring the DOM with jQuery to effectively select multiple elements

I am currently attempting to locate and select all elements with the "findme" class, and then identify the selects that are located nearby. http://jsfiddle.net/R93md/1/ Specifically, I have experimented with $(".findme").parent().prev().first(); Once ...

Encountering a timeout error while using selenium-webdriver and phantomJS

Currently, I am developing my automated test scripts using a combination of selenium-webdriver, phantomJS, and mocha. The script I am working on is written in JavaScript. One requirement is to wait until a specific element becomes completely visible befo ...

Learn how to extract data from the "this" object in JavaScript

Can anyone help me with retrieving the numbers generated by this code snippet? $("#100wattaren") .countdown("2018/01/01", function(event) { $(this).text( event.strftime('%D days %H hours %M minutes %S seconds') ); }); When I con ...

The HTTP request seems to be malfunctioning

When attempting to establish a connection and retrieve data from a PHP file using an AJAX request, it's important to note that the AJAX JS is located on a different website. Here is the script being used: var quer; try { quer = new XMLHttpRequest( ...

As the user types, the DD/MM/YYYY format will automatically be recognized in the

In my React component, I have an input box meant for entering a date of birth. The requirement is to automatically insert a / after each relevant section, like 30/03/2017 I am looking for something similar to this concept, but for date of birth instead of ...

Tips on incorporating the Browserified npm package into an Angular controller

After spending countless hours searching for a solution to integrate Browserify with my project, I found myself struggling to find a tutorial or example that addressed my specific issue. Most resources focused on bundling code rather than demonstrating how ...

Indeed, verifying parent.parent access

Currently, I am utilizing the yup module to validate my form. My objective is to access the parent in order to test the value. Below is my schema: enabled: yup.boolean(), contactDetail: yup.object().shape({ phoneNumber1: yup.string().nullable(), pho ...

Retrieving a result from the reduce function in Angular

Is there a way to use the reduce function in order to return a list of objects? I am currently only able to return a single object with keys as project names and values as hours: { Name1: 9, Name2: 10, Name3: 30, } What changes can I make to my code to ac ...

Ensuring all ajax calls have completed before proceeding using selenium webdriverjs

In my attempt to create a function that can wait for all active (jQuery) ajax calls to complete in a Selenium WebdriverJS test environment, I am faced with challenges. My current approach involves integrating the following components: Implementing WebDri ...

The pure JavaScript function for changing the background color in CSS is not functioning properly

I'm having trouble understanding why the color does not change after clicking. Can anyone explain? function butt(color) { if(document.getElementById("bt").style.backgroundColor=="green"){ document.getElementById("bt").style.backgrou ...

Is jest the ideal tool for testing an Angular Library?

I am currently testing an Angular 9 library using Jest. I have added the necessary dependencies for Jest and Typescript in my local library's package.json as shown below: "devDependencies": { "@types/jest": "^25.1.3", "jest": "^25.1.0", ...

The compression feature in express.js middleware is not functioning correctly

The middlewares set up in my app are as follows: app.use(express.favicon(__dirname + '/static/public/images/favicon.ico')); app.use(express.compress()); app.use(express.json()); app.use(express.urlencoded()); app.use(express.cookieParser('S ...

Updating a class within an AngularJS directive: A step-by-step guide

Is there a way to change the class (inside directive) upon clicking the directive element? The current code I have updates scope.myattr in the console but not reflected in the template or view: <test order="A">Test</test> .directive("test", ...

Using Typescript: invoking static functions within a constructor

This is an illustration of my class containing the relevant methods. class Example { constructor(info) { // calling validateInfo(info) } static validateInfo(info):void { // validation of info } I aim to invoke validateInfo ...