Search code examples
javascriptecmascript-6generator

Can't destructure an object inside a function generator


I want to destructure the result of a previous yield using default values when the object is empty. But I'm getting a Cannot read property 'xxx' of undefined, meaning that where I try to destructure the variable theObject is undefined, but why?

const DEFAULT_POSITION = {x: 20, y: 20}
const myObject = {}

function* myGenerator(i) {
  const theObject = yield myObject;
  const { posX = DEFAULT_POSITION.x, posY = DEFAULT_POSITION.y, scale = 1 } = theObject

  yield {posX, posY, scale}
}

The first yield returns me an empty object as expected, but then when I run the generator again I get the error that the first item (posX) in the object destruction can not be read since the theObject is undefined.


Solution

  • The problem is that you yield myObject to the caller, the result of that yield is read from the caller via next(/* this "undefined" argument gets passed into your generator function*/) call. So theObject is undefined because you're not using the generator as intended.

    When the iterator's next() method is called, the generator function's body is executed until the first yield expression, which specifies the value to be returned from the iterator

    Calling the next() method with an argument will resume the generator function execution, replacing the yield expression where execution was paused with the argument from next()

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*

    It's not very intuitive, generator functions are weird like that.

    The following works but is probably not what you want as far as I understand

    const DEFAULT_POSITION = {x: 20, y: 20}
    const myObject = {}
    
    function* myGenerator(i) {
      const theObject = yield
      const { posX = DEFAULT_POSITION.x, posY = DEFAULT_POSITION.y, scale = 1 } = theObject
    
      yield {posX, posY, scale}
    }
    const it = myGenerator()
    console.log(it.next()) // -> {value: undefined, done: false}
    console.log(it.next(myObject)) // -> {value: { posX: 20, posY: 20, scale: 1 }, done: false}