使用生成器异步调用列表中的每一项

Asynchronous call for every item of list with generator

我不知道如何恰当地写这个问题的标题,但我会解释我的问题:

假设有一个包含一些数据的列表,例如:["first", "second", "third"]

还有一个 AJAX 调用对其参数做一些事情,例如:

function ajax(data){
  return new Promise(function (resolve, reject) {
    setTimeout(() => resolve(data+"1"), 2000)
  });
}

对于每个 AJAX 调用,您都需要一个 follow-up 操作,例如:

ajax(e).done(data => d.resolve(data+"2"));

现在我想对列表中的每个项目异步进行 AJAX 调用和 follow-up 操作,但想等待(非阻塞)直到每个项目完成.

对于解决方案,我想使用生成器和 co library

只有 运行 每个列表项的 AJAX 异步调用效果很好:

var consoleLine = "<p class=\"console-line\"></p>";

console = {
    log: function (text) {
        $("#console-log").append($(consoleLine).html(text));
    }
};

co(function*(){
  let res = yield ["first", "second", "third"].map(e => ajax(e));
  res.forEach((a, b, c) => console.log(a));
});

function ajax(data){
  return new Promise(function (resolve, reject) {
       setTimeout(() => resolve(data+"1"), 2000)
  });
}
.console-line
{
    font-family: monospace;
    margin: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>

<div id="console-log"></div>

但是 运行 它与 follow-up 操作不起作用:

var consoleLine = "<p class=\"console-line\"></p>";

console = {
    log: function (text) {
        $("#console-log").append($(consoleLine).html(text));
    }
};

co(function*(){
  let res = yield test(["first", "second", "third"]);
  res.forEach((a, b, c) => console.log(a));
});

function test(g) {
  return g.map(e => function(){
    let d = new $.Deferred();
    ajax(e).done(data => d.resolve(data+"2"));
   return d.promise();
  });
}

function ajax(data){
  return new Promise(function (resolve, reject) {
       setTimeout(() => resolve(data+"1"), 2000)
  });
}
.console-line
{
    font-family: monospace;
    margin: 2px;
}
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="console-log"></div>

这是为什么?如何让我的需求生效?

Now i want to make the AJAX call and the follow-up action asynchronously for every item of the list but want to wait (non blocking) until every item is finished.

听起来您想等待所有的承诺兑现。这就是 Promise.all() and $.when 的好处。

var data = ["first", "second", "third"];

function ajax(data){
  let d = new $.Deferred();
  setTimeout(() => d.resolve(data+"1"), 1000);
  return d.promise();
}

function followUpAction(data){
  console.log('following', data);
}

var promises = data.map(function(item) {
  return ajax(item).then(followUpAction);
});

$.when.apply($, promises).then(function() {
  console.log('all done');
});

我想使用发电机,因此 rodneyrehm 的回答不能解决我的需要。但他的回答确实帮助我解决了我的问题!

我现在不再自己创建和 return 一个新的 promise,而是使用 .then() 本身 return 一个 promise(自 jQuery 1.8) . 这就是我需要更改以使其正常工作的全部内容:

旧:

function test(g) {
  return g.map(e => function(){
    let d = new $.Deferred();
    ajax(e).done(data => d.resolve(data+"2"));
    return d.promise();
  });
}

新:

function test(g) {
      return g.map(e => ajax(e).then(data => data+"2"));
}

最终的解决方案看起来是这样的:

var consoleLine = "<p class=\"console-line\"></p>";

console = {
    log: function (text) {
        $("#console-log").append($(consoleLine).html(text));
    }
};

var list = ["first", "second", "third"];

co(function*(){
  let res = yield list.map(e => ajax(e).then(r => r+"2"));
  res.forEach((a, b, c) => console.log(a));
});

function ajax(data){
  return new Promise(function (resolve, reject) {
       setTimeout(() => resolve(data+"1"), 2000)
  });
}
.console-line
{
    font-family: monospace;
    margin: 2px;
}
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="console-log"></div>

感谢 rodneyrehm 指出我的解决方案。

这是你的问题:

return g.map(e => function(){
//             ^^ ^^^^^^^^^^
    let d = new $.Deferred();
    ajax(e).done(data => d.resolve(data+"2"));
    return d.promise();
});

这是一个返回函数表达式的箭头函数。你要么想要

return g.map(function(e) {

return g.map(e => {

让它工作,否则你只会得到一个函数数组(co 会以奇怪的方式处理它)。


是的,you definitely should use then 而不是 done + deferreds。

我不喜欢使用带有承诺的生成器。当 ES7 的 async await 出现时,我会重新考虑这一点。我相信这项工作可以通过使用 Array.prototype.reduce() 以功能方式很好地完成,如果它们依赖于它们在数组中的顺序并且需要一个接一个 运行。否则我会推荐 Array.prototype.map()Promise.all.

的组合

假设错误首先使用回调。

function promisify(fun){
  return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}

function async(data, callback){
  data.val+= " msec"                                  // do something with the data asynchronously
  setTimeout(_ => callback(false,data.val),data.dur); // we are expected to handle returned data in our callback
}

function myCallback(result){     // supposed to work in the asych function but now moved to then stage
  console.log(result);
  return result;
}

var      dataSet = [100, 200, 300],
promisifiedAsych = promisify(async),
 chainedPromises = dataSet.reduce((prom,data) => prom.then(_ => promisifiedAsych({val:data, dur:data*10}))
                                                     .then(myCallback), Promise.resolve());
chainedPromises.then(val => console.log("dataSet completed and returned", val))