Node.js 如何按顺序在Q中链接可变数量的承诺?

Node.js 如何按顺序在Q中链接可变数量的承诺?,node.js,promise,q,Node.js,Promise,Q,我见过;我的问题不同 如何按顺序进行可变数量的调用,每个调用异步返回? 该场景是一组HTTP请求,其数量和类型由第一个HTTP请求的结果决定 我想简单地做到这一点 我也看到过类似的情况: var q = require('q'), itemsToProcess = ["one", "two", "three", "four", "five"]; function getDeferredResult(prevResult) { return (function (someResul

我见过;我的问题不同

如何按顺序进行可变数量的调用,每个调用异步返回?
该场景是一组HTTP请求,其数量和类型由第一个HTTP请求的结果决定

我想简单地做到这一点

我也看到过类似的情况:

var q = require('q'),
    itemsToProcess =  ["one", "two", "three", "four", "five"];

function getDeferredResult(prevResult) {
  return (function (someResult) {
    var deferred = q.defer();
    // any async function (setTimeout for now will do, $.ajax() later)
    setTimeout(function () {
      var nextResult = (someResult || "Initial_Blank_Value ") + ".." + itemsToProcess[0];
      itemsToProcess = itemsToProcess.splice(1);
      console.log("tick", nextResult, "Array:", itemsToProcess);
      deferred.resolve(nextResult);
    }, 600);

    return deferred.promise;
  }(prevResult));
}

var chain = q.resolve("start");
for (var i = itemsToProcess.length; i > 0; i--) {
    chain = chain.then(getDeferredResult);
}

…但以这种方式循环使用ItemStopProcess似乎很尴尬。或者定义一个名为“loop”的新函数来抽象递归。什么方法更好?

我更喜欢这种方法:

var q = require('q'),
    itemsToProcess =  ["one", "two", "three", "four", "five"];

function getDeferredResult(a) {
  return (function (items) {
    var deferred;

    // end
    if (items.length === 0) {
      return q.resolve(true);
    }

    deferred = q.defer();

    // any async function (setTimeout for now will do, $.ajax() later)
    setTimeout(function () {
      var a = items[0];
      console.log(a);
      // pop one item off the array of workitems
      deferred.resolve(items.splice(1));
    }, 600);

    return deferred.promise.then(getDeferredResult);
  }(a));
}

q.resolve(itemsToProcess)
  .then(getDeferredResult);
这里的关键是调用
.then()
,使用拼接版本的工作项数组调用
deferred.promise
。该
然后
在初始延迟承诺解析后运行,该承诺位于setTimeout的fn中。在更现实的场景中,延迟承诺将在http客户端回调中得到解决

初始的
q.resolve(itemsToProcess)
通过将工作项传递给工作fn的第一个调用来启动工作


我添加这个是希望它能帮助其他人。

这里是一个用
Q
定义的状态机概念

假设您定义了HTTP函数,因此它返回一个
Q
promise对象:

var Q_http = function (url, options) {
  return Q.when($.ajax(url, options));
}
您可以定义递归函数
nextState
,如下所示:

var states = [...]; // an array of states in the system.

// this is a state machine to control what url to get data from
// at the current state 
function nextState(current) {
  if (is_terminal_state(current))
    return Q(true);

  return Q_http(current.url, current.data).then(function (result) {
    var next = process(current, result);
    return nextState(next);
  });
}
其中,
function process(current,result)
是根据HTTP调用的
current
状态和
result
找出下一步的函数

使用时,请像以下那样使用:

nextState(initial).then(function () {
  // all requests are successful.
}, function (reason) {
  // for some unexpected reason the request sequence fails in the middle.
});

有一个很好的干净的方法来解决这个问题

遍历数组,传入上一次迭代的返回值。在这种情况下,您将返回承诺,因此每次链接
时,都会返回
。您提供了一个初始承诺(就像您在
q.resolve(“开始”)
中所做的那样)来启动事情


起初,你可能需要一段时间来了解这里正在发生的事情,但如果你花一点时间来了解它,那么这是一种易于在任何地方使用的模式,而不必设置任何机器。

我提出了另一种解决方案,这看起来更容易理解。 当直接链接承诺时,您将执行相同的操作:
promise.then(doSomethingFunction).then(doNothingFunction)

如果我们将其放入一个循环中,我们得到:

var chain = Q.when();
for(...) {
  chain = chain.then(functionToCall.bind(this, arg1, arg2));
};
chain.then(function() {
    console.log("whole chain resolved");
});


var functionToCall = function(arg1, arg2, resultFromPreviousPromise) {
}
我们使用多个参数。在我们的例子中
functionToCall.bind(this,arg1,arg2)
将返回一个带有一个参数的函数:
functionToCall(resultFromPreviousPromise)

您不需要使用前面承诺的结果。

按照第一个示例,在循环中构建
.then()
链可能比递归更正常。无论您选择哪种方式,不要破坏原始阵列可能很重要。如果是这样,那么您可以让承诺(及其回调)使用在原始未受污染数组上运行的递增索引值。sweet!而且非常方便!如果承诺被拒绝,会发生什么?或者,至少,我们应该如何处理这种情况?@naivedeveloper如果一个步骤被拒绝,那么下面的步骤将不会被执行,
chain
也将被拒绝,因此您可以使用
chain.catch
来捕捉这种情况。非常好的解决方案。谢谢这是完美的,融化了我的大脑,直到我看到它运行。这对我来说比其他while循环解决方案更有意义。它也更容易适应其他Promise库。这似乎不是一个可变的Promise数,而是一个取决于数组长度的集合数。有些情况下,您实际上有一个可变的数字,我不确定
reduce
方法是否适用于这些情况。将作为官方参考
var chain = Q.when();
for(...) {
  chain = chain.then(functionToCall.bind(this, arg1, arg2));
};
chain.then(function() {
    console.log("whole chain resolved");
});


var functionToCall = function(arg1, arg2, resultFromPreviousPromise) {
}