Javascript 如何同时映射生成器结果?

Javascript 如何同时映射生成器结果?,javascript,ecmascript-6,Javascript,Ecmascript 6,如何在生成器中同时映射异步调用的结果 var generator = (function *() { var lotsOfThings = yield asyncTask.call(generator); var mappedResults = yield lotsOfThings.map(thing => { // fails with a syntax error unless you do a `for…of` loop, but also doesn’t run

如何在生成器中同时映射异步调用的结果

var generator = (function *() {
  var lotsOfThings = yield asyncTask.call(generator);

  var mappedResults = yield lotsOfThings.map(thing => {
    // fails with a syntax error unless you do a `for…of` loop, but also doesn’t run concurrently regardless
    return (yield asyncTask.call(generator, thing));
  });

  // then do something with mappedResults
})();

generator.next();

function asyncTask(…query) {
  somethingAsync(…query, (err, res) => this.next(res));
}
此外,即使在常规的
for…of
循环中,也不能同时运行每个
asyncTask
yield
将导致每个任务之间的暂停,实质上是发出一个同步的AJAX请求。理想情况下,您希望它能像承诺一样工作,如以下范例:

// these tasks will run concurrently (unlike the above example)
let promises = someThings.map(thing => {
  return new Promise((resolve, reject) => {
    somethingAsync((err, res) => {
      resolve(res);
    });
  });
});

Promise.all(promises).then(/* do stuff */);

promise方法可能会因为所有嵌套而变得棘手,但其好处是异步任务可以并发运行……虽然生成器看起来不错,但通过任务的循环不是并发的。有什么想法吗?

我想要的答案可以通过以下方式实现


edit:将
somethingsync
更改为不可
,然后
可以,就像在我的实际情况中一样,这是对尚未返回承诺的第三方API的调用

我试图在没有第三方库的情况下绘制类似的东西:

// Async runner
function async(generator){
  var process = function(result){       
    if (result.done) {
      return;
    }
    (Array.isArray(result.value) ? Promise.all(result.value) : result.value).then(function(value){
      process(sequence.next(value));
    });
  };

  var sequence = generator();
  var next = sequence.next();
  process(next);
};

// Generator function
var generator = function* () {
  var list = yield getList();
  console.log(list); // outputs [1, 2, 3, 4, 5]

  var details = yield list.map(p => getDetails(p));    
  console.log(details); // outputs [11, 12, 13, 14, 15]
}

// Your async requests go here
function fakeAsync(f) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      f(resolve);
    }, 500);
  });
}

function getList() {
  return fakeAsync(function(resolve) {
    resolve([1, 2, 3, 4, 5]);                  
  });
}

function getDetails(i) {
  return fakeAsync(function(resolve) {
    resolve(i + 10);                  
  });
}

async(generator);

这是您试图实现的吗?

生成器在本质上是迭代的,为什么您希望并行运行它们?从客户机的角度来看,Generator只是您调用next()或在循环中使用的东西。是的,您真的应该对异步任务使用承诺,而不是回调-您已经得到了这一承诺。所有这些都是免费的。然后使用一个“协同程序”库(也是许多promise LIB的一部分),它允许您使用它们运行生成器。不要自己写这段代码。@Bergi,谢谢。这可能超出了问题的范围,但您是否知道有一个现成的协同程序库可以提供生成器的语法糖分和承诺的并发性?我可能想要一只独角兽……不,协同程序库只提供语法糖,并发性是通过
Promise.all
实现的。当然,一些lib(如Q、when、Bluebird等)提供了这两个函数。另外,像
co
这样的大多数纯协同程序库能够等待
生成
ed集合(如数组),巧妙地抽象了回调计数器。@Bergi,谢谢,看起来我想要的功能是能够并行生成一个项集合,这似乎可以通过
co
(如果每个项目都是承诺).避免在您的
请求中使用
方法,though@Bergi是的,谢谢。在他的例子中,somethingAsync指的是,比如说,一个第三方API调用,它还没有返回承诺,所以我认为我可以安全地将它包装成一个,因为它需要返回一个承诺。是吗?是的,在这种情况下,它会,但你答案中的代码看起来不正确例如。
somethingAsync
似乎至少返回一个thenable(即,您不信任其实现的“promise”),因此您应该执行
return promise.resolve(somethingAsync(路由、令牌))
@Bergi-ah-shoot你说得对,是的,那是我的一个打字错误。在我的上下文中,somethingAsync实际上会接受回调,并且不可恢复。这个解决方案缺少错误处理。这就是为什么你应该只使用库的原因之一(这是polyfill
承诺所必需的。总之
)@Mikhail,这可能是我试图实现的。看起来基本的范例是,要让这些东西并行运行,你基本上需要服从一个承诺或一组承诺。我这么说对吗?@JoshBeam是的,你是对的。基本上,你做出的任何事情都必须被运行在g中的代码理解Generator。如果需要运行并行承诺,则生成它们并解决所有问题。@Bergi True。这不是一个可用于生产的代码,只是概念的说明。使用库是可以的,但更好的是理解它们是如何做的。
// Async runner
function async(generator){
  var process = function(result){       
    if (result.done) {
      return;
    }
    (Array.isArray(result.value) ? Promise.all(result.value) : result.value).then(function(value){
      process(sequence.next(value));
    });
  };

  var sequence = generator();
  var next = sequence.next();
  process(next);
};

// Generator function
var generator = function* () {
  var list = yield getList();
  console.log(list); // outputs [1, 2, 3, 4, 5]

  var details = yield list.map(p => getDetails(p));    
  console.log(details); // outputs [11, 12, 13, 14, 15]
}

// Your async requests go here
function fakeAsync(f) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      f(resolve);
    }, 500);
  });
}

function getList() {
  return fakeAsync(function(resolve) {
    resolve([1, 2, 3, 4, 5]);                  
  });
}

function getDetails(i) {
  return fakeAsync(function(resolve) {
    resolve(i + 10);                  
  });
}

async(generator);