从 setTimeout 调用生成器函数

Calling generator function from setTimeout

以下 js 代码在 firefox、chrome 和 nodejs 的开发者控制台中失败。无法弄清楚为什么?

function* x() {}
let y = x()
setTimeout(y.next, 100)

Firefox 错误

TypeError: CallGeneratorMethodIfWrapped method called on incompatible Window

错误 chrome

Uncaught TypeError: Method [Generator].prototype.next called on incompatible receiver # at next ()

错误 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)

当您将 y.next 作为要调用的函数传递时,对象 y 丢失。你可以这样做:

setTimeout(y.next.bind(y), 100)

当您传递 y.next 时,它到达 y 对象并获取对 next 函数的引用,并且它仅传递对 next 函数的引用.它是对 next 函数的通用引用,与 y 对象完全没有关联。然后,稍后当 setTimeout() 触发时,它仅自行调用 next 函数,而对象 y 未在函数调用中使用。这意味着当 next 执行时,this 值将是 undefined 而不是合适的 y 对象。

你可以想象它这样做:

let x = y.next;
setTimeout(x, 100);

y 无关的内容已传递给 setTimeout()。它将调用 next() 方法作为普通函数。如果您这样做,您可能会看到同样的问题:

let x = y.next;
x();

根据其设计,setTimeout() 只是像 fn() 中那样调用函数。它不像 y.next() 那样调用方法。要调用方法,必须在实际函数调用中使用对象引用,如您在 y.next() 中所见。 setTimeout() 不会那样做。它只是调用函数。

因此,.bind() 创建了一个小的存根函数,然后可以为您正确地调用它。因此,如上所示使用它类似于此:

let x = function() {
    y.next();
}
setTimeout(x, 100);

或者,内联版本:

setTimeout(function() {
    y.next();
}, 100);