0

Good Morning Everyone,

I'm having some trouble wrapping my head around some asynchronous activities.

What I'm looking to do is grab some data, pass the returned value to a variable, display that information (and do some other functions based on what comes out). This process should be expandable to multiple datasets, such as hourly forecasts, weekly forecasts, fire-hazard info etc. and will be running on an interval.

I've gone through MDN's Promise page along with numerous searching on here for an answer, but it doesn't seem to click.

What I have so far:


let weeklyData;
let pull;

async function pullData(url){
  fetch(url).then(res=>res.json()).then((val)=>{pull=val});
}

function buildDataSets(){
  for(let x = 0; x < pull.properties.periods.length; x++){
    weeklyDataSets[x] = pull.properties.periods[x];
  }

}

When called by hand in the console, these resolve correctly. Pull comes out as an object, and buildDataSets() can run. I imagine that's because there's enough time for pullData(url) to resolve.

I need to process the response from pullData(url) so I need to assign its return value to an object.

What I can tell, I should be using something in the ballpark of

Promise.resolve(pullData(url))
    .then(()=> buildDataSets());

This returns a pending status, and pull remains undefined when buildDataSets() goes off. I know that I need to pull the data, wait until fulfilled, then assign the value into pull if successful. Then after pull has been assigned call the buildDataSets() function. Adding a .then() that calls buildDataSets() doesn't seem to work either resulting in pull to be undefined when it attempts to access values.

Would someone please help explain what I'm missing? I feel like I'm staring at the answer, but can't see it.

Chris Gergler
  • 168
  • 11
  • `pullData` doesn't return the promise from `fetch`. – VLAZ Dec 08 '21 at 16:29
  • 1
    `pull=val` consider not doing this. Put `buildDataSets` inside the `then` of `pullData` – evolutionxbox Dec 08 '21 at 16:29
  • It should be explained in this question: [Why is my variable unaltered after I modify it inside of a function?](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – evolutionxbox Dec 08 '21 at 16:30
  • @derpirscher it's a promise, it already carries the data. Ferrying the data through a shared (global) variable is a crutch. It's not needed and it's error-prone – VLAZ Dec 08 '21 at 16:31
  • @VLAZ thats correct. But if one says "don't do this" some sort of reasoning would be nice ... – derpirscher Dec 08 '21 at 16:31
  • @evolutionxbox thats correct. But if one says "don't do this" some sort of reasoning would be nice ... – derpirscher Dec 08 '21 at 16:32
  • @derpirscher I added my reasoning in the second comment. I couldn't find it originally. – evolutionxbox Dec 08 '21 at 16:33
  • Thank you guys for helping and this discussion! It was helpful! – Chris Gergler Dec 08 '21 at 16:55

1 Answers1

0

You should be returning a value from pullData so that others can await for it (or access it in the then handler). See Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference

You typically don't set a non-local variable from an asynchronous callback exactly because of what you have mentioned. THe

You probably want buildDataSets to also return a Promise.

async function pullData(url){
  return fetch(url).then(res=>res.json()));
}

async function buildDataSets(){
  const pull = await pullData('/url');
  const weeklyDataSets = {};
  for(let x = 0; x < pull.properties.periods.length; x++){
    weeklyDataSets[x] = pull.properties.periods[x];
  }
  return weeklyDataSets;
}

async function myMain () {
    const data = await buildDataSets();
    console.log({data});
}

Here's the same code without using awaits, just Promise chains

function pullData(url){
  return fetch(url).then(res=>res.json()));
}

function buildDataSets(){
  return pullData('/url')
    .then((pull) => {
      const weeklyDataSets = {};
      for(let x = 0; x < pull.properties.periods.length; x++){
        weeklyDataSets[x] = pull.properties.periods[x];
      }
      return weeklyDataSets;
    });
}

function myMain() {
    return buildDataSets().then((sets) => {
        console.log({sets});
    });
}
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • Thank you! (my comment got locked) So I just want to make sure I'm understanding this correctly. We get the promise with a json value out of pullData. We assign it to pull with an await so that it resolves in the buildDataSets queue(?) then we can use it to pass its value into weeklyDataSets? Otherwise we're pulling the data and then immediately trying to assign. Would that be accurate? – Chris Gergler Dec 08 '21 at 17:02
  • @ChrisGergler I don't understand the question. The point is that you should (almost) never assign a non-local variable in a callback. Returning a promise from a function and using that instead of assigning variables is what you need to do. I used `await` but could have used `Promise.then` chains. I'll the chains example to the answer – Ruan Mendes Dec 08 '21 at 17:17
  • Ahh, I'm seeing something with it. We have pull.properties (etc) failing to be recognized because those are all functions for the object that I made from pull previously. However now I'm back to square one where I need to access the values of the promise that's passed through but have no access to them. So within my promise, I have the status and the value. I don't know how to access the value without assigning it. – Chris Gergler Dec 08 '21 at 18:10
  • @ChrisGergler You access he promise value either from the `then` callback, or by using `await`. Assignment to some global function will lead to race conditions and hard to find bugs. – Ruan Mendes Dec 08 '21 at 19:59
  • So, for example, I have: ```js async function getLocation( long, lat) { //Establish initial local vars and call for the location data. let response = await fetch(myloc + long +"," +lat); if(!response.ok){throw new Error(response.status)} site = await response.json(); } ``` site is receiving an object output. I would instead want to just assign the promise received to site, and then when I need to I put on ```site.then((x)=> { return data });```? – Chris Gergler Dec 09 '21 at 16:43
  • You seem focused on trying to assign some asynchronous value to a global variable but we're telling you that you should not do that. Doing that is asking for trouble because you have no way to know when that variable will be set. – Ruan Mendes Dec 09 '21 at 19:14
  • Did you go through the linked answers? It sounds like you haven't and you still don't understand how asynchronous programming works in JS but you want to keep using a method we're explicitly telling you not to use (assigning an asynchronous value to a non-local variable). The only safe way to read an asynchronous value is from the callback of a promise (or by using `await`) and properly chain the promises to ensure everyone is waiting for it to be available before trying to use it. If it's still confusing, ask a more focused question because you have the solution for the problem you posed. – Ruan Mendes Dec 09 '21 at 19:16
  • I explicitly stated that I'm having trouble understanding. – Chris Gergler Dec 09 '21 at 19:25
  • That's why I suggested you ask another question. One that doesn't involve trying to assign to a global variable and accessing the same global variable from a different function. Notice that my solution doesn't assign to non-local variables, it chains them through return values. – Ruan Mendes Dec 09 '21 at 21:33
  • By the way, you may have missed the very beginning of my answer "You should be returning a value from pullData so that others can await for it" If you change `pullData` to `return fetch`, then `Promise.resolve(pullData(url)).then(()=> buildDataSets());` will work as expected though it could be simplified to `pullData(url).then(buildDataSets)`; However, we're explicitly telling you not to do that because it is problematic. – Ruan Mendes Dec 09 '21 at 21:37
  • I definitely did miss that, thank you for highlighting it! I did post a second question, with a bit more breakdown of what I'm attempting to do. Hopefully when I do these adjustments it'll work as I need it and I can just keep going. Thank you for your patience with me! – Chris Gergler Dec 09 '21 at 21:46