2014-09-06 88 views
12

在承诺库Q,你可以做以下顺序链的承诺:

var items = ['one', 'two', 'three']; 
var chain = Q(); 
items.forEach(function (el) { 
    chain = chain.then(foo(el)); 
}); 
return chain; 

但是,以下不$ Q工作:

var items = ['one', 'two', 'three']; 
var chain = $q(); 
items.forEach(function (el) { 
    chain = chain.then(foo(el)); 
}); 
return chain; 

回答

29

只需使用$ q.when()函数:

var items = ['one', 'two', 'three']; 
var chain = $q.when(); 
items.forEach(function (el) { 
    chain = chain.then(foo(el)); 
}); 
return chain; 

注意:foo m最好是工厂,例如

function setTimeoutPromise(ms) { 
    var defer = $q.defer(); 
    setTimeout(defer.resolve, ms); 
    return defer.promise; 
} 

function foo(item, ms) { 
    return function() { 
    return setTimeoutPromise(ms).then(function() { 
     console.log(item); 
    }); 
    }; 
} 

var items = ['one', 'two', 'three']; 
var chain = $q.when(); 
items.forEach(function (el, i) { 
    chain = chain.then(foo(el, (items.length - i)*1000)); 
}); 
return chain; 
+3

这是行不通的。它同时执行它们全部。我知道这一点,因为我执行一系列需要大约500个MS的请求。看着我的网络流量,他们都同时出去(但是按顺序)。 – FlavorScape 2015-03-19 23:45:03

+0

@FlavorScape,确保foo是一个工厂 - 查看我的编辑。 – redgeoff 2015-03-20 00:21:00

+1

啊,确定它是一个工厂,这样它就不会立即在调用堆栈中执行,当我们构建链时,对吧? – FlavorScape 2015-03-20 00:50:44

28

Redgeoff,你自己的答案是我用于转化为数组链一系列承诺的方式。

紧急事实上模式如下:

function doAsyncSeries(arr) { 
    return arr.reduce(function (promise, item) { 
     return promise.then(function(result) { 
     return doSomethingAsync(result, item); 
     }); 
    }, $q.when(initialValue)); 
} 

//then 
var items = ['x', 'y', 'z']; 
doAsyncSeries(items).then(...); 

注:

  • .reduce是原始的JavaScript,而不是一个库的一部分。
  • result是以前的异步结果/数据,为了完整而包括在内。初始resultinitialValue。如果没有必要传递结果,那就简单地将它排除在外。
  • adapt $q.when(initialValue)取决于您使用哪个promise lib。
  • 在你的情况下,doSomethingAsyncfoo(或什么foo()返回?) - 在任何情况下,一个函数。

如果你和我一样,那么这个图案看起来就像一个无法穿透的衣服,但是一旦你的眼睛变得适合,你就会开始认为它是一个老朋友。

编辑

这里有一个demo,旨在证明,上述推荐的模式其实不按顺序执行其doSomethingAsync()电话,没有立即在构建链在下面的意见建议。

+1

原始答案在语法上不正确。我修正了这一点。另外,initialValue应该设置为什么?就像上面的答案一样,这将会同时激发所有人。 – FlavorScape 2015-03-19 23:54:09

+0

@FlavorScape,很好的捕捉。很高兴知道有人在那里检查这些东西。 – 2015-03-20 08:57:07

+0

'initialValue'在'reduce()'循环的第一次迭代中显示为'result'。它的价值取决于应用程序。如果'doSomethingAsync()'不需要传递给它的前一个结果,那么简化初始化器将简化为'$ q.when()' – 2015-03-20 09:06:17

2

我更喜欢使用angular.bind(或Function.prototype.bind)返回promise的函数,然后使用reduce快捷方式将它们链接到一个链中。例如

// getNumber resolves with given number 
var get2 = getNumber.bind(null, 2); 
var get3 = getNumber.bind(null, 3); 
[get2, get3].reduce(function (chain, fn) { 
    return chain.then(fn); 
}, $q.when()) 
.then(function (value) { 
    console.log('chain value =', value); 
}).done(); 
// prints 3 (the last value) 
+0

您可否在这种情况下详细说明'angular.bind'的好处? – pulkitsinghal 2015-06-17 16:01:44

+0

我使用绑定来创建不需要任何参数的promise返回函数。因此,它们可以像'.then(foo).then(bar)'那样使用。' – 2015-06-18 16:18:21

2

你的回答是正确的。不过,我想我会提供一个替代方案。如果您发现自己经常连续承诺承诺,您可能对$q.serial感兴趣。

var items = ['one', 'two', 'three']; 
var tasks = items.map(function (el) { 
    return function() { foo(el, (items.length - i)*1000)); }); 
}); 

$q.serial(tasks); 

function setTimeoutPromise(ms) { 
    var defer = $q.defer(); 
    setTimeout(defer.resolve, ms); 
    return defer.promise; 
} 

function foo(item, ms) { 
    return function() { 
    return setTimeoutPromise(ms).then(function() { 
     console.log(item); 
    }); 
    }; 
} 
+1

这看起来很有趣,我将在今天为我的平板写入百万个单元测试后尝试重构... – FlavorScape 2015-04-06 18:47:11

+0

这绝对是一个更简单因为稍后阅读代码更容易。 – supersan 2017-02-18 16:06:56

2

在也许比redgeoff's answer方式简单,如果你不需要它的自动化,如在this post开始你可以链的承诺使用$q.when().then()结合。 return $q.when() .then(function(){ return promise1; }) .then(function(){ return promise2; });

3

更改此:

var items = ['one', 'two', 'three']; 

这样:

var items = [ 
    foo.bind(null, 'one'), 
    foo.bind(null, 'two'), 
    foo.bind(null, 'three') 
]; 

然后一行:

return items.reduce($q.when, $q.resolve()); 
1
var when = $q.when(); 

for(var i = 0; i < 10; i++){ 
    (function() { 
     chain = when.then(function() { 
     return $http.get('/data'); 
     }); 

    })(i); 
}