- Write consumer code as if you're pulling values out of the producer
- Write producer code as if you're pushing values onto the consumer
Each side gets to pretend they own the control-flow: they each decide when the other gets called. Pulling values allows you to terminate early, for example (but not pulling any more).
Pushing values on the producer side makes it easier to keep track of how far you've reached in your data-structure traversing. Quoting Wikipedia:
However, Java does not have generators built into the language. This means that creating iterators is often much trickier than in languages with built-in generators, especially when the generation logic is complex. Because all state must be saved and restored every time an item is to be yielded from an iterator, it is not possible to store state in local variables or use built-in looping routines, as when generators are available; instead, all of this must be manually simulated, using object fields to hold local state and loop counters.