0

I started working on an android app. I am at the point that i need to use an API to get some info. I am using an API for movies so it contains id, title and some other info. My problem is that in this URL i don't hav every information that i need so i have to make another fetch for every movie to get the additional info but as i noticed first JS runs the code in the first fetch ( without the nested fetch ) and then it runs the nested fetch. This way some info are not displayed on the screen.

fetch(movie)
    .then(function(response) { return response.json(); })
    .then(function(responseObject) {
        movies.value = responseObject;
        for(var i=0;i<movies.value.results.length;i++)
        {
            movies.value.results[i].poster_path = poster+movies.value.results[i].poster_path;
            var info = "http://api.themoviedb.org/3/movie/"+movies.value.results[i].id+"?api_key";
            fetch(info)
                .then(function(response) { return response.json(); })
                .then(function(responseObject) {
                    moreInfo.value = responseObject;
                    if(j < movies.value.results.length)
                    {
                        movies.value.results[j].runtime = moreInfo.value.runtime;
                        console.log("print 1 "+movies.value.results[j]);
                        j++;
                    }
            });
            console.log("print 2 "+movies.value.results);
        }
});

what i am trying to do here is add from the second fetch the runtime to my movies Observable object. A sortened version of the result :

print 2 : movies.value.results

print 1 : movies.value.results[i]

The problem as i said is that the code inside the first fetch is executed without executing the nested fetch and then the nested fetch executes. Sorry if i did something really bad but i just started developing on android so please show me mercy :D

*Sorry for not explaining about my j variable. It is defined as zero above fetch(movie). The only reason i use it is because i inside fetch(info) is always at the max number i cang get from movie.value.results.length and because i need to pass the runtime at the specific movie.value.result[j]. So i have a counter that increases only when the fetch(info) is executed.

