6

I need to run generator async (I need to have result in console 1,2,3,4,5 cause now I have 4,1,2,3,5) any one can help me? I need run task and wait when previous task is finished before it run next task. I need to use (if possible: only) generators (or generator + promise?)

Here my code

/*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();
1
  • Try to write a solution without generators (using only callbacks or promises) first. Then we might be able to show you how to incorporate generators in that picture - because on their own, generators are not asynchronous. Commented May 27, 2015 at 3:04

3 Answers 3

4

This can be done purely with a generator. Here's an example of one approach, in which we move the .next() into the timeout itself in order to ensure it doesn't occur early. Additionally, the generator now returns the function off the stack instead of executing it, because you can't call .next() on a generator from within the execution of the generator itself.

It's worth noting here that this probably isn't the way I'd do this 'in the wild'; I'd include promises. But you asked if it could be done with just a generator - the answer is 'yes'.

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/

Sign up to request clarification or add additional context in comments.

1 Comment

generators on top of callbacks, to be precise :-)
2

There are many "task running" functions for this, you can even write your own. But you will have to use Promises for this, and not setTimeout. Here is a quick example:

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

Basically, we call the next method of the generator, wait for it to complete, and then fire the next method again, and recurse until generator halts.

Bluebird has a more fault-proof function called Promise.coroutine.

Task.js: http://taskjs.org/ provides a function specially for this.

Hope that helps!

Comments

1

You need a way for your functions to tell when they are finished. Promises are a good way to solve that problem.

I will stick to your original code as much as I can:

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

Of course it looks ugly, and can be improved. As mentioned in the other answer, there are task runners and flow control libs, such as task.js , gen-run, and co.

With co, the last part would be:

co(generator1); 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.