Skip to content

Instantly share code, notes, and snippets.

@codehag
Forked from jorendorff/option_1.js
Last active September 30, 2023 03:35
Show Gist options
  • Save codehag/1692139c2bc4fb6c170363e6812d36ae to your computer and use it in GitHub Desktop.
Save codehag/1692139c2bc4fb6c170363e6812d36ae to your computer and use it in GitHub Desktop.
"use strict";
Iterator.prototype = {
*map(mapper) {
for (let value of this) {
yield mapper(value);
}
},
};
"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);
},
};
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 = iteratorRecord.nextMethod.call(iteratorRecord.iterator, [sent]);
}
let {done, value} = result;
if (done) {
return value;
}
try {
outVal = mapper(value);
} catch (exc) {
IteratorClose(inner, {type: "throw", value: exc}); // always throws
}
}
}
function* Iterator(x) {
while (true) {
try {
x += 1;
yield x;
} catch (e) {
console.log(e)
}
}
}
Iterator.prototype.map = function(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