-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New implementation with better abstractions
- Loading branch information
1 parent
42eb617
commit c1be2e7
Showing
10 changed files
with
279 additions
and
146 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,6 @@ | |
[ | ||
"env", | ||
{ | ||
"loose": true, | ||
"target": { | ||
"node": "current" | ||
} | ||
|
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
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import intercept, { effect, fork } from './intercept'; | ||
const print = effect('print'); | ||
const sleep = effect('sleep'); | ||
const get = effect('get'); | ||
const set = effect('set'); | ||
|
||
function* initializeStores(values) { | ||
for (const key of Object.keys(values)) { | ||
yield set(key, values[key]); | ||
} | ||
} | ||
|
||
function* displayStores(keys) { | ||
for (const key of keys) { | ||
yield print(`${key}: ${yield get(key)}`); | ||
} | ||
} | ||
|
||
function* doWork() { | ||
yield* initializeStores({ | ||
foo: 'bar', | ||
fizz: 'buzz', | ||
}); | ||
return yield fork({ | ||
print: (...args) => resume => { | ||
console.log('(within inner context)', ...args); | ||
resume(); | ||
}, | ||
})(function*() { | ||
yield sleep(1000); | ||
yield* displayStores(['foo', 'fizz']); | ||
return yield Promise.resolve(42); | ||
}); | ||
} | ||
|
||
(async () => { | ||
const store = {}; | ||
|
||
const res = await intercept({ | ||
print: (...args) => resume => { | ||
console.log('(within outer context)', ...args); | ||
resume(); | ||
}, | ||
sleep: ms => resume => { | ||
console.log('sleep', ms); | ||
setTimeout(resume, ms); | ||
}, | ||
get: key => resume => setTimeout(() => resume(store[key]), 500), // simulate lag | ||
set: (key, value) => resume => { | ||
store[key] = value; | ||
setTimeout(resume, 500); // simulate lag | ||
}, | ||
})(doWork); | ||
console.log({ res }); | ||
})(); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
const isEffect = Symbol('intercept.isEffect'); | ||
const isFork = Symbol('intercept.isFork'); | ||
export const $throw = Symbol('intercept.$throw'); | ||
export const $return = Symbol('intercept.$return'); | ||
|
||
const createChildContext = (handlers, parent) => ({ | ||
parent, | ||
handlers, | ||
}); | ||
|
||
const createChildContextWithPromise = (handlers, parent) => { | ||
let resolve, reject; | ||
const promise = new Promise((_resolve, _reject) => { | ||
resolve = _resolve; | ||
reject = _reject; | ||
}); | ||
const context = createChildContext( | ||
{ | ||
[$throw]: err => () => reject(err), | ||
[$return]: val => () => resolve(val), | ||
...handlers, | ||
}, | ||
parent, | ||
); | ||
return { promise, context }; | ||
}; | ||
|
||
const callNextHandler = (context, handlerName, resume, interrupt, ...args) => { | ||
if (!context) { | ||
throw new Error(`Unhandled yield: ${handlerName.toString()} (${args})`); | ||
} | ||
const bubble = () => | ||
callNextHandler(context.parent, handlerName, resume, interrupt, ...args); | ||
if (context.handlers[handlerName]) { | ||
context.handlers[handlerName](...args)(resume, interrupt, bubble); | ||
return; | ||
} | ||
bubble(); | ||
}; | ||
|
||
export const effect = effectName => (...args) => ({ | ||
[isEffect]: true, | ||
effectName, | ||
args, | ||
}); | ||
|
||
export const fork = handlers => genFn => ({ | ||
[isFork]: true, | ||
handlers, | ||
genFn, | ||
}); | ||
|
||
const intercept = (handlers, parent = null) => genFn => { | ||
const gen = genFn(); | ||
const { context, promise } = createChildContextWithPromise(handlers, parent); | ||
['next', 'throw'].forEach(method => (gen[method] = gen[method].bind(gen))); | ||
const step = (err, val) => { | ||
const resume = nextVal => step(null, nextVal); | ||
const interrupt = nextErr => step(nextErr, null); | ||
const method = err ? 'throw' : 'next'; | ||
let item; | ||
try { | ||
item = gen[method](err || val); | ||
} catch (err) { | ||
callNextHandler(context, $throw, resume, interrupt, err); | ||
return; | ||
} | ||
const { done, value } = item; | ||
if (done) { | ||
callNextHandler(context, $return, resume, interrupt, value); | ||
return; | ||
} | ||
if (typeof value.then === 'function') { | ||
value.then(val => resume(val), err => interrupt(err)); | ||
return; | ||
} | ||
if (value[isFork]) { | ||
intercept( | ||
{ | ||
...value.handlers, | ||
[$return]: val => () => resume(val), | ||
[$throw]: err => () => interrupt(err), | ||
}, | ||
context, | ||
)(value.genFn); | ||
return; | ||
} | ||
if (value[isEffect]) { | ||
const { effectName, args } = value; | ||
callNextHandler(context, effectName, resume, interrupt, ...args); | ||
return; | ||
} | ||
throw new TypeError('Only effects can be yielded within a coroutine.'); | ||
}; | ||
step(null, null); | ||
return promise; | ||
}; | ||
|
||
export default intercept; |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.