// primes the generator function from arguments provided up to the first yield // no priming performed without arguments/initial call export default function unlazy(lazyGeneratorFunction) { const generatorProxy = function (...args) { const lazy = lazyGeneratorFunction.apply(this, args); const first = lazy.next(); // run up to first yield // create a proxy generator that replays that first result once let sentFirst = false; const proxy = { next(value) { if (!sentFirst) { sentFirst = true; return first; } return lazy.next(value); }, return(value) { return lazy.return(value); }, throw(err) { return lazy.throw(err); }, [Symbol.iterator]() { return this; } }; Object.setPrototypeOf(proxy, Object.getPrototypeOf(lazy)); return proxy; }; Object.setPrototypeOf(generatorProxy, Object.getPrototypeOf(lazyGeneratorFunction)); return generatorProxy; } export { unlazy } /* Usage: function* lazy(arg1, arg2) { console.log("priming with", arg1, arg2); const result = yield arg1 + arg2; console.log("resumed with", result); return result * 2; } const ul = unlazy(lazy); // straight iteration results in the same behavior. No difference in priming here: for (const value of lazy(2, 3)) { console.log("value:", value); } for (const value of ul(2, 3)) { console.log("value:", value); } // the difference comes when the iterator is created before actual iteration: const it1 = lazy(5, 7); // not primed yet const it2 = ul(5, 7); // primed immediately for (const value of it1) { // primes now console.log("value:", value); } for (const value of it2) { // primed already console.log("value:", value); } */