Home > Blockchain >  Understanding the asyncronous nature of fetch().then()
Understanding the asyncronous nature of fetch().then()

Time:01-13

This is a predominantly theoretical question as I guess I would not use the fetched data outside of the .then() method. I was just checking the behaviour of fetch.then() out of curiosity as I like to test and understand the principles of the languages I learn.

As expected

setTimeout(()=>{
console.log("myGlobVar in setTimeOut");
console.log(myGlobVar)}, 1000);

returns the fetched object, provided I use a delay long enough in setTimeOut (about 40ms in this case)

But I'm curious why the

console.log("myGlobVar after for loop");
console.log(myGlobVar); //returns undefined

does not work. My rationale here was that running the loop long enough, would give enough time to fetch() to fetch the data and assign it to myGlobVar, which doesn't happen no matter how long it runs.

Actually -counterintuitively- for extremely high numbers in the loop (1000000000) even the

setTimeout(()=>{
console.log("myGlobVar in setTimeOut");
console.log(myGlobVar)}, 1000);

returns undefined.

<script>
  "use strict"
let myGlobVar;
  function loadPeaks() {
  console.log("loadPeaks");
  fetch('soundofChengdu.json')
  .then(response => {
    if (!response.ok) {
        throw new Error("HTTP error: "   response.status);
    }
    return response.json();
  })
  .then(peaks => {
    // wavesurfer.load('soundofChengdu.mp3', peaks.data);
    myGlobVar= peaks;
  })
  .catch((e) => {
    console.log({errorIs: e})
    // console.error('error', e);
  });
  }

  loadPeaks();
  console.log("myGlobVar without waiting");
  console.log(myGlobVar); //returns undefined
  setTimeout(()=>{console.log("myGlobVar in setTimeOut"); console.log(myGlobVar)}, 1000); //returns undefined under  -40 ms, works above.

let b;
 console.log("loop running");
  for (let a=0; a<100000000; a  ) {
    b=b a;
  }

console.log("myGlobVar after for loop");
console.log(myGlobVar); //returns undefined

</script>

CodePudding user response:

The first part is quite usual, you blocked the event-loop synchronously, no other task or even microtask can be executed in between so your variable is not set.

The second part about setTimeout() firing before the request's Promise gets resolved is a bit more interesting though, what you've discovered here is the task prioritization system.

All tasks don't have the same priority, and browsers are free to execute one kind of task before another.

For instance here, your browser gives more importance to timer tasks than to network tasks. And for the ones wondering "but fetch().then() is a microtask and should have higher priority", the returned Promise is resolved in a "normal" task (see this answer of mine), and that task is subject to the task prioritization system.

CodePudding user response:

The code after loadPeaks() is synchronously called, meaning that the code will not wait for loadPeaks() to finish running and will run completely before loadPeaks() finishes.

This is the same for the for loop. Regardless of the iteration count, the for loop will finish before the request finishes. You can test this by adding a console.log() statement in your second then() call and see when it is printed in relation to the rest of your log statements.

Promise.then() on the other hand, waits for the request to finish. In your case, fetch() will run first and the then() calls will run sequentially, waiting for the previous call to finish before running.

CodePudding user response:

My rationale here was that running the loop long enough, would give enough time to fetch() to fetch the data and assign it to myGlobVar, which doesn't happen no matter how long it runs.

JavaScript is actually strictly single-threaded, unlike most other languages you're probably familiar with. Instead of having threads running in parallel, it uses an Event Loop pattern where each operation runs, pushes follow-up operations onto the queue, and then yields back to the loop. I'll quote from the article I just linked to explain how this affects your code (emphasis mine):

"Run-to-Completion"

Each message is processed completely before any other message is processed.

This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be preempted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it may be stopped at any point by the runtime system to run some other code in another thread.

This explains why the code after the for loop never has access to that global variable no matter how high the iteration count is: the event loop is still executing your original function, so none of those promise handlers have had any chance to execute.

@Kaiido's answer for your second question is much better than what I was going to write, so I won't bother. I'll leave the above up just in case a more basic explanation helps.

  • Related