Skip to content

Instantly share code, notes, and snippets.

@getify
Last active December 15, 2020 15:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save getify/0e1b1ef7e270c6d16f4d1d616296eda4 to your computer and use it in GitHub Desktop.
Save getify/0e1b1ef7e270c6d16f4d1d616296eda4 to your computer and use it in GitHub Desktop.
a better "async generator", where `return(..)` immediately tears it down? Insipired from: https://alinacierdem.com/the-problem-with-async-generators/
// NOTE: ag(..) is defined in 2.js below
for await (let v of ag("hello")) {
console.log(`v: ${v}`);
}
// a: hello
// b
// v: 42
// c: some data: 10
// d
// v: some data: 50
// .... waiting 5 seconds ....
// e
// finally!
// ********************************************************
for await (let v of ag("hello")) {
console.log(`v: ${v}`);
if (v == "some data: 50") {
break;
}
}
// a: hello
// b
// v: 42
// c: some data: 10
// d
// v: some data: 50
// finally!
// ********************************************************
var it = ag("hello");
for await (let v of it) {
console.log(`v: ${v}`);
if (v == "some data: 50") {
setTimeout(function(){ it.return(); },10);
}
}
// a: hello
// b
// v: 42
// c: some data: 10
// d
// v: some data: 50
// .... waiting only 10 milliseconds ....
// finally!
// NOTE: asyncGenerator(..) utility is defined in 3.js below
var ag = asyncGenerator(function *main(pwait,a){
// NOTE: `pwait` parameter here is how we fake an `await` inside a normal generator
// for it to work, you have to do `yield pwait(..)`, not just `pwait(..)`
try {
console.log(`a: ${a}`);
yield pwait(delay(500)); // <---- fake `await`
console.log("b");
yield 42;
var c = yield pwait(getData(10)); // <---- fake `await`
console.log(`c: ${c}`);
yield pwait(delay(500)); // <---- fake `await`
console.log("d");
yield getData(50); // but THIS one is just yielding a promise out through the async generator machinery, which is where the "v: some data: 50" log message comes from
yield pwait(delay(5000)); // <---- fake `await`
console.log("e");
yield pwait(delay(500)); // <---- fake `await`
return 250;
}
finally {
console.log("finally!");
}
});
// inspired by: https://alinacierdem.com/the-problem-with-async-generators/
function asyncGenerator(gen) {
var awaiteds = new WeakSet();
var unset = Symbol("unset");
var returned = Symbol("returned");
return function wrapped(...args){
var def = deferred();
var it = gen(pwait,...args);
var ait = runner(it,def.pr);
var aitRet = ait.return;
ait.return = doReturn;
return ait;
// ***************************
function doReturn(v){
try {
def.pr.resolved = true;
def.resolve(returned);
return it.return(v);
}
finally {
aitRet.call(ait);
ait.return = aitRet;
def = ait = aitRet = null;
}
}
};
// ***************************
function pwait(v) {
var pr = Promise.resolve(v);
awaiteds.add(pr);
return pr;
}
async function *runner(it,complete) {
var res;
var excp = unset;
try {
while (!complete.resolved) {
if (excp !== unset) {
res = it.throw(excp);
}
else {
res = it.next(res);
}
if (isPromise(res.value)) {
if (awaiteds.has(res.value)) {
awaiteds.delete(res.value);
try {
res = await Promise.race([
complete,
res.value,
]);
if (res === returned) {
return;
}
}
catch (err) {
excp = err;
}
}
else {
res = yield res.value;
}
}
else if (res.done) {
return res.value;
}
else {
res = yield res.value;
}
}
}
finally {
it = complete = null;
}
}
function isPromise(pr) {
return (pr && typeof pr == "object" && typeof pr.then == "function");
}
function deferred() {
var resolve;
var pr = new Promise(function c(res){
resolve = res;
});
return { pr, resolve };
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment