简体   繁体   English

AngularJS中带有promise的递归查询

[英]Recursive queries with promises in AngularJS

I have a recursive query that needs to potentially make further queries based on the results. 我有一个递归查询,可能需要根据结果进行进一步查询。 I would ideally like to be able to construct a promise chain so that I know when all of the queries are finally complete. 理想情况下,我希望能够构建一个Promise链,以便我知道所有查询何时最终完成。

I've been using the example from this question , and I have the following method: 我一直在使用这个问题示例 ,并且有以下方法:

 this.pLoadEdges = function(id,deferred) {
    if (!deferred) {
        deferred = $q.defer();
    }
    $http.post('/Create/GetOutboundEdges', { id: id }).then(function(response) {
        var data = response.data;

        if (data.length > 0) {
            for (var i = 0; i < data.length; i++) {
                var subID = data[i].EndNode;

                edgeArray.push(data[i]);

                self.pLoadEdges(subID, deferred);
            }
        } else {
            deferred.resolve();
            return deferred.promise;
        }

    });

    deferred.notify();
    return deferred.promise;
}

Which I then start elsewhere using: 然后我开始在其他地方使用:

 self.pLoadEdges(nodeID).then(function() {
                    var edgedata = edgeArray;
                });

And of course I intend to do some more stuff with the edgeArray. 当然,我打算用edgeArray做更多的事情。

The problem is that the then() function is trigged whenever any individual path reaches an end, rather than when all the paths are done. 问题在于,只要有任何一条单独的路径到达终点,而不是在所有路径都完成时,就会触发then()函数。 One particular pathway might be quite shallow, another might be quite deep, I need to know when all of the pathways have been explored and they're all done. 一种特定的途径可能很浅,另一种可能很深,我需要知道何时探索了所有途径并且它们都已完成。

How do I construct a promise array based on this recursive query, ideally so that I can use $q.all[] to know when they're all done, when the number of promises in the promise array depends on the results of the query? 我如何基于此递归查询构造一个promise数组,理想情况下,以便我可以使用$ q.all []知道它们何时完成,何时promise数组中的promise数量取决于查询结果?

I'm not 100% positive what the end result of the function should be, but it looks like it should be a flat array of edges based on the example that you provides. 我不是100%肯定该函数的最终结果应该是什么,但是根据您提供的示例,它看起来应该是边缘的平面阵列。 If that's correct, then the following should work 如果是正确的话,那么以下应该工作

this.pLoadEdges = function(id) {
    var edges = [];

    // Return the result of an IIFE so that we can re-use the function
    // in the function body for recursion
    return (function load(id) {
        return $http.post('/Create/GetOutboundEdges', { id: id }).then(function(response) {
            var data = response.data;

            if (data.length > 0) {
                // Use `$q.all` here in order to wait for all of the child
                // nodes to have been traversed. The mapping function will return
                // a promise for each child node.
                return $q.all(data.map(function(node) {
                    edges.push(node);

                    // Recurse
                    return load(node.EndNode);
                });
            }
        });
    }(id)).then(function() {
        // Change the return value of the promise to be the aggregated collection
        // of edges that were generated
        return edges;
    });
};

Usage: 用法:

svc.pLoadEdges(someId).then(function(edgeArray) {
// Use edgeArray here
});

You need $q.all function: 您需要$q.all函数:

Combines multiple promises into a single promise that is resolved when all of the input promises are resolved. 在将所有输入的Promise解析后,将多个Promise合并为一个Promise。


Update 1 更新1

Check this demo: JSFiddle 检查此演示: JSFiddle

The controller can be like following code (well, you may want to put it in a factory ). 控制器可能类似于以下代码(嗯,您可能希望将其放置在factory )。

It loads a list of users first, then for each user, load the posts of this user. 它首先加载用户列表,然后为每个用户加载该用户的帖子。 I use JSONPlaceholder to get the fake data. 我使用JSONPlaceholder来获取假数据。

$q.all accepts an array of promises and combine them into one promise. $q.all接受一系列的诺言并将它们组合成一个诺言。 The message All data is loaded is only displayed after all data is loaded. 仅在加载所有数据后,才会显示消息All data is loaded所有数据”。 Please check the console. 请检查控制台。

angular.module('Joy', [])
    .controller('JoyCtrl', ['$scope', '$q', '$http', function ($scope, $q, $http) {
    function load() {
        return $http.get('http://jsonplaceholder.typicode.com/users')
            .then(function (data) {
            console.log(data.data);
            var users = data.data;
            var userPromises = users.map(function (user) {
                return loadComment(user.id);
            });
            return $q.all(userPromises);
        });
    }

    function loadComment(userId) {
        var deferred = $q.defer();
        $http.get('http://jsonplaceholder.typicode.com/posts?userId=' + userId).then(function (data) {
            console.log(data);
            deferred.resolve(data);
        });
        return deferred.promise;
    }

    load().then(function () {
        console.log('All data is loaded');
    });
}]);

Update 2 更新2

You need a recursive function, so, check: JSFiddle . 您需要一个递归函数,因此,请检查: JSFiddle

The code is below. 代码如下。 I use round to jump out of the recursion because of the fake API. 由于使用了伪造的API,我使用round来退出递归。 The key is here: $q.all(userPromises).then(function () { deferred.resolve(); }); 密钥在这里: $q.all(userPromises).then(function () { deferred.resolve(); }); . That tells: Please resolve this defer object after all promises are resolved . 这说明: Please resolve this defer object after all promises are resolved

angular.module('Joy', [])
    .controller('JoyCtrl', ['$scope', '$q', '$http', function ($scope, $q, $http) {
    var round = 0;

    function load(userId) {
        return $http.get('http://jsonplaceholder.typicode.com/posts?userId=' + userId)
            .then(function (data) {
            var deferred = $q.defer();
            console.log(data.data);
            var posts = data.data;
            if (round++ > 0 || !posts || posts.length === 0) {
                deferred.resolve();
            } else {
                var userPromises = posts.map(function (post) {
                    return load(post.userId);
                });
                $q.all(userPromises).then(function () {
                    deferred.resolve();
                });
            }
            return deferred.promise;
        });
    }

    load(1).then(function () {
        console.log('All data is loaded');
    });
}]);

You can try building up an array of returned promises and then use the $.when.apply($, <array>) pattern. 您可以尝试建立返回的$.when.apply($, <array>)数组,然后使用$.when.apply($, <array>)模式。 I've used it before to accomplish a similar thing to what you're describing. 我以前用它来完成与您所描述的内容类似的操作。

More info on this SO thread . 有关此SO线程的更多信息

UPDATE: 更新:

You probably also want to read the docs on the apply function , it's pretty neat. 您可能还想阅读apply函数上的文档,这很简洁。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM