0

I am trying to create a promise with a looped API request to Spotify. Spotify works so that I can only get 100 tracks at a time, so I check if there are more and rerun the function until all the tracks are appended to the "items" list. Right now, the promise resolves after 1 request, but I need to to resolve after all the requests are made. My code:

function getItems(myUrl) {
  return new Promise((resolve, reject) => {
    let items = []
    request.post(authOptions, function(error, response, body) {
      if (!error && response.statusCode === 200) {

        // use the access token to access the Spotify Web API
        var token = body.access_token;
        var options = {
          url: myUrl,
          headers: {
            'Authorization': 'Bearer ' + token
          },
          json: true
        };
        request.get(options, function(error, response, body) {
          if (error) return reject(error);

          for (var item of body['items']) {
            items.push(item)
          }
          if (response.body.next != null) {
            getItems(response.body.next)
          }
          resolve(items)
        })
      } else {
        reject(error)
      }
    })
    return items
  })
}

getItems('https://api.spotify.com/v1/playlists/1vZFw9hhUFzRugOqYQh7KK/tracks?offset=0')
  .then(res => console.log(res))
  • This would be a lot, lot easier (particularly implementing good error handling) if you switch to using a library like [`got()`](https://www.npmjs.com/package/got) that natively supports promises so you're not trying to patch together plain callbacks and somehow wrap them in a promise. Also, the `request()` library has been deprecated and should probably not be used for new code. – jfriend00 Jun 16 '20 at 20:44
  • 1
    FYI, your recursive call to `getItems()` returns a promise. You HAVE to use that promise. – jfriend00 Jun 16 '20 at 20:44
  • *"How do you resolve a promise after multiple API requests?*" - you don't. You resolve a promise immediately after each request. Then, you use promise chaining to implement your recursive `getItems` method. – Bergi Jun 16 '20 at 21:01
  • Have a look at https://stackoverflow.com/a/54219609/1048572 or https://stackoverflow.com/a/28550024/1048572 – Bergi Jun 16 '20 at 21:41

3 Answers3

0

Do something like

function getItems(myUrl, offset=0, items=[]) {
  return new Promise((resolve, reject) => {
request.post(authOptions, function(error, response, body) {
  if (!error && response.statusCode === 200) {

    // use the access token to access the Spotify Web API
    var token = body.access_token;
    var options = {
      url: myUrl,
      headers: {
        'Authorization': 'Bearer ' + token
      },
      json: true
    };
    request.get(options, function(error, response, body) {
      if (error) return reject(error);

      items = items.concat(body.item);
      offset++;

      if (response.body.next != null) {
         return resolve(getItems(response.body.next), offset, items)
      } else {
        return resolve(items)
      }
    })
  } else {
    reject(error)
  }
})
  })
}

getItems('https://api.spotify.com/v1/playlists/1vZFw9hhUFzRugOqYQh7KK/tracks?offset=0')
  .then(console.log(items))

The way this works is that the same function gets recursively called to get more items from Spotify and simply adds to previously returned items

takinola
  • 1,643
  • 1
  • 12
  • 23
  • `return resolve(items)` doesn't make sense. You conveniently dropped the actual asynchronous code - what does it really look like? – Bergi Jun 16 '20 at 21:02
  • I am obviously yada, yada -ing some of the code. My understanding is the question is about how to handle the recursion rather than the actual call to Spotify. The resolve statement presumes the async code uses a Promise – takinola Jun 16 '20 at 21:05
  • Yes, but the important part - that the OP didn't understand - is missing: how does the code after the async code relate to that promise? – Bergi Jun 16 '20 at 21:07
  • tried, this. Still skips to the .then instantly, before resolving the promise – Kishan Sripada Jun 16 '20 at 21:59
0

I changed it a bit to be able to resolve the promise after all the requests have been completed; however, when I console.log the items, nothing is returned.

let items = []

function getItems(myUrl) {
  return new Promise((resolve, reject) => {
    request.post(authOptions, function(error, response, body) {
      if (!error && response.statusCode === 200) {

        // use the access token to access the Spotify Web API
        var token = body.access_token;
        var options = {
          url: myUrl,
          headers: {
            'Authorization': 'Bearer ' + token
          },
          json: true
        };
        request.get(options, function(error, response, body) {
          if (error) return reject(error);

          for (var item of body['items']) {
            items.push(item)
          }
          if (response.body.next != null) {
            getItems(response.body.next)
          } else {
            resolve(items)
          }
        })
      } else {
        reject(error)
      }
    })
  })
}

getItems('https://api.spotify.com/v1/playlists/1vZFw9hhUFzRugOqYQh7KK/tracks?offset=0')
  .then(console.log(items))
  • it makes it to the else statement to resolve the items after about 3 seconds, but it skips to console.logging the items instantly – Kishan Sripada Jun 16 '20 at 21:15
  • Every (recursive) `getItems` call creates and returns its own promise. You are only resolving the innermost one. – Bergi Jun 16 '20 at 21:48
0

Try to use async/await. I couldn't debug my code, because I don't have spotify access. But I hope it's help you understand idea.

async function getItems(url) {
    var items = [];
    
    async function getData(url, items) {
        return new Promise((resolve, reject) => {
            request.post(authOptions, function(error, response, body) {
                if (error || response.statusCode !== 200) reject(error);

                var token = body.access_token;
                var options = {
                    url: url,
                    headers: {
                        'Authorization': 'Bearer ' + token
                    },
                    json: true
                };
                request.get(options, function(error, response, body) {
                    if (error) return reject(error);

                    for (var item of body['items']) {
                        items.push(item)
                    }
                    if (response.body.next == null) {
                        resolve([items, null])
                    } else {
                        resolve([items, response.body.next])
                    }
                })
            })
        })
    }

    while (url) {
        var response = await getData(url, items);
        items = response[0];
        url = response[1];
    }

    return items;
}


const myUrl = 'https://api.spotify.com/v1/playlists/1vZFw9hhUFzRugOqYQh7KK/tracks?offset=0';
async function main() {
    const items = await getItems(myUrl);
    console.log('Result: ' + items)
}


main();
Isa
  • 418
  • 5
  • 13