nodejs Q.all 承诺函数调用自身

nodejs Q.all promises on function calling itself

我需要发出请求以获取 html 的列表,我需要对其进行扫描并循环遍历它,并对找到的列表中的每个项目发出更多请求,这些列表中可能包含列表他们,依此类推,直到剩下none。

我需要一种方法来跟踪所有调用的请求并在它们完成时调用另一个函数。棘手的一点是函数会为在 HTML.

中找到的任何列表项一遍又一遍地调用自身

我遇到的问题是使用 Q promises 它等待的唯一 promises 来自第一个请求,我不明白为什么假设节点像我想的那样工作,请参见代码:

var _ = require('underscore');
var request = require('request');

var allPromises = [];
var finalArray = [];

var start = function(id) {

  var deferred = Q.defer(); 

  request.get({
    url: 'http://www.example.com/id/' + id
  }, function() {

    _.each(body.items, function(index) {

      var item = this;

      finalArray.push(item);

      if(item.hasMore) {
        start(item.id);
      }
    }

    deferred.resolve();

  });

  allPromises.push(deferred.promise);

}

console.log('Starting');

start(1);

Q.all(allPromises).done(function (values) {
  console.log('All Done');
});

我认为发生的事情是:

1 - 第一次调用 starts() 并创建第一个延迟变量
2 - 发出第一个请求并将第一个创建的延迟变量推送到 promises 数组
3 - Q.all 被调用并等待

4 - 调用第一个请求的回调 5 - 如果请求包含 body.x,将使用新的 id
再次调用 start() 6 - 创建并推送新的承诺并发出新的请求
7 - 第一个承诺已解决

假设这只深入了一层

8 - 第二个承诺已解决
9 - Q.all 调用其回调

但实际上,Q.all 在第一个承诺之后调用它的回调,它不会等待任何其他承诺,即使第二个承诺在第一个承诺得到解决之前被推送。

为什么?我怎样才能做到这一点?

更新 忘记在请求回调中添加循环。

已编辑问题的答案:

var request = require('request');

var finalArray = [];

var start = function(id) {

    var deferred = Q.defer();

    request.get({
        url: 'http://www.example.com/id/' + id
    }, function() {

        var subitems = [];
        _.each(body.items, function(index) {

            var item = this;

            finalArray.push(item);

            if(item.hasMore) {
                subitems.push(start(item.id));
            }
        }
        if (subitems.length) {
            deferred.resolve(Q.all(subitems)); // resolve result of Q.all
        } else {
            deferred.resolve();
        }
    });
    return deferred.promise;
}

start(1).done(function() {
    console.log('All Done');
});

@Bergi 的代码

var request = require('request');

var start = function(id) {

    var deferred = Q.defer();

    request.get({
        url: 'http://www.example.com/id/' + id
    }, function(err, body) {
        if (err) deferred.reject(err);
        else deferred.resolve(body);
    });
    return deferred.promise.then(function(body) {
        var finalArray = [];
        return Q.all(_.map(body.items, function(index) {
            var item = this;
            finalArray.push(item);
            if(item.hasMore)
                return start(item.id);
            else
                return [];
        })).then(function(moreResults) {
            return finalArray.concat.apply(finalArray, moreResults);
        });
    });
}

start(1).then(function(finalArray) {
    console.log('All Done');
});