承诺&递归,如何在没有回调的情况下处理找到的项目?

时间:2015-09-09 13:13:34

标签: javascript angularjs asynchronous recursion promise

我在服务器上有一个文件夹层次结构。我想以异步方式和递归方式遍历此层次结构,并在找到文件时调用自定义方法。

目前,我的代码如下所示:

searchFile(root, handleFile);

// loop over all folders recursively, then handle files
function searchFile(item, callbackWhenFile) {
    if (itIsFolder(item)) {
        $http.get("api.call.com/children/" + item.id)
                .then(function (children) {
                    angular.forEach(children,
                        function (child) {
                            searchFile(child, callbackWhenFile);
                        });
                });
    }
    else {
        // this is not a folder, so this is a file
        callbackWhenFile(item);
    }
}

使用回调而不是承诺的事实并不是一件好事。

但是我试图找到一种方法让它无条件地与承诺一起工作。

在理想的解决方案中,我想做类似的事情:

searchFile(root).then(function (file) {
     handleFile(file)
});

我看了$ q.all,$ q.when,但似乎没有任何东西适合这种情况。我们不知道文件数量会导致问题变得困难。

任何人都知道如何解决这个问题?

修改答案

Promises are not the way to go.

使用RxJS和可观察对象,可以写成:

getFileStream(root).subscribe(function (file) {
    handleFile(file);
});

function getFileStream(root) {
    var source = Rx.Observable.create(function (observer) {
        observeFiles(observer, root);
    });
    return source;
}

function observeFiles(observer, item) {
    if (itIsFolder(item)) {
        $http.get("api.call.com/children/" + item.id)
                .then(function (children) {
                    angular.forEach(children,
                        function (child) {
                            searchFile(observer, child);
                        });
                });
    }
    else {
        observer.onNext(item);
    }
}

2 个答案:

答案 0 :(得分:3)

承诺只能解析为单个值。该值可以是对象或数组,但最后,它是单个值 - 而不是流。

您的“理想解决方案”被定义为确实生成了流,但这是不可能的。

你可以:

  1. 保持您的代码不变。您的回调是异步调用的,只要有要处理的文件就会这样做。
  2. 更改递归以组合单个值。文件的平面数组/地图(对象...)。然后可以在下一个函数(.then(processArray))内同步处理该值。
  3. 使用BaconJSRxJS等流媒体库。

答案 1 :(得分:-1)

以下代码可以解决这个问题:

searchFile(root).then(function (files) {
     function (files) {
         angular.forEach(files,
                         function (file) {
                             handleFile(file);
                         });
     });
});

// loop over all folders recursively, then handle files
function searchFile(item) {
    var deferred = $q.defer();
    var files = [];
    if (itIsFolder(item)) {
        $http.get("api.call.com/children/" + item.id)
                .then(function (children) {
                    angular.forEach(children,
                        function (child) {
                            searchFile(child).then(
                               function (files) {
                                   angular.forEach(files,
                                   function (file) {
                                       files.add(file);
                                   });
                               });
                        });

                        deferred.resolve(files);
                });
    }
    else {
        files.add(item);
        deferred.resolve(files);
    }

    return deferred.promise;
}