如何使用angularjs $ q顺序链接promise?

时间:2014-09-06 21:06:56

标签: angularjs q chaining sequential angular-promise

在promise库 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;

7 个答案:

答案 0 :(得分:36)

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。如果没有必要通过`结果,那么就把它留下来。
  • 根据您使用的保证库来适应$q.when(initialValue)
  • 在你的情况下,doSomethingAsyncfoo(或者foo()返回什么?) - 无论如何,是一个函数。

如果你像我一样,那么乍一看,这种模式看起来就像一个难以理解的污垢,但是一旦你的眼睛变得协调,你就会开始认为它是一个老朋友。

修改

这是一个demo,旨在证明上面推荐的模式实际上是按顺序执行其doSomethingAsync()调用,而不是在构建链时立即执行,如下面的评论所示。

答案 1 :(得分:28)

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

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

注意:foo必须是工厂,例如

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;

答案 2 :(得分:4)

或许比redgeoff's answer更简单,如果您不需要自动化,则可以使用$q.when().then()结合使用来链接承诺,如 return $q.when() .then(function(){ return promise1; }) .then(function(){ return promise2; }); 所示{3}}。 URI uri = new URIBuilder().setScheme("https") .setHost("somehost.com") .setPath("/API/v1/export").build(); HttpPost post = new HttpPost(uri); post.setHeader("X-API-ID", "myId"); post.setHeader("Accept", "application/json"); List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("format", "csv")); params.add(new BasicNameValuePair("userId", "userId")); post.setEntity(new UrlEncodedFormEntity(params)); JsonNode responseJson = sendResponseEngineRequest(post);

答案 3 :(得分:4)

有这个:

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

一行(好,3为可读性):

return items
    .map(item => foo.bind(null, item))
    .reduce($q.when, $q.resolve());

答案 4 :(得分:4)

var when = $q.when();

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

    })(i); 
}

答案 5 :(得分:2)

我更喜欢使用angular.bind(或Function.prototype.bind)准备将返回承诺的函数,然后使用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)

答案 6 :(得分:1)

你的答案是对的。但是,我认为我提供了另一种选择。如果你发现自己经常串联链接承诺,你可能会对$ 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);
    });
  };
}