使用AngularJS

时间:2015-08-05 14:38:22

标签: javascript angularjs recursion

我有一个递归查询,需要根据结果进行进一步的查询。理想情况下,我希望能够构建一个promise链,以便我知道所有查询何时最终完成。

我一直在使用example from this question,我有以下方法:

 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;
}

然后我从其他地方开始使用:

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

当然我打算用edgeArray做更多的事情。

问题是只要任何单个路径到达终点,就会触发then()函数,而不是在 all 路径完成时触发。一条特定的路径可能很浅,另一条路可能很深,我需要知道所有的路径何时被探索过并且它们都完成了。

如何基于这个递归查询构造一个promise数组,理想情况是我可以使用$ q.all []知道它们何时完成,当promise数组中的promise数量依赖于查询的结果?

3 个答案:

答案 0 :(得分:2)

我不是100%肯定该函数的最终结果应该是什么,但它看起来应该是基于您提供的示例的平面数组边。如果这是正确的,那么以下内容应该有效

public void addToNewPlaylist(){

    Tab nTab = new Tab();

    FXMLLoader nLoader = new FXMLLoader(getClass().getResource("/fxml/fxPlaylist.fxml"));
    try {
        Parent nRoot = nLoader.load();
        PlaylistController controller = nLoader.getController();
        controller.setTracks(table.getSelectionModel().getSelectedItems());
        nTab.setContent(nRoot);     
    } catch (IOException e) {
        e.printStackTrace();
    }
    tabPane.getTabs().add(nTab);
}

用法:

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;
    });
};

答案 1 :(得分:1)

您需要$q.all功能:

  

将多个promises组合成一个promise,当所有输入promise都被解析时解析。

更新1

查看此演示:JSFiddle

控制器可以像下面的代码一样(好吧,你可能想把它放在factory中)。

首先加载用户列表,然后为每个用户加载该用户的帖子。我使用JSONPlaceholder来获取虚假数据。

$q.all接受一系列承诺并将它们合并为一个承诺。消息All data is loaded仅在加载所有数据后显示。请检查控制台。

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');
    });
}]);

更新2

您需要一个递归函数,因此,请检查:JSFiddle

代码如下。由于假API,我使用round跳出递归。关键在于:$q.all(userPromises).then(function () { deferred.resolve(); });。这告诉我: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');
    });
}]);

答案 2 :(得分:0)

您可以尝试构建返回的promises数组,然后使用$.when.apply($, <array>)模式。我之前用它来完成与你所描述的类似的事情。

More info on this SO thread

更新:

您可能还想阅读apply function上的文档,它非常整洁。