The following js code fails in developer console of firefox, chrome and nodejs as well. Unable to figure out why?
function* x() {}
let y = x()
setTimeout(y.next, 100)
Error in firefox
TypeError: CallGeneratorMethodIfWrapped method called on incompatible Window
Error in chrome
Uncaught TypeError: Method [Generator].prototype.next called on incompatible receiver # at next ()
Error in node.js
timers.js:475
timer._onTimeout();
^
TypeError: Method [Generator].prototype.next called on incompatible receiver #<Timeout>
at Timeout.next [as _onTimeout] (<anonymous>)
at ontimeout (timers.js:475:11)
at tryOnTimeout (timers.js:310:5)
at Timer.listOnTimeout (timers.js:270:5)
The object y
is lost when you pass y.next
as the function to be called. You can do this:
setTimeout(y.next.bind(y), 100)
When you pass y.next
, it reaches onto the y
object and gets a reference to the next
function and it passes just a reference to the next
function. It's a generic reference to the next
function that has no association at all with the y
object. Then, later when the setTimeout()
fires, it just calls the next
function all by itself and the object y
is not used in the function call. That means that when next
executes, the this
value will be undefined
and will not be the appropriate y
object.
You can imagine it doing this:
let x = y.next;
setTimeout(x, 100);
Nothing to do with y
was passed to setTimeout()
. It's going to call that next()
method as a normal function. You could see the same problem if you did this:
let x = y.next;
x();
By its design, setTimeout()
just calls functions as in fn()
. It doesn't call methods as in y.next()
. To call a method, the object reference has to be used in the actual function call as you see in y.next()
. setTimeout()
does not do that. It just calls functions.
So, .bind()
creates a small little stub function that will then call it properly for you. So, using it as I showed above is analogous to this:
let x = function() {
y.next();
}
setTimeout(x, 100);
Or, the inline version:
setTimeout(function() {
y.next();
}, 100);