【问题标题】:How to wait until all async calls are finished如何等到所有异步调用完成
【发布时间】:2021-01-30 02:53:50
【问题描述】:

我有 NestJS 应用程序,它与 YoutubeAPI 交互并从中加载视频。 一种特殊的方法很重要,它是下面的loadVideos。它自己的方法内部有多个异步,一旦一切完成,我需要使用 videoIdMap 属性

private loadVideos(
    playListId: string,
    channel: Channel,
    nextPageToken: string,
    stopLoadingOnVideoId: string,
  ) {
    const baseUrl = YoutubeService.VIDEO_URL_SNIPPET_BY_ID + playListId;
    const response = this.httpService
      .get(nextPageToken ? baseUrl + '&pageToken=' + nextPageToken : baseUrl)
      .pipe(map((response) => response.data));
    response.subscribe((data) => {
      data.items.forEach((item) => {
        if (stopLoadingOnVideoId && item.snippet.resourceId.videoId === stopLoadingOnVideoId) {
          return;
        }        
        this.prepareVideoEntity(item.snippet, channel).then((partialVideo) =>              
          this.videoService.create(partialVideo).then((video) => {     
            this.videoIdMap[video.youtubeId] = video.id;
          }),
        );
      });      
      if (data.nextPageToken) {        
        this.loadVideos(
          playListId,
          channel,
          data.nextPageToken,
          stopLoadingOnVideoId,
        );
      }
    });
  }

对我来说理想的解决方案是以某种方式使 loadVideos 异步,以便我以后可以这样做:

public methodWhichCallLoadVideos(): void {
  await loadVideos(playListId, channel, null, stopLoadingOnVideoId)
  // My code which have to be executed right after videos are loaded
}

我尝试的每个解决方案都以 this.videoIdMap 为空对象或存在编译问题,因此任何想法都非常受欢迎。

【问题讨论】:

  • 为了“等待”加载视频,您需要将其设为“异步”。为此,它必须返回一个 promise,而 Nest 的 HttpService 返回一个 observable。检查这个问题:stackoverflow.com/questions/51910908/…

标签: express promise observable es6-promise nestjs


【解决方案1】:

您可以切换到 promises 而不是 Observables,从而将该方法变成一个异步方法,只要 data 有一个 nextPageToken 就可以重复:

private async loadVideos(
        playListId: string,
        channel: Channel,
        nextPageToken: string,
        stopLoadingOnVideoId: string,
    ) {
        const baseUrl = YoutubeService.VIDEO_URL_SNIPPET_BY_ID + playListId;
        const response = await this.httpService
            .get(nextPageToken ? url + '&pageToken=' + nextPageToken : url).toPromise();
        const { data } = response;
        for (const item of data.items) {
            if (stopLoadingOnVideoId && item.snippet.resourceId.videoId === stopLoadingOnVideoId) {
                continue;
            }
            const partialVideo = await this.prepareVideoEntity(item.snippet, channel);
            const video = await this.videoService.create(partialVideo)
            this.videoIdMap[video.youtubeId] = video.id;
        }
        if (data.nextPageToken) {
            await this.loadVideos(
                playListId,
                channel,
                data.nextPageToken,
                stopLoadingOnVideoId,
            );
        }
    }

在你的调用者中,你可以简单地等待loadVideos(...)

private async initVideoIdMap(...) {
  await this.loadVideos(...);
  // this.videoIdMap should be correctly populated at this point
}

【讨论】:

  • 接受 + 竖起大拇指!这是我一直在寻找的漂亮而干净的代码。我只能问你为什么用for循环替换forEach
  • 乐于助人 - foreach 不会等待在循环内完成的异步调用。它只会启动,而不是等待承诺解决。这里有一篇文章详细解释:itnext.io/…
猜你喜欢
  • 2011-10-04
  • 2016-02-03
  • 1970-01-01
  • 2016-03-16
  • 2020-02-22
  • 2015-06-09
  • 1970-01-01
  • 2017-01-22
  • 1970-01-01
相关资源
最近更新 更多