使用生成器对列表的每个项进行异步调用

时间:2016-08-21 10:07:56

标签: javascript asynchronous promise generator yield

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

想象一下,列表中包含一些数据,例如:["first", "second", "third"]

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

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

对于每个AJAX通话,您需要采取后续行动,例如:

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

现在我想为列表中的每个项目异步进行AJAX调用和后续操作,但是想等待(非阻塞)直到每个项目都完成。

对于解决方案,我想使用生成器和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>

但是使用后续操作运行它不起作用:

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>

为什么?如何让我的工作要求?

4 个答案:

答案 0 :(得分:1)

  

现在我想对列表中的每个项目异步进行AJAX调用和后续操作,但是想要等待(非阻塞)直到每个项目都完成。

听起来你想要等待所有的承诺。这是Promise.all()$.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');
});

答案 1 :(得分:1)

这是你的问题:

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 +延迟。

答案 2 :(得分:0)

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

我现在只使用自己确实返回承诺的.then()而不是创建并返回我自己的新承诺(自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"));
}

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

&#13;
&#13;
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)
  });
}
&#13;
.console-line
{
    font-family: monospace;
    margin: 2px;
}
&#13;
<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>
&#13;
&#13;
&#13;

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

答案 3 :(得分:0)

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

假设使用了第一个回调错误。

&#13;
&#13;
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))
&#13;
&#13;
&#13;