【问题标题】:Javascript ES6 generator asyncJavascript ES6 生成器异步
【发布时间】:2015-08-08 20:57:52
【问题描述】:

我需要运行生成器异步(我需要在控制台 1、2、3、4、5 中得到结果,因为现在我有 4、1、2、3、5)有人可以帮助我吗?我需要运行任务并等待上一个任务完成后再运行下一个任务。我需要使用(如果可能:仅)生成器(或生成器 + 承诺?)

这是我的代码

/*jshint esnext: true */
function show(msg) {
  var _msg = msg;
  setTimeout(function() { console.log(_msg);}, 2000);
}

function show2(msg) {
  console.log(msg);
}

var stack = [];

// add some function to stack
stack.push(function() { show(1); });
stack.push(function() { show(2); });
stack.push(function() { show(3); });
stack.push(function() { show2(4); });
stack.push(function() { show(5); });

function* generator1() {
  for(var key of stack) {
    yield key();
  }
}
var gen = generator1();
gen.next();
gen.next();
gen.next();
gen.next();
gen.next();

【问题讨论】:

  • 首先尝试编写一个没有生成器的解决方案(仅使用回调或承诺)。然后我们可能会向您展示如何在该图中合并生成器 - 因为生成器本身并不是异步的。

标签: javascript generator ecmascript-6


【解决方案1】:

这可以完全使用生成器来完成。这是一种方法的示例,其中我们将.next() 移动到超时本身,以确保它不会提前发生。此外,生成器现在从堆栈中返回函数而不是执行它,因为您不能在生成器本身的执行过程中调用生成器上的.next()

值得注意的是,这可能不是我“在野外”这样做的方式;我会包括承诺。但是您问是否可以仅使用生成器来完成 - 答案是“是”。

function show(msg) {
  var _msg = msg;
  setTimeout(function() { 
      console.log(_msg);
      execute();
  }, 2000);
}

function show2(msg) {
  console.log(msg);
  execute();
}

var stack = [];

function execute() {
  var fn = gen.next().value;
  if (fn) fn();
}

// add some function to stack
stack.push(function() { show(1); });
stack.push(function() { show(2); });
stack.push(function() { show(3); });
stack.push(function() { show2(4); });
stack.push(function() { show(5); });

function* generator1() {
  for(var key of stack) {
    yield key;
  }
}
var gen = generator1();
execute();

http://jsfiddle.net/smmccrohan/k271gz7o/

【讨论】:

  • 回调之上的生成器,确切地说:-)
【解决方案2】:

有很多“任务运行”功能,你甚至可以自己写。但是你必须为此使用 Promises,而不是 setTimeout。这是一个简单的例子:

function delay (ms, val) {
  return new Promise(function (res) {
    setTimeout(res, ms || 1000, val || Math.random());
    });
  }

function* run () {
  yield delay();
  console.log(yield delay());
  yield delay();
  console.log('foo'); // sync calls anywhere in between
  console.log(yield delay());
  }

function async(gen){ "use strict";
    gen = gen();
    return Promise.resolve().then(function cont(a){
        var n = gen.next(a),
            v = Promise.resolve(n.value);
        if(n.done) return v; // a `return`
        return n.value.catch(gen.throw.bind(gen)).then(cont);
    });
};

async(run);

基本上,我们调用生成器的next方法,等待它完成,然后再次触发next方法,递归直到生成器停止。

Bluebird 有一个更可靠的功能,称为Promise.coroutine

Task.js:http://taskjs.org/为此专门提供了一个函数。

希望有帮助!

【讨论】:

    【解决方案3】:

    您需要一种方法来告诉您的函数何时完成。 Promise 是解决这个问题的好方法。

    我会尽可能地坚持你的原始代码:

    function show(msg) {
      return new Promise(function(resolve){
        var _msg = msg;
        setTimeout(function() { console.log(_msg); resolve(_msg);}, 2000);
      });
    }
    
    function show2(msg) {
      return new Promise(function(resolve){
        console.log(msg);
        resolve(msg);
      });
    }
    
    var stack = [];
    
    // add some function to stack
    stack.push(function() { return show(1); });
    stack.push(function() { return show(2); });
    stack.push(function() { return show(3); });
    stack.push(function() { return show2(4); });
    stack.push(function() { return show(5); });
    
    function* generator1() {
      for(var key of stack) {
        yield key();
      }
    }
    
    var gen = generator1();
    gen.next().value.then(function(){
      gen.next().value.then(function(){
         gen.next().value.then(function(){
            gen.next().value.then(function(){
               gen.next();
            });
        });
      });
    });
    

    当然它看起来很丑,并且可以改进。正如另一个答案中提到的,有任务运行器和流控制库,例如task.jsgen-runco

    使用co,最后一部分是:

    co(generator1); 
    

    【讨论】:

      猜你喜欢
      • 2016-04-14
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 2015-03-11
      • 2021-11-12
      • 2015-03-17
      • 1970-01-01
      相关资源
      最近更新 更多