'Chained Fetch Promises (JSON, React, Webpack, NodeJS)

I need to fetch something 10 times and return the JSON of each request and push that into state. Most of the fetching works, but slows down about half way and halts before it completes. The promises seem to accept the 'cors' response, not the actual json, and thus triggers prematurely.

Also, I don't like how I have to repeat the same code so many times just to change the offset, but I spent hours fiddling with a for loop and I got stuck, so if you guys can suggest a better way to do this that would be awesome.

Here's the code:

function FetchAll(API_KEY, CX, query, E, that, pushState){

    fetch(`https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id=${E.target.value}&key=${API_KEY}`, {
      method: 'get',
      headers : { 
        'Content-Type': 'application/json',
        'Accept': 'application/json'
       }
    }).then(function(response){
      return response.json()
    }).then(function(uploads){
        console.log(uploads)
      return fetch(`https://www.googleapis.com/youtube/v3/playlistItems?playlistId=${uploads.items[0].contentDetails.relatedPlaylists.uploads}&key=${API_KEY}&part=snippet&maxResults=50`)
    }).then(response => {
      return response.json()
    }).then(function(data){
        console.log(data)
      that.setState({yt:data})
    }).then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${1}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${11}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${21}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${31}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${41}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${51}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${61}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${71}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${81}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${91}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))
    .then(function(){
      return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=10&cx=${CX}&q=${query}&start=${101}`)
    }).then(function(response){
      return response.json();
    }).then(r => pushState(r))

}

export default FetchAll

In case you were wonering, FetchAll is being called in the main App.js file, with everything being sent to it in parameters.

This is pushState (if needed)

FetchAll(API_KEY, CX, query, e, that,
    (r) => {
      console.log(r)
      let c = that.state.compareRaw
      c.push(r)
      that.setState({
        compareRaw: c
      })
    }
    )
}

'that' is a reference to 'this'

Any help or tips are greatly appreciated. Thanks!



Solution 1:[1]

First decide which request are waterfall request and which requests are parallel requests.

In waterfall model current request is dependent from previous request result, and is handled with sequencing .then() functions

In parallel model request are independent and can all fire in the same time. It can be solved with bluebird as a great helper tool for promises.

const Promise = require('bluebird');
Promise.all([fetch(...), fetch(...)...., fetchN(...)], 
            (result1, result2, result3 ..., resultN) => {
              //handle results
            })

You can always wrap api call in a function:

function search(start, limit) {
    return fetch(`https://www.googleapis.com/customsearch/v1?key=${API_KEY}&num=${limit}&cx=${CX}&q=${query}&start=${start}`)  
}

All together now

fetch(...) // request 1
.then((response) => fetch(...)) // request 2
.then((response) => {
   const apiCalls = [];
   for let i = 0; i < 10; i++ {
      apiCalls.push( search(i*10+1, 10) );
   }
   return Promise.all(apiCalls);
})
.then((...results) => {
   // handle search results
})

Hope it helps.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 J.D.