Skip to content

Instantly share code, notes, and snippets.

@aaaristo
Last active October 10, 2018 14:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save aaaristo/85415d4581ac6d719a5a to your computer and use it in GitHub Desktop.
Save aaaristo/85415d4581ac6d719a5a to your computer and use it in GitHub Desktop.
a "ThreadLocal"-like context for asynchronous callback domains
function isIterator(obj)
{
return obj&&obj.next&&obj.throw;
}
function isRunWithContext(obj)
{
return obj&&typeof obj=='function'&&obj.ctx;
}
function run(gen)
{
var ctx= {};
return new Promise(function (mainResolve,mainReject)
{
try
{
var iterator= gen(ctx);
(function iterate(iterator,element,done)
{
try
{
var promise= element.value;
if (promise instanceof Promise)
promise.then
(function (value)
{
iterate(iterator,iterator.next(value),done);
},
function (err)
{
iterate(iterator,iterator.throw(err),done);
});
else
if (isIterator(promise))
iterate(promise,promise.next(),
function (result)
{
iterate(iterator,iterator.next(result),done);
});
else
if (isRunWithContext(promise))
{
var iter= promise(ctx);
iterate(iter,iter.next(),
function (result)
{
iterate(iterator,iterator.next(result),done);
});
}
else
if (element.done)
done(element.value);
else
iterate(iterator,iterator.next(),done);
}
catch (ex)
{
mainReject(ex);
}
})(iterator,iterator.next(),mainResolve);
}
catch (ex)
{
mainReject(ex);
}
});
}
run.withContext= function (gen)
{
gen.ctx= true;
return gen;
};
function readFile(path)
{
return new Promise(function (resolve,reject)
{
require('fs').readFile(path,function (err,buff)
{
if (err) return reject(err);
resolve(buff);
});
});
}
function* reader(n)
{
try
{
var contents= yield readFile(process.argv[2]);
console.log('here'+n,contents);
}
catch (err)
{
console.log('err'+n,err);
}
}
function readerCtx(n)
{
return run.withContext(function* (ctx)
{
console.log('readerCtx',ctx);
try
{
var contents= yield readFile(process.argv[2]);
console.log('here'+n,contents);
}
catch (err)
{
console.log('err'+n,err);
}
return contents;
});
}
run(function* (ctx)
{
try
{
var contents= yield readFile(process.argv[2]);
ctx.name= 'ciao';
console.log('here',contents);
yield reader(2);
yield reader(3);
contents= yield readerCtx(4);
console.log('out',contents);
}
catch (err)
{
console.log('err',err);
}
}).then(console.log,console.error);
import fs from 'fs';
function isIterator(obj)
{
return obj&&obj.next&&obj.throw;
}
function isRunWithContext(obj)
{
return obj&&typeof obj=='function'&&obj.ctx;
}
let ids = 0;
const runInTransaction = async (gen) => {
const ctx = { id: ids++ };
const iterator = gen(ctx);
return iterate(iterator, ctx);
};
const iterate = async (iterator, ctx) => {
let element = iterator.next();
while (element) {
//console.log('element', element);
if (element.value instanceof Promise) {
try {
const value = await element.value;
element = { value, done: true };
} catch (ex) {
element = iterator.throw(ex);
}
} else if (isIterator(element.value)) {
element = iterator.next(await iterate(element.value, ctx));
} else if (isRunWithContext(element.value)) {
const iter = element.value(ctx);
element = iterator.next(await iterate(iter, ctx));
} else if (element.done) {
return element.value;
} else {
element = iterator.next(element.value);
}
}
};
const withContext = function (afn) {
const gen = function* (ctx) {
return yield afn(ctx);
};
gen.ctx = true;
return gen;
};
const iteratorToAsync = async (iterator, ctx) => iterate(iterator, ctx);
const readFileTX = (path) => withContext(async (ctx) => {
ctx.magic++;
console.log('ctx', ctx);
return readFile(path, 'utf8');
});
const reader = function* () {
yield readFileTX('/tmp/x');
return yield readFileTX('/tmp/x');
};
const readFile = (...args) => new Promise((resolve, reject) => {
fs.readFile(...args, (err, data) => {
if (err) return reject(err);
resolve(data);
})
});
(async () => {
const result = await runInTransaction(function* (ctx) {
ctx.magic = 1;
const txt = yield reader();
console.log('txt', txt);
return 'transaction result';
});
console.log('here', result);
console.log('iteratorToAsync result', await iteratorToAsync(reader(), { magic: 100 }));
})().catch(console.log);
@aaaristo
Copy link
Author

The idea is that since all generators (async functions) are having a dialog with the run function, we can use the run function scope as a global scope for that asynchronous callback domain. See https://www.youtube.com/watch?v=DqMFX91ToLw for reference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment