【问题标题】:Generator function with yield Promise.all()带有 yield Promise.all() 的生成器函数
【发布时间】:2017-08-29 08:53:31
【问题描述】:

我想分块运行一个函数,所以它会等待 10k 个 promise 解决然后继续,我使用以下生成器函数:

  function* processNodes(nodes, task){
    let i;
    let cnt = 0;
    let promiseArray = new Array(10000);
    let pInd = 0;
    let currId;

    for(i = 0; i<nodes.length; i++){
      currId = nodes[i];
      promiseArray[pInd++] = asyncFunc(currId, 0, task); // return a promise
      cnt++;

      if(cnt > 10000){
        console.log("going to yield", promiseArray)
        let pall = Promise.all(promiseArray);
        console.log("promise all", pall);
        yield pall;
        console.log("return from yield");  // never get here
        pInd = cnt = 0;
      }
    }
  }

但即使我看到pall 已解决,它也永远不会从产量中返回。

可以用生成器函数做这样的事情吗?

编辑: 我想我想做的是实现像 Bluebird 的协程这样的东西:http://bluebirdjs.com/docs/api/promise.coroutine.html

Edit2:这就是我调用这个函数的方式:

let x = processNodes(allNodes, someSimpleTask);
x.next();

【问题讨论】:

  • 你能展示你测试这个函数的代码吗? yield 将暂停函数,直到调用者使用该值。
  • 这个:console.log("return from yield"); // never get here@trincot
  • 我指的不是那个代码,而是调用函数的主要代码。
  • 这里太大了,无法包含...@trincot
  • 我不确定我是否理解您的用例:首先,您创建了 10'000 个承诺。然后你yield,首先是 10'000 个承诺,然后是 10'001、10'002 等等……

标签: javascript asynchronous ecmascript-6 generator


【解决方案1】:

您的问题是您没有按应有的方式使用生成器功能。您永远不会到达console.log('return from field'),因为在yield,代码在yield 语句之后停止执行。只有当你再次调用迭代器时,才会在yield语句之后继续(直到下一个yield语句)

所以生成器创建了一个迭代器,它有一个value 和一个布尔标志done。只要 done 没有设置为 true,你可以/应该再次调用下一个函数

您的代码的简化版本如下

// a very basic async function, just outputting the argument each 5 ms
function asyncFunc(arg) {
  return new Promise(function(resolve) {
    setTimeout(() => {
      console.log(arg);
      resolve();
    }, 5);
  });
}

// the generator
function* generator(processNodes, task) {
  var limit = 4,
    queue = [];
  for (let i = 0; i < processNodes.length; i++) {
    queue.push(task(processNodes[i]));
    if (queue.length >= limit) {
      yield Promise.all(queue);
      // clears the queue after pushing
      console.log('after queue');
      queue = [];
    }
  }
  // make sure the receiver gets the full queue :)
  if (queue.length !== 0) {
    yield Promise.all(queue);
  }
}

function runThroughArguments(args, task) {
  return new Promise(function(resolve) {
    setTimeout(() => {
      var nodes = generator(args, task),
        iterator = nodes.next();

      if (!iterator.done) {
        // if it's not done, we have to recall the functionallity
        iterator.value.then(function q() {
          setTimeout(() => {
            iterator = nodes.next();
            if (!iterator.done && iterator.value) {
              // call the named function (in this case called q) which is this function after the promise.all([]) completed
              iterator.value.then(q);
            } else {
              // everything finished and all promises are through
              resolve();
            }
          }, 2);
        });
      } else {
        iterator.value.then(resolve);
      }
    }, 2);
  });
}

runThroughArguments(
  ['hey', 'you', 'the', 'rock', 'steady', 'crew'], 
  asyncFunc).then(() => console.log('completed'));

console.log('runs before everything');

在上面的 sn-p 中,它也是通过一个 Promise 运行的。因此,当整个队列通过时,您会收到通知,它比原始的 sn-p 要复杂一些,可以找到here

您可以在 MDN 上找到对您所使用模式的更易于理解的解释

【讨论】:

    猜你喜欢
    • 2021-09-05
    • 2011-11-02
    • 2016-04-10
    • 2017-07-07
    • 1970-01-01
    • 2021-01-16
    • 1970-01-01
    • 2017-07-07
    • 2016-03-19
    相关资源
    最近更新 更多