异步HTTP(S)遍历仅使用Node.js Promisses

时间:2016-06-24 10:00:10

标签: node.js recursion promise traversal tree-traversal

我尝试遍历一堆HAL / JSON资源,这些资源模拟通过hrefs连接并通过https检索的项目树。即“项目”可以是休假或其他分支与其他分支。 唉,我做不到。遍历将始终结束,即在完全递归完成之前,在我的代码示例的最后一个语句中命中then()回调。实际上,我只在儿童中获得了第一层。

我的问题:我错过了什么才能做到正确?

我还在学习Nodejs,但我在前面的例子中成功使用了promises。我在这里问这个问题,因为我不得不使用模块https和集成Nodejs的东西。其他示例通常使用其他模块 和/或没有解决我的问题。

var traverse = function(rootItemUrl, depth, children) {
    var deferred = Promise.defer();

    var itemUrl = rootItemUrl;
    var options = {
        'path'      : itemUrl
        , 'host'      : "<host>"
        , 'method'  : 'GET'
        , 'headers' : {
            'Content-Type'  : 'application/json'
            , 'Accept'      : 'application/json'
            , 'Forwarded'   : 'proto=https;host=<host>'
            , 'Cookie'      : options_.headers['Cookie']
        }
    };

    https.get(options, onItemResultResponse);
    function onItemResultResponse(itemResultResponse) {
        var body = [];
        itemResultResponse.on('data', function onDataChunk(data) {
            body.push(data);
        });
        itemResultResponse.on('end', onItemResultData);
        itemResultResponse.on('error', onRequestItemsError);
        function onRequestItemsError(e) {
            console.log('Get items failed for <'+rootItemUrl+'>.');
            deferred.reject();
        }
        function onItemResultData() {
            var items = [];
            var itemResult = JSON.parse(Buffer.concat(body).toString());
            var embedded = itemResult._embedded;
            var collection = embedded ? embedded['collection'] : undefined;
            if(collection) {
                var itemsObject = collection._links['item'];
                if(itemsObject) {
                    if(itemsObject.length) {
                        for(var i = 0; i < itemsObject.length; ++i) {
                            items.push(itemsObject[i].href);
                        }
                    } else {
                        items.push(itemsObject.href);
                    }
                }
            }

            var type = itemResult.base.type;
            var name = itemResult.common.name;

            var text = repeatChar(depth, '\t') + ('folder' === type ? '- (folder) ' : '')+ 'depth: '+depth+' '+name;
            children.push(text);
            //console.log(text);

            if(items.length) {
                for (var j = 0; j < items.length; ++j) {
                    traverse(items[j], depth + 1, children)
                        .then(function() {deferred.resolve(depth);});
                }
            } else {
                deferred.resolve(depth);
            }
        }
    }

    return deferred.promise;
};

var children = [];
traverse(rootItemUrl, 0, children)
                        .then(function toConsole(depth) {
                            // >> Alas I hit this point too early <<
                            console.log(children);
                            console.log('End');
                        });

1 个答案:

答案 0 :(得分:0)

对于对此答案感兴趣的人,请继续阅读,因为我找到了一个没有承诺的解决方案。我完全摆脱了有利于回调的承诺。请注意,与原始代码相比,我做了一些更改,例如收集结果而不是在递归代码中将它们打印到stdout,我删除了一些其他的漏洞。

var traverse = function(options, rootItemUrl, depth, done) {
    var results = [];

    options.path = rootItemUrl;
    https.get(options, onItemResultResponse);
    function onItemResultResponse(itemResultResponse) {
        var body = [];
        itemResultResponse.on('data', function onDataChunk(data) {
            body.push(data);
        });
        itemResultResponse.on('end', onItemResultData);
        itemResultResponse.on('error', onRequestItemsError);
        function onRequestItemsError(e) {
            console.log('Get items failed for <' + rootItemUrl + '>.');
            done(e);
        }
        function onItemResultData() {
            var items = [];
            var itemResult = JSON.parse(Buffer.concat(body).toString());

            var embedded = itemResult._embedded;
            results.push(toItemInfo(itemResult, depth));
            var collection = embedded ? embedded['collection'] : undefined;
            var embeddedItems = collection ? collection._embedded : undefined;
            if (embeddedItems) {
                var itemsObject = embeddedItems['item'];
                if (itemsObject) {
                    if (itemsObject.length) {
                        for (var i = 0; i < itemsObject.length; ++i) {
                            items.push(itemsObject[i]);
                        }
                    } else {
                        items.push(itemsObject);
                    }
                }

                var itemInfos = new Array(items.length);
                for (var iii = 0; iii < items.length; ++iii) {
                    itemInfos[iii] = toItemInfo(items[iii], depth + 1);
                }

                var ii = 0;
                (function next() {
                    var nextItemInfo = itemInfos[ii++];
                    if (!nextItemInfo) {
                        return done(null, results);
                    }
                    if ('folder' === nextItemInfo.type) {
                        traverse(options, nextItemInfo, depth + 1, function done(err, result) {
                            results = results.concat(result);
                            next();
                        });
                    } else {
                        results.push(nextItemInfo);
                        next();
                    }
                })();
            }
        }
    }
};
traverse(options, rootItemUrl, 0, function done(e, results) {
   var text = '';
   for(var ii = 0; ii < results.length; ++ii) {
        var itemInfo = results[ii];
        text += repeatChar(itemInfo.depth, '\t') + ('folder' === itemInfo.type ? '- (folder) ' : '') + 'depth: ' + itemInfo.depth + ' ' + itemInfo.name;            
    }
    console.log(text);
    console.log("End");
});