【问题标题】:Programmatically Obtain All YouTube Videos Belonging to a User以编程方式获取属于用户的所有 YouTube 视频
【发布时间】:2019-05-22 08:52:38
【问题描述】:

利用 YouTube 数据 API,我们可以进行查询以获取属于具有以下请求的用户的前 50 个(单个查询可获得的最大结果量)视频:https://www.googleapis.com/youtube/v3/search?key={access_key}&channelId={users_id}&part=id&order=date&maxResults=50&type=video

如果有超过 50 个视频,那么生成的 JSON 将有一个 nextPageToken 字段,为了获得接下来的 50 个视频,我们可以将 &pageToken={nextPageToken} 附加到上述请求中,生成下一页 50 个视频。这可以重复,直到 nextPageToken 字段不再存在。

这是我使用 fetch API 编写的一个简单的 JavaScript 函数,用于获取由nextPageToken 参数(或没有参数)指定的单个视频页面。

function getUploadedVideosPage(nextPageToken) {
    return new Promise((resolve, reject) => {
        let apiUrl = 'https://www.googleapis.com/youtube/v3/search?key={access_key}&channelId={users_id}&part=id&order=date&maxResults=50&type=video';
        if(nextPageToken)
            apiUrl += '&pageToken=' + nextPageToken;

        fetch(apiUrl)
        .then((response) => {
            response.json()
            .then((data) => {
                resolve(data);
            });
        });
    });
}

现在我们需要一个包装函数,只要我们有一个nextPageToken,它就会迭代地调用getUploadedVideosPage。这是我的“工作”,虽然很危险(稍后会详细介绍)。

function getAllUploadedVideos() {
    return new Promise((resolve, reject) => {
        let dataJoined = [];
        let chain = getUploadedVideosPage();

        for(let i = 0; i < 20000; ++i) {
            chain = chain
            .then((data) => {
                dataJoined.push(data);

                if(data.nextPageToken !== undefined)
                    return getUploadedVideosPage(data.nextPageToken);
                else
                    resolve(dataJoined);
            });
        }
    });
}

“危险”方面是 for 循环的条件,理论上它应该是无限的 for(;;) 因为我们没有预定义的方法来确切知道要进行多少次迭代,并且终止循环的唯一方法应该是resolve 声明。然而,当我以这种方式实现它时,它确实是无限的并且永远不会终止。

因此,我为什么要硬编码 20,000 次迭代,它似乎可以工作,但我不相信这个解决方案的可靠性。我希望这里有人可以阐明如何实现这个没有预定义终止条件的迭代 Promise 链。

【问题讨论】:

  • return 调用后解决?
  • resolve(dataJoined); 更改为 return resolve(dataJoined); 并没有解决它陷入无限循环的问题。
  • 为什么你有一个 for 循环?仅仅让递归做它的事情还不够吗?
  • 我推测递归可能是要走的路,不幸的是我的递归技能“生疏”。如果您能证明递归方法,我将非常感激。
  • 对不起,如果我之前的评论含糊不清。我得到的是,我希望 getUploadedVideosPage 将返回页面的结果,并与使用下一页令牌调用 getUploadedVideosPage 的剩余结果相连接。没有循环。只是 getUploadedVideosPage 在 thenable 中调用自己。

标签: javascript es6-promise youtube-data-api


【解决方案1】:

如果适用,您可以使用一个调用自身的函数来完成这一切。

您还在 new Promise 中使用 explicit promise construction anti-pattern 包装 fetch(),因为 fetch() 已经返回了一个承诺

function getVideos(nextPageToken, results = []) {

  let apiUrl = 'https://www.googleapis.com/youtube/v3/search?key={access_key}&channelId={users_id}&part=id&order=date&maxResults=50&type=video';
  if (nextPageToken) {
    apiUrl += '&pageToken=' + nextPageToken;
  }

  // return fetch() promise
  return fetch(apiUrl)
    .then(response => response.json())
    .then(data => {
      // merge new data into final results array
      results = results.concat(data);

      if (data.nextPageToken !== undefined) {
        // return another request promise
        return getVideos(data.nextPageToken, results);
      } else {
        // all done so return the final results
        return results
      }
    });
}

// usage
getVideos().then(results=>{/*do something with all results*/})
           .catch(err=>console.log('One of the requests failed'));

【讨论】:

  • 感谢您提供非常干净的解决方案。关于 Promise 的机制,我肯定还有更多的阅读要读。干杯!
  • 所以这里主要关注的是返回保证链继续运行的承诺或返回数据到最后的then()
【解决方案2】:

完全摆脱 for 循环。像这样的:

function getAllUploadedVideos() {
    return new Promise((resolve, reject) => {
        let dataJoined = [];

        function getNextPage(nextPageToken) {
            return new Promise((resolve, reject) => {
                getUploadedVideosPage(nextPageToken)
                    .then((data) => {
                        dataJoined.push(data);
                        if(data.nextPageToken !== undefined) {
                            resolve(data.nextPageToken);
                        }
                        else {
                            reject();
                        }
                    })
                    .catch((err) => {
                        // Just in case getUploadedVideosPage errors
                        reject(err);
                    });
            });
        }

        getNextPage()
            .then((nextPageToken) => {
                getNextPage(nextPageToken);
            })
            .catch(() => {
                // This will hit when there is no more pages to grab
                resolve(dataJoined);
            });

    });
}

【讨论】:

  • 它更像是一个“getNextPage”而不是“getNextVideo”,但基本上是正确的想法。也许“getNextVideos”实际上是它在每个请求中获取的 50 个视频的下一页。
  • 是的,如果合适的话。但这让它继续拉新页面直到它用完,而不需要任意迭代。
  • 更新为“getNextPage”
猜你喜欢
  • 2016-02-29
  • 1970-01-01
  • 2014-07-15
  • 2013-09-28
  • 2014-09-28
  • 1970-01-01
  • 2015-09-02
  • 1970-01-01
相关资源
最近更新 更多