Fotis
  • 97
  • 2
  • 13
  • Why should it appear anything on the screen? You use `console.log`. Try `document.write("print 1 "+movies.value.results[j]);` – vaso123 May 04 '16 at 15:20
  • @lolka_bolka sorry for misunderstading but i get results... i just cant understand why with the order i wrote. ( first print 2 and then print 1) – Fotis May 04 '16 at 15:26
  • @Riddell will check it out thanks – Fotis May 04 '16 at 15:27
  • Sorry accidentally deleted my comment lol. Promises have better browser support as Fetch does not support IE like promises but Fetch has bug on Safari ( Webkit ). Here is docs for Promiseshttps://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise It might not help but It's another information giver. – Riddell May 04 '16 at 15:30
  • Where is the Variable `j` declared? Why do you need it? You start each inner fetch in the for loop that test i for being `i < movies.value.results.length`. The function in the `then` call is a lambda function or closure that is instanciated for each loop step. That asures that you can use the variable `ì` inside this function. If j is not initialized correctly the code in the if block may not be executed or not as often as you think ;-). – Peter Paul Kiefer May 04 '16 at 15:42
  • @PeterPaulKiefer thanks for the reply. I edited the answer to answer your question. Hopefully i am not completely wrong ( according to the answer that was given ) – Fotis May 05 '16 at 14:44
  • 1
    *"So i have a counter that increases only when the fetch(info) is executed."* This sounds like it could be very error-prone approach when dealing with asynchronous stuff like the fetch api. If you call the fetch(info) 2 times in a row `j` will be 1 instead of 0 when they both finish loading, won't it? – noppa May 05 '16 at 14:50
  • 1
    You can't reliably predict the order in which the fetch(info) calls will finish loading and thus the value of `j` might point to the wrong movie. – noppa May 05 '16 at 14:57
  • @noppa i see. but how can i fix this ? – Fotis May 05 '16 at 15:00
  • You can fix it by removing the variable `j` and use `i` instead of it. If you declared j outside the callback function that you passed to `then` then, inside the callback you deal with a copy of that `j`. That's essential for a closure. Increasing `j` from a `then` call will not increase the value of j outside of the function. So you only work with `j=0`. `i`is defined in by the for loop outside of the callback and so each call to `then` gets its own copy of the actual value of `i`. If I understood you correctly, that's what you want. – Peter Paul Kiefer May 05 '16 at 15:08
  • 2
    [Working fiddle](https://jsfiddle.net/nobba/8cpe45bu/4/) to give you an example. It could still be improved for example by using the return values of the promises instead of shared variable but I tried concentrating on the main issue. – noppa May 05 '16 at 15:51
  • @noppa thanks very much for your time and effort. I will study and try to make things work ! Thanks again – Fotis May 05 '16 at 15:55

1 Answers1

3

This snippet might have some other problems too, like pointed out in the comments, but I think I know where the main confusion comes here.

The problem as i said is that the code inside the first fetch is executed without executing the nested fetch and then the nested fetch executes.

This is not true. Or, at least the order of console logs doesn't imply that. The order of execution inside the outer then-callback is

  1. fetch(info) This statement starts fetching the data
  2. .then(function(response){ ... }) Register a callback to execute when the promise gets resolved. Note that as fetching is asynchronous, the callback function does not get called right away. It will get called later, when the data has finished loading. Meanwhile, synchronous execution of the call stack continues.
  3. .then(function(responseObject) { ... }) Same thing here. We create a function to run when the data is ready (if ever). Meanwhile the statements outside that function can still be evaluated.
  4. console.log("print 2 "+movies.value.results); Now here comes the first log statement. The JavaScript execution does not halt to wait for the fetch to finish. That's the whole idea of asynchronous programming.

Now, some time passes and finally, the data has finished loading.

  1. return response.json(); The Promise returned from the fetch gets fulfilled because the operation was successful. It then runs the callback that you provided earlier.
  2. moreInfo.value = responseObject; if(j < movies.value.results.length) { movies.value.results[j].runtime = moreInfo.value.runtime; console.log("print 1 "+movies.value.results[j]); j++; } This is the second callback we created and same thing applies here. Now that the data is finally ready, this can be executed. I'm not sure about the whole j thing, but apparently the execution will eventually get to the console.log("print 1 "+movies.value.results[j]); statement.

The important thing to understand here is that whether or not the 'print 1' statement was in the source code above the 'print 2' line is irrelevant. The 'print 1' was scheduled for later, namely, when the second fetch is done.

Fixed version

var info = //URL
fetch(info)
    .then(function(response) { return response.json(); })
    .then(function(responseObject) {
        moreInfo.value = responseObject;
        ...
            console.log("print 1 "+movies.value.results[j]);
        ...
     })
     .then(function(){
        console.log("print 2 "+movies.value.results);
     });

Now it could also be that I totally misunderstood the question. But if this was about asynchronicity, you should also check out the MDN Promise docs linked above and this SO question about asynchronicity in Node.js, which also applies to other JavaScript environments.

Community
  • 1
  • 1
noppa
  • 3,947
  • 21
  • 22
  • Thank you very much @noppa for answering. I do understand some things better now about how fetch works. Now my question is how can i wait ( if possible ) for fetch(info) to finish before it continues any further ? I am using Fusetools for my app and my problem is that by the time i create the movies on the screen (poster image, title etc.) the extra info from the fetch(info) are not ready thus they are not displayed. – Fotis May 05 '16 at 14:57
  • All the operations that require the data to be available need to be wrapped inside the `then` callbacks of the request, like you already have done to some of them. Tell it to *"fetch data,* **then** *display it on the screen"* – noppa May 05 '16 at 15:03
  • Yes i understand how the 'fetch' and 'then' works now but i can't figure out how to display them inside a 'then' when the code responsible for that is in different file. The Js code is in a .js file and the rest in a .ux file. – Fotis May 05 '16 at 15:32
  • 1
    @Fotis I updated the example I posted to cover using that as a function. https://jsfiddle.net/nobba/8cpe45bu/6/ Unfortunately I don't know much about android development or how to use the promises in .ux files, but there is an example how it would be used in "regular" JavaScript app. Good luck! – noppa May 05 '16 at 16:04