Last active
October 10, 2018 14:09
-
-
Save aaaristo/85415d4581ac6d719a5a to your computer and use it in GitHub Desktop.
a "ThreadLocal"-like context for asynchronous callback domains
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
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); |
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
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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The idea is that since all generators (async functions) are having a dialog with the
run
function, we can use therun
function scope as a global scope for that asynchronous callback domain. See https://www.youtube.com/watch?v=DqMFX91ToLw for reference.