简体   繁体   English

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

[英]How do you map generator results concurrently?

How can you map the results of async calls in a generator, concurrently?如何同时映射生成器中异步调用的结果?

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));
}

Also, even in a regular for...of loop, you can't run each asyncTask concurrently.此外,即使在常规的for...of循环中,您也不能同时运行每个asyncTask yield will cause a pause between each task, essentially making a synchronous AJAX request. yield将导致每个任务之间的暂停,本质上是一个同步的 AJAX 请求。 Ideally you'd want it to work like it does with promises, like this paradigm:理想情况下,您希望它像使用 Promise 一样工作,例如以下范例:

// 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 */);

The promise approach can get hairy 'cause of all the nesting, but the benefit is that the async tasks can run concurrently… whereas the generators look nice, but looping through tasks is not concurrent.由于所有的嵌套,promise 方法可能会变得很麻烦,但好处是异步任务可以并发运行……而生成器看起来不错,但任务循环不是并发的。 Any ideas?有任何想法吗?

I tried to sketch something similar without third-party libraries:我试图在没有第三方库的情况下绘制类似的草图:

// 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);

Is it what you were trying to achieve?这是你想要达到的目标吗?

The answer I was looking for can be implemented with co .我正在寻找的答案可以用co实现。

co(function* () {
  // maybe we need a token from our API before we can do anything
  let token = yield new Promise((resolve, reject) {
    getToken(token => resolve(token));
  });

  // these run in parallel
  var queries = yield [
    request('/route', token),
    request('/another-route', token)
  ];

  // [[], []] becomes [...]
  return _.flatten(queries);
});

// our async request function returns a promise
function request(route, token) {
  return new Promise((resolve, reject) => {
    somethingAsync(route, token, (res) => {
      resolve(res);
    });
  });
}

edit : changed somethingAsync to not be then able, as in my actual case, it's a call to a 3rd party API that does not already return a promise.编辑:改变somethingAsync到不then能干,在我的实际情况,这是一个第三方的API,它已经不返回一个承诺的电话。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM