【问题标题】:angularjs $q.all for nested promisesangularjs $q.all 用于嵌套承诺
【发布时间】:2017-07-18 20:16:02
【问题描述】:

尝试在 AngularJS (1.6) 中获取嵌套的异步 REST 请求,然后在所有请求完成后执行代码。

我尝试使用$q.all,但似乎这不会考虑内部请求(剧集的请求)。

如何修改以下示例,以便在所有请求完成后执行代码?

// foreach series --> load series details & seasons, foreach season --> load episodes
// when all requests are completed: do something

var requests = []

// e.g. series = [1]
series.forEach(function(seriesId) {
    requests.push(loadSeriesDetails(seriesId).then(function(data) {
        console.log("received details for series "+seriesId)
        // e.g. {id:1, title:"show 1"}
    }))
    requests.push(loadSeasons(seriesId).then(function (data) {
        console.log("received seasons for series "+seriesId+": ", data.seasons.length)
        // e.g. [{id:11, title:"season 1"}, {id:12, title:"season 2"}]
        data.seasons.map(function(e) {return e.id}).forEach(function(seasonId) {
            requests.push(loadEpisodes(seasonId).then(function (data) {
                console.log("received episodes for season "+seasonId+": ", data.episodes.length)
                // e.g.
                // season 11: [{id:111, title:"episode 1-1-1"}, {id:112, title:"episode 1-1-2"}, {id:113, title:"episode 1-1-3"}]
                // season 12: [{id:121, title:"episode 1-2-1"}, {id:122, title:"episode 1-2-2"}]
            }))
        })
    }))
})

$q.all(requests).then(function(result) {
    console.log("*** all requests completed ***")
    console.log(result.length)
})

上面的示例将返回 2 (1x loadSeriesDetails, 1x loadSeasons) 而不是 4 (1x loadSeriesDetails, 1x loadSeasons, 2x loadEpisodes)。

建议?提前致谢!

更新:每个请求函数都是这样的:

loadSeriesDetails = function(id) {
  url = "..."+id

  return $http.get(url).then(
    function (result) {
      return result.data
    }, function (error) {
      ̶r̶e̶t̶u̶r̶n̶ ̶e̶r̶r̶o̶r̶ 
      throw error;
  });
}

【问题讨论】:

  • JavaScript 有一个神奇的东西,叫做return statement。要了解如何将它与 Promise 一起使用,请参阅 You're Missing the Point of Promises
  • 不确定我是否理解您的评论。承诺函数有回报(我已经更新了问题以显示一个例子)。不要认为 .then(...) 回调函数需要返回。你有一个应该是什么样子的例子吗?
  • 为避免将被拒绝的承诺转换为已履行的承诺,请在拒绝处理程序中使用throw statement

标签: angularjs angular-promise


【解决方案1】:

Frank's answer 的变体,更接近原始代码 sn-p。

// foreach series --> load series details & seasons, foreach season --> load episodes
// when all requests are completed: do something

var requests = []

// e.g. series = [1]
series.forEach(function(seriesId) {
    requests.push(loadSeriesDetails(seriesId).then(function(data) {
        console.log("received details for series "+seriesId)
        // e.g. {id:1, title:"show 1"}
    }))
    requests.push(loadSeasons(seriesId).then(function (data) {
        console.log("received seasons for series "+seriesId+": ", data.seasons.length)
        // e.g. [{id:11, title:"season 1"}, {id:12, title:"season 2"}]
        var innerRequests = [] // <<<==============
        data.seasons.map(function(e) {return e.id}).forEach(function(seasonId) {
            innerRequests.push(loadEpisodes(seasonId).then(function (data) {
                console.log("received episodes for season "+seasonId+": ", data.episodes.length)
                // e.g.
                // season 11: [{id:111, title:"episode 1-1-1"}, {id:112, title:"episode 1-1-2"}, {id:113, title:"episode 1-1-3"}]
                // season 12: [{id:121, title:"episode 1-2-1"}, {id:122, title:"episode 1-2-2"}]
            }))
        })
        return $q.all(innerRequests) //  <<<==============
    }))
})

$q.all(requests).then(function(result) {
    console.log("*** all requests completed ***")
    console.log(result.length)
})

【讨论】:

    【解决方案2】:

    当您将requests 数组传递给$q.all 时,您的requests 数组应该包含所有需要解决的承诺。目前,您正在为 loadEpisodes 添加您对 requests 的承诺,这是在较早的承诺解决后。

    您可以将其分解,以便内部部分返回自己的$.q.all,这将成为链接:

        var requests = [];
    
        series.forEach(function (seriesId) {
            requests.push(loadSeriesDetails(seriesId).then(function (data) {
                console.log("received details for series " + seriesId);
                return data;
            }));
    
            requests.push(loadSeasons(seriesId).then(function (data) {
                console.log("received seasons for series " + seriesId + ": ", data.seasons.length);
                return loadAllEpisodes(data.seasons);
            }));
        });
    
        function loadAllEpisodes(seasons) {
            var requests = seasons.map(function (season) {
                return loadEpisodes(season.id).then(function (data) {
                    console.log("received episodes for season " + season.id + ": ", data.episodes.length);
                    return data;
                });
            });
    
            return $q.all(requests);
        }
    
        q.all(requests).then(function(result) {
            console.log("*** all requests completed ***");
            console.log(result.length);
        });
    

    请注意,在您的示例中,您仍然只能在从外部 $q.all 返回的最终数组中看到 2 个结果,因为您从每个链的最后一个承诺中获得结果。因此,您可能需要调整您的退货声明,以便它们按照您需要的方式对信息进行分组。

    【讨论】:

    • 感谢弗兰克,这确实是解决方案。我将添加答案的变体,这需要更少的修改。
    • @georgeawg 感谢您的编辑。最初的问题没有返回结果,所以我就这样离开了,但你是对的,如果需要,应该返回结果。
    • 我想鼓励读者总是从函数返回结果。它使这些功能更有用、更通用、更易于调试、更易于测试和更易于维护。
    猜你喜欢
    • 1970-01-01
    • 2016-02-01
    • 2014-02-02
    • 1970-01-01
    • 2016-03-04
    • 1970-01-01
    • 2015-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多