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

Async HTTP(S) Traversal only using Node.js Promisses

我尝试遍历一堆 HAL/JSON 资源,这些资源模拟了通过 href 连接并通过 https 检索的项目树。 IE。一个 'item' 可能是一个树叶或另一个有其他树叶的分支。 las,我做对了。遍历将始终结束,即在完整递归完成之前,在我的代码示例的最后一条语句中点击 then() 回调。实际上,我只得到第一层儿童。

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

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

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

任何对这个问题的答案感兴趣的人,请继续阅读,因为我找到了一个解决方案没有承诺。我完全摆脱了支持回调的承诺。请注意,与原始代码相比,我做了一些更改,例如收集结果而不是在递归代码中将它们打印到标准输出,我删除了一些其他的绒毛。

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