Вызов функции генератора из setTimeout
Следующий код js не работает в консоли разработчика для firefox, chrome и nodejs. Невозможно понять, почему?
function* x() {}
let y = x()
setTimeout(y.next, 100)
Ошибка в Firefox
TypeError: CallGeneratorMethodIfWrapped метод, вызываемый в несовместимом окне
Ошибка в хроме
Uncaught TypeError: Метод [Generator].prototype.next вызван на несовместимом получателе # 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)
1 ответ
Предмет 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);