Last active
September 30, 2023 03:35
-
-
Save jorendorff/35504c2553170be98fc2810ccf60c608 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
Iterator.prototype = { | |
*map(mapper) { | |
for (let value of this) { | |
yield mapper(value); | |
} | |
}, | |
... | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
function IteratorRecord(iter) { | |
if (Object(iter) !== iter) { | |
throw new TypeError("iterator object required"); | |
} | |
return {iterator: iter, nextMethod: iter.next, done: false}; | |
} | |
class MapIterator { | |
#status = "suspended"; // "suspended" | "running" | "completed" | |
#inner; | |
#fn; | |
constructor(inner, fn) { | |
this.#inner = IteratorRecord(inner); | |
if (typeof fn !== "function") { | |
throw new TypeError("map requires a function"); | |
} | |
this.#fn = fn; | |
} | |
#forward(methodName, args) { | |
if (this.#status === "completed") { | |
return {value: undefined, done: true}; | |
} | |
if (this.#status === "running") { | |
throw new TypeError("already running"); | |
} | |
// Status is "suspended", the usual case. | |
this.#status = "running"; | |
let done, value; | |
try { | |
let iter = this.#inner.iterator; | |
let method = (methodName === "next" ? this.#inner.nextMethod : iter[methodName]); | |
let result = Call(iter, method, args); | |
done = !!result.done; | |
value = result.value; | |
} catch (exc) { | |
this.#markComplete(); | |
throw exc; | |
} | |
if (done) { | |
this.#markComplete(); | |
} else { | |
// Pass this value to the mapping-function. | |
try { | |
let fn = this.#fn; | |
value = fn(value); | |
} catch (exc) { | |
this.#iteratorClose(); | |
this.#markComplete(); | |
throw exc; | |
} | |
this.#status = "suspended"; | |
} | |
return {value, done}; | |
} | |
#markComplete() { | |
this.#status = "completed"; | |
this.#inner = undefined; | |
this.#fn = undefined; | |
} | |
#iteratorClose() { | |
try { | |
this.#inner.iterator.return(); | |
} catch { | |
// Ignore this error. | |
} | |
} | |
next(value) { | |
return this.#forward("next", arguments.length === 0 ? [] : [value]); | |
} | |
throw(value) { | |
return this.#forward("throw", [value]); | |
} | |
return(value) { | |
return this.#forward("return", [value]); | |
} | |
} | |
// Do not expose the constructor. | |
delete MapIterator.prototype.constructor; | |
Iterator.prototype = { | |
map(mapper) { | |
return new MapIterator(this, mapper); | |
}, | |
... | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
function IteratorRecord(iter) { | |
if (Object(iter) !== iter) { | |
throw new TypeError("iterator object required"); | |
} | |
return {iterator: iter, nextMethod: iter.next, done: false}; | |
} | |
function IteratorClose(iter) { | |
try { | |
iter.return(); | |
} catch { | |
// Ignore the error. | |
} | |
} | |
function* SyncMap(inner, mapper) { | |
if (typeof mapper !== "function") { | |
throw new TypeError("not a function"); | |
} | |
let iteratorRecord = IteratorRecord(inner); | |
let outVal = undefined; | |
while (true) { | |
let sent; | |
let yieldCompletionType = "return"; | |
let result; | |
try { | |
sent = yield outVal; | |
yieldCompletionType = "normal"; | |
} catch (exc) { | |
yieldCompletionType = "throw"; | |
result = inner.throw(exc); | |
} finally { | |
if (yieldCompletionType === "return") { | |
// Caller did `.return(value)`. We are returning. | |
// We don't have access to the value being returned. | |
// A polyfill can get access to that value by patching | |
// %GeneratorPrototype%.return. | |
inner.return(undefined); | |
// After the finally block, generator will exit. | |
} | |
} | |
if (yieldCompletionType === "normal") { | |
result = Call(iteratorRecord.iterator, iteratorRecord.nextMethod, [sent]); | |
} | |
let {done, value} = result; | |
if (done) { | |
return value; | |
} | |
try { | |
outVal = mapper(value); | |
} catch (exc) { | |
IteratorClose(inner, {type: "throw", value: exc}); // always throws | |
} | |
} | |
} | |
Iterator.prototype = { | |
map(mapper) { | |
let it = SyncMap(this, mapper); | |
it.next(); | |
return it; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment