1

I am working with ReactJS and need to create an array to insert data, store it in localStorage, and view it later. I first create the array using this.subscriptionsList = []; I then use this method to retrieve the data.

  fetchNow = async (token, nextToken) => {
      fetch("somewebsite")
        .then(( unformattedData ) => {
          return unformattedData.json();
        })
        .then(( webData) => {
          for(let i = 0; i < webData.length; i++){
            this.subscriptionsList.push("someData");
          }
          if(webData.hasOwnProperty('token')){
            this.fetchNow(token, webData.token);
          }else{
            return;
          }
        })
      }

The function is not complete as it is not the focal point of my question. It works fine and pushes the correct data.

I retrieve the data using this method:

this.fetchNow(response.accessToken, "")
      .then( () => {
        console.log(this.subscriptionsList);
        console.log(JSON.stringify(this.subscriptionsList));
        localStorage.setItem("subscriptionsList", JSON.stringify(this.subscriptionsList));
        //console.log(JSON.parse(localStorage.getItem("subscriptionsList")));
      })

The problem appears when I try to use JSON.stringify. Doing console.log(this.subscriptionsList) prints an array object with all the data inside of it as expected. However when I go to do console.log(JSON.stringify(this.subscriptionsList)) it returns []. Further, when printing the object itself, the property length: 125 but when doing console.log(this.subscriptionsList.length) it returns 0 and when accessing using this.subscriptionsList[0] it returns undefined. I was reading the specifications and it appears as though JSON.stringify should work with a javascript array. Am I missing something react specific or is this just not available with JSON.parse?

https://gyazo.com/72aafe248ef61649a38c06d03fb3d830
https://gyazo.com/c2690b4392774fe4a98254a4d15f3a32
https://gyazo.com/e0793b5ec05da4259e16100f275da414
3
  • Could you please object structure screenshot Commented Jun 9, 2018 at 5:00
  • Not sure, Could you try to store this.subscriptionsList on a local variable and do operation on that. Commented Jun 9, 2018 at 5:01
  • I thought I knew what the issue was until you said logging the length property shows a different value than observing the length property, which is practically impossible. Are you sure of this? Commented Jun 9, 2018 at 5:01

2 Answers 2

1

You need to return the promise chain in fetchNow or you can't chain to it later as you're trying. You'll also have to return the recursive fetchNow called inside fetchNow if you want it to be chained as well:

fetchNow = (token, nextToken) => {
  return fetch("somewebsite")
    .then(( unformattedData ) => {
    return unformattedData.json();
  })
    .then(( webData) => {
    for(let i = 0; i < webData.length; i++){
      this.subscriptionsList.push("someData");
    }
    if(webData.hasOwnProperty('token')){
      return this.fetchNow(token, webData.token);
    }
  })
}

async functions do not magically make asynchronous operations synchronous. They (1) return Promises and (2) allow for the await keyword. But since you're not using await, there's no need for fetchNow to be async, and since the fetch chain is already a Promise, you can return it directly without async.

If you do want to use async and await, it would make the code much flatter and easier to understand:

fetchNow = async (token, nextToken) => {
  const response = await fetch("somewebsite");
  const webData = await response.json();
  for(let i = 0; i < webData.length; i++){
    this.subscriptionsList.push("someData");
  }
  if(webData.hasOwnProperty('token')){
    await this.fetchNow(token, webData.token);
  }
  // async function will automatically return a Promise that resolves when the end is reached
}
Sign up to request clarification or add additional context in comments.

5 Comments

As per @Jimmy comment console.log(this.subscriptionsList) prints an array object with all the data inside of it as expected.So,it's not promise chain issue.
That's due to counter-intuitive console.log implementation - it's logging the live object, not the object as it was when it was logged.
See here: stackoverflow.com/questions/24175017/… (it's not limited to Chrome, many more questions on this issue as well)
I thought of this at first as well but when I go to print the object it is the complete array. Am I still missing something?
@Jimmy console.log unintuitively returns the live object, not the object at the moment it was logged. If you stringify the object beforehand, it'll more accurately represent the object's contents at that moment in time.
1

This is what's happening.... What console.log(JSON.stringify(something)) does is that it serialises the immediate value and prints the serialised reference.(in other words it creates a cloned copy and prints the cloned copy not the original reference it won't wait for hoisted/mutated value). What console.log(something) does is that it refers to the original value and prints the actual result(mutated/hoisted) rather the immediate result. As your method being an asynchronous one you can clearly observe this. You can wait until the asyc call is done then you can push the actual variable to the localStorage. (Note that even local storage stores as serialised values)

Edit 1:-

let onComplete = (subscriptionsList) => {
    console.log(JSON.stringify(subscriptionsList));
    localStorage.setItem("subscriptionsList", JSON.stringify(subscriptionsList));
    //console.log(JSON.parse(localStorage.getItem("subscriptionsList")));
}

fetchNow = async (token, nextToken, onComplete) => {
  fetch("somewebsite")
    .then(( unformattedData ) => {
      return unformattedData.json();
    })
    .then(( webData) => {
      for(let i = 0; i < webData.length; i++){
        this.subscriptionsList.push("someData");
      }
      if(webData.hasOwnProperty('token')){
        this.fetchNow(token, webData.token);
      }else{
        onComplete(this.subscriptionsList);
        return;
      }
    })
}

this.fetchNow(response.accessToken, "", onComplete)
  .then( () => {
    console.log("direct call ", this.subscriptionsList);
})

3 Comments

I don't really understand what you are saying as I am not too familiar with hoisting and such in javascript. From what I understand you are saying that somehow the array becomes empty? This, however, isn't the case. The end result should have all of the data. I have included screenshots in my original post for more details
@Jimmy try the sample code in edit1 haven't tested it though. All you have to do is to wait for the async call to complete.
I see you and CertainPerformance were providing the same point but I believe his answer will help those in the future more as it demonstrates the importance of promise chaining so I accepted his answer. However, your answer does demystify the situation greatly and is also very important.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.