首页 > 解决方案 > 从 setTimeout 调用生成器函数

问题描述

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

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

火狐中的错误

TypeError:在不兼容的窗口上调用的 CallGeneratorMethodIfWrapped 方法

铬错误

Uncaught TypeError: Method [Generator].prototype.next 在不兼容的接收器上调用 # 在 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)

标签: javascriptnode.jses6-generator

解决方案


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

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

推荐阅读