链接映射 axios/API 调用

Chaining mapped axios/API calls

对于我正在使用的 API,我必须从 3 个单独的端点反弹才能获得我需要的数据。我被困在最后一个终点,这让我发疯。这是我目前正在做(或试图做)的要点。

  1. 直接调用ENDPOINT 1。通过map处理数据(到return我具体需要的数据)然后推送到ARRAY 1。
  2. ARRAY 1 完成处理后,我将映射 ARRAY 1 的数据以对其每个 ID 进行 API 调用到 ENDPOINT 2,然后将其推送到 ARRAY 2。
  3. ARRAY 2 完成处理后,我将映射 ARRAY 2 的数据以对其每个 ID 进行 API 调用到 ENDPOINT 3,然后将其推送到 ARRAY 3。
  4. 所有这些步骤都包含在一个 promise 中,该 promise 用 3 个完整的数组解析。

第 1 步和第 2 步做得很好,但我已经为第 3 步尝试了一百万种不同的方法,并且它保持 returning 。处理此问题的最佳方法是什么?任何帮助将不胜感激!

router.get("/", (req, res) => {
  let artists = [];
  let albums = [];
  let tracks = [];

  const options = {
    headers: {
      'Authorization': `Bearer ${token}`,
    },
  };

  function getArtists(url) {
    return new Promise(resolve => {
      axios.get(url, options).then(response => {
        artists.push(...response.data.artists.items.map(artist => ({
          url: artist.external_urls.spotify,
          name: artist.name,
          images: artist.images,
          id: artist.id,
          genres: artist.genres,
        })));
        let next = response.data.artists.next;
        if (next !== null) {
          getArtists(next);
        } else {
          resolve(getAlbums().then(() => getTracks().then(() => res.send({artists, albums, tracks}))));
        };
      });
    });
  };

  let getAlbums = () => {
    return new Promise(resolve => {
      const requests = artists.map(item => {
        return axios.get(`https://api.spotify.com/v1/artists/${item.id}/albums?market=us&include_groups=single,appears_on`, options).then(response => {
          albums.push(...response.data.items);
        });
      });
      Promise.all(requests).then(() => {
        const filtered = albums.filter((curr, index, self) => self.findIndex(t => t.id === curr.id) === index);
        const sorted = filtered.sort((a, b) => (b.release_date > a.release_date) ? 1 : -1); // change data to filtered to filter duplicates
        const sliced = sorted.slice(0, 50);
        albums = sliced;
        // res.send({artists, albums});
        resolve();
      });
    });
  };

  let getTracks = () => {
    albums.map(item => {
      return axios.get(`https://api.spotify.com/v1/albums/${item.id}/tracks`, options).then(response => {
        tracks.push(...response.data.items);
      });
    });
  };

  if (token) {
    const url = 'https://api.spotify.com/v1/me/following?type=artist&limit=50';
    getArtists(url);
  } else {
    res.send({
      message: 'Please login to retrieve data',
    });
  };
});

我认为您最好采用更简单的方法,使用 async/await。这使我们能够以更易于遵循的方式构建代码。如果我们有大量的 .then() 和新的 Promises 等,它会很快变得非常混乱!

我已经像这样重组了,我认为这更容易理解并且希望能够调试!

我们可以遍历 getXXX 函数中的每个项目,或者我们可以使用 Promise.all,这两种方法都有效,尽管后者可能更高效。

我在 getTracks() 中使用过这个

router.get("/", async (req, res) => {
    let options = {
        headers: {
        'Authorization': `Bearer ${token}`,
        }
    }

    if (!token) {
        res.send({
            message: 'Please login to retrieve data',
        });
        return;
    }

    const url = 'https://api.spotify.com/v1/me/following?type=artist&limit=50';
    let artists = await getArtists(url, options);
    console.log ("Artists (length):", artists.length );
    let albums = await getAlbums(artists, options);
    console.log ("Albums (length):", albums.length );
    let tracks = await getTracks(albums, options);
    console.log("Tracks (length):", tracks.length);
    res.send( { albums, artists, tracks } );   
}

async function getArtists(url, options, maxLoopCount = 100) {

    let artists = [];
    let count = 0;
    do {
        console.log(`getArtists: Page #${++count}...`);
        let artistResp = await getArtistsPage(url, options);
        artists.push(...artistResp.artistList);
        url = artistResp.next;
    } while (url && count < maxLoopCount) ;
    return artists;
}

async function getArtistsPage(url, options) {

    let response = await axios.get(url, options);

    let artistList = response.data.artists.items.map(artist => ({
        url: artist.external_urls.spotify,
        name: artist.name,
        images: artist.images,
        id: artist.id,
        genres: artist.genres,
    }));

    let next = response.data.artists.next;
    return { artistList, next}
};

async function getAlbums(artists, options, sliceCount = 50) {
    let albums = [];

    for(let artist of artists) {
        let response = await axios.get(`https://api.spotify.com/v1/artists/${artist.id}/albums?market=us&include_groups=single,appears_on`, options);
        albums.push(...response.data.items);
    }

    const filtered = albums.filter((curr, index, self) => self.findIndex(t => t.id === curr.id) === index);
    const sorted = filtered.sort((a, b) => (b.release_date > a.release_date) ? 1 : -1); // change data to filtered to filter duplicates
    const sliced = sorted.slice(0, sliceCount);
    return sliced;
}

async function getTracks(albums, options) {
    let promises = albums.map(album => axios.get(`https://api.spotify.com/v1/albums/${album.id}/tracks`, options));
    let responseList = await Promise.all(promises);
    return responseList.map(response => response.data.items).flat();
}