Вызов функции генератора из 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);
Другие вопросы по тегам