Last active
October 28, 2020 02:30
-
-
Save hackwaly/1e5cfd0daf4c95f0014d to your computer and use it in GitHub Desktop.
Coroutine, go and call/cc implemented in javascript (use generator, less then 100 line).
This file contains 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
var go; | |
var callcc; | |
(function () { | |
function noop() {} | |
var iteratorConstructor = function* () {}().constructor; | |
function isIterator(p) { | |
return p != null && p.constructor === iteratorConstructor; | |
} | |
var yieldToken = {}; | |
var currentCoroutine = null; | |
function go_(f) { | |
schedule(new Coroutine(f)); | |
} | |
function Coroutine(f) { | |
this._f = f; | |
} | |
Coroutine.prototype._continuedValue = undefined; | |
Coroutine.prototype._next = function () { | |
var r = (this._f)(); | |
if (isIterator(r)) { | |
this._f = r; | |
this._next = function () { | |
var p = this._continuedValue; | |
this._continuedValue = undefined; | |
var r = this._f.next(p); | |
if (r.done) { | |
this._next = noop; | |
} else if (r.value !== yieldToken) { | |
this._continuedValue = r.value; | |
schedule(this); | |
} | |
}; | |
this._next(); | |
} else { | |
this._next = noop; | |
} | |
}; | |
function callcc_(f) { | |
var coroutine = currentCoroutine; | |
f(function (p) { | |
if (coroutine !== null) { | |
coroutine._continuedValue = p; | |
schedule(coroutine); | |
coroutine = null; | |
} | |
}); | |
return yieldToken; | |
} | |
var queuedCoroutines = []; | |
var scheduled = false; | |
var resolvedPromise = Promise.resolve(); | |
function schedule(coroutine) { | |
queuedCoroutines.push(coroutine); | |
if (!scheduled) { | |
scheduled = true; | |
resolvedPromise.then(run); | |
} | |
} | |
function run() { | |
while (queuedCoroutines.length > 0) { | |
var coroutines = queuedCoroutines; | |
queuedCoroutines = []; | |
for (var i = 0; i < coroutines.length; i++) { | |
var coroutine = coroutines[i]; | |
currentCoroutine = coroutine; | |
coroutine._next(); | |
} | |
} | |
currentCoroutine = null; | |
scheduled = false; | |
} | |
go = go_; | |
callcc = callcc_; | |
})(); |
This file contains 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
var chan; | |
var end; | |
(function () { | |
var end_ = {}; | |
// Non-buffered channel | |
function Channel() { | |
this._put = null; | |
this._take = null; | |
} | |
Channel.prototype.take = function () { | |
if (this._take !== null) { | |
return this._take(); | |
} | |
var self = this; | |
return callcc(function (continue_) { | |
self._put = function (data) { | |
self._put = null; | |
continue_(data); | |
return true; | |
}; | |
}); | |
}; | |
Channel.prototype.put = function (data) { | |
if (this._put !== null) { | |
return this._put(data); | |
} | |
var self = this; | |
return callcc(function (continue_) { | |
self._take = function () { | |
self._take = null; | |
continue_(); | |
return data; | |
}; | |
}) | |
}; | |
Channel.prototype.close = function () { | |
var self = this; | |
function close() { | |
if (self._put !== null) { | |
self._put(end_); | |
} | |
self._put = function () { | |
return false; | |
}; | |
self._take = function () { | |
return end_; | |
}; | |
} | |
if (this._take !== null) { | |
var take = this._take; | |
this._take = function () { | |
var r = take(); | |
close(); | |
return r; | |
}; | |
} else { | |
close(); | |
} | |
}; | |
function chan_() { | |
return new Channel(); | |
} | |
chan = chan_; | |
end = end_; | |
})(); | |
function* player(name, table) { | |
while (true) { | |
var ball = yield table.take(); | |
if (ball === end) { | |
console.log(name + ": table's gone"); | |
return; | |
} | |
ball.hits += 1; | |
console.log(name + " " + ball.hits); | |
yield sleep(100); | |
yield table.put(ball); | |
} | |
} | |
go(function* () { | |
var table = chan(); | |
go(player.bind(null, "ping", table)); | |
go(player.bind(null, "pong", table)); | |
yield table.put({hits: 0}); | |
yield sleep(1000); | |
table.close(); | |
}); |
This file contains 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
function sleep(time) { | |
return callcc(function (continue_) { | |
setTimeout(continue_, time); | |
}); | |
} | |
go(function *() { | |
for (var i = 0; i < 10; i++) { | |
console.log(i); | |
yield sleep(1000); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment