如何在Q中链接可变数量的promise,依次?

在Q中 ,我已经看到Chaining中的任意数量的承诺 ; 我的问题是不同的。

我怎样才能进行可变数量的调用,每个调用都是asynchronous返回的?
场景是一组HTTP请求,其数量和types由第一个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); } 

…但以这种方式循环遍历itemsToProcess似乎很尴尬。 或者定义一个叫做“循环”的新函数来抽象recursion。 什么是更好的方法?

[].reduce有一个很好的干净的方法。

 var chain = itemsToProcess.reduce(function (previous, item) { return previous.then(function (previousValue) { // do what you want with previous value // return your async operation return Q.delay(100); }) }, Q.resolve(/* set the first "previousValue" here */)); chain.then(function (lastResult) { // ... }); 

reduce对数组的迭代,传递前一次迭代的返回值。 在这种情况下,你要回诺言,所以每次你链接的then 。 你提供了一个初始的承诺(就像你使用q.resolve("start") )来解决问题一样。

起初可能需要一段时间才能把头发缠绕在这里,但是如果你花一点时间来解决这个问题,那么在任何地方使用它都是一个简单的模式,而不需要设置任何机器。

我更喜欢这种方式:

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

这里的关键是使用工作项数组的拼接版本在deferred.promise上调用.then()then在初始的延迟承诺parsing之后运行,这在setTimeout的fn中。 在更现实的情况下,延迟承诺将在http客户端callback中得到解决。

最初的q.resolve(itemsToProcess)通过将工作项目传递给工作fn的第一个调用来解决问题。

我希望这会帮助别人。

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

假设你定义了HTTP函数,所以它返回一个Q promise对象:

 var Q_http = function (url, options) { return Q.when($.ajax(url, options)); } 

你可以定义一个recursion函数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)是根据current状态和HTTP调用result找出下一步是什么的函数。

当你使用它时,像这样使用它:

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

我提出了另一个解决scheme,这对我来说更容易理解。 你可以像直接链接诺言一样做: promise.then(doSomethingFunction).then(doAnotherThingFunction);

如果我们把它放到循环中,我们得到这个:

 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) { } 

我们使用函数currying来使用多个参数。 在我们的例子中, functionToCall.bind(this, arg1, arg2)将返回一个带有一个参数的functionToCall(resultFromPreviousPromise)functionToCall(resultFromPreviousPromise)你不需要使用前一个promise的结果。