Here is my problem:
Spotify doesn't return all user's saved tracks. There is limit for count of returning tracks - 50 (here is API).
I found a solution that returns all user's saved track (used loop do-while). It makes a lot of requests (in my case was ~17 times - 814 tracks) But my page loads from 6 secs to 8 secs.
I read about Concurrent requests but I don't know how to use this and async requests in my situation because in my case is no known amount of requests. The loop ends only when count of returning tracks(items) are 0. Can you help me with my problem?
<?php
namespace AppBundle\Service;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class SpotifyRequester
{
protected $client;
protected $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
$this->client = new Client();
}
public function getSavedTracks()
{
$token = $this->getToken(); // true token
$offset = 0;
do {
$response = $this->client->request('GET',
'https://api.spotify.com/v1/me/tracks?limit=50&offset=' . $offset, [
'headers' => [
'Authorization:' => 'Bearer ' . $token,
'Accept:' => 'application/json',
'Content-Type:' => 'application/json',
]
]);
// Response from current request
$content = json_decode($response->getBody()->getContents(), true);
$offset += count($content['items']);
}
while (count($content['items']) != 0);
// Count of tracks
return $offset;
}
}
Don't rely on that condition. Either rely on the next
entry not being null
or count the total entries you have and compare it to the total
entry.
Spotify exposes a total
number of entries in the pagination wrapper around the response. You can make a first request with the first 50 entries, then make concurrent requests for all remaining chunks, because you know the total number at that point.
You have to use asyncRequest()
for the further requests, which returns a promise, and schedule all your remaining requests. Then you can wait for the promises sequentially using the wait()
instance method. The order of your wait()
calls doesn't matter, because wait()
will tick the internal event loop and make progress for any of your requests. All further wait()
calls take either way shorter to run or even resolve immediately.
Unfortunately, you will have to construct the URLs manually, instead of being able to rely on the next
entry for your URLs.
I'd recommend to add some limit of concurrency, Spotify probably has some guidelines for that. Guzzle offers a Pool
implementation for that.