3

I was experimenting with generators for a bit. When I tried this piece of code I got unexpected errors:

Uncaught TypeError: Method [Generator].prototype.next called on incompatible receiver # or TypeError: CallStarGeneratorMethodIfWrapped method called on incompatible HTMLButtonElement

My question, does it not work ? What is the meaning behind those error messages ? And most importantly; Why is first().next not handled as a normal function ? Why does the addEventListener care about the origins of the first().next function. Type first().next in the console. It says function. Below is out commented a similar function to the next except it produces always the same result.

The code, that you can try to reproduce:

<html>
<button id="test">Click</button>
<script>

var first = function* (){
    console.log("first click");
    yield true;
    console.log("second click");
    yield true;
};

document.getElementById("test").addEventListener('click', first().next);

/*var wouldwork = function (){
    console.log("would work");
    return { value: true, done: false };
    // same as the next returns
};*/
/*document.getElementById("test").addEventListener('click', wouldwork);*/
</script>
</html>

Another option would be to put next with the correct context in another function. To do that we store the iterator in a variable.

var iterator = first();

document.getElementById("test").addEventListener('click',

    function (event){return iterator.next();}

    //context of next is always the correct one

);

If that happens more often it can be a good idea to create a new function named createNext that returns a next function in a more pure functional style

var createNext = function (generatorFunction){

    var iterator = generatorFunction();

    return function() {

        return iterator.next();

    };

};



document.getElementById("test").addEventListener('click',

    createNext(first)

);
Walle Cyril
  • 3,087
  • 4
  • 23
  • 55
  • Probably because the `this` keyword is internally used in both functions but it has a different meaning in the event listener function (button element) and the `next` function of the generator (the generator itself, perhaps). – Sebastian Simon Oct 21 '15 at 23:24
  • `() => first().next()` – zerkms Oct 21 '15 at 23:36
  • 2
    Indeed it has to do with `this`, as `var f = first(); ...addEventListener('click', f.next.bind(f));` works. – Jon Oct 21 '15 at 23:36
  • @zerkms: You don't want to call `first()` again – Bergi Oct 21 '15 at 23:43

1 Answers1

2

jsFiddle Demo

The way that event listen works is that it will call the function handle using call and assign the this binding from its execution context to the called function handle. So this is going to be bound as the context for next being called. That is why it doesn't work as written.

Next, there is the issue of actually getting the function from your generator function for iteration. "Calling a generator function does not execute its body immediately; an iterator object for the function is returned instead.". This means that yes, first().next is a function object, but it isn't the handle you want to pass. You simply want to use the iterator function itself, but that would be first(), so how does that work if you want it to call next each time?

An option here is simply to take your generator function and pass the iterator function in later as a binding. In order to maintain the iterator function's binding inside of .next, you could do this:

document.getElementById("test").addEventListener('click', first().next.bind(first()));

This will bind the iterator function for first() to the iterator function's .next function. Kind of messy. The first call to first() exposes the iterator function and then accesses its next function, the second call simply binds the next function's this to the iterator function which is otherwise overwritten when the eventListener overrides the this binding using call.

You can read more in general about these generator functions and their iterators here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*

Travis J
  • 81,153
  • 41
  • 202
  • 273
  • how to make put action in saga using addEventListener? When I do it with function call is working (yield someFunc()) but when I write yield put(action()) is not( – HackerMF May 25 '21 at 04:14
  • @HackerMF https://stackoverflow.com/q/48735724/1026459 – Travis J May 26 '21 at 00:10