Skip to content

Commit

Permalink
New implementation with better abstractions
Browse files Browse the repository at this point in the history
  • Loading branch information
elierotenberg committed Aug 9, 2017
1 parent 42eb617 commit c1be2e7
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 146 deletions.
1 change: 0 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
[
"env",
{
"loose": true,
"target": {
"node": "current"
}
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
"scripts": {
"make:lint": "eslint src/**/*.js",
"make:clean": "rimraf dist",
"make:build": "yarn run clean && mkdir dist && cp -r src/* dist && babel src -d dist --source-maps",
"make:build": "yarn run make:clean && mkdir dist && cp -r src/* dist && babel src -d dist --source-maps inline",
"make": "yarn run make:build"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.0",
Expand All @@ -24,6 +25,8 @@
"eslint": "^4.4.0",
"eslint-plugin-prettier": "^2.1.2",
"prettier": "^1.5.3",
"rimraf": "^2.6.1"
"pretty-error": "^2.1.1",
"rimraf": "^2.6.1",
"source-map-support": "^0.4.15"
}
}
8 changes: 0 additions & 8 deletions src/CallEffect.js

This file was deleted.

69 changes: 0 additions & 69 deletions src/Context.js

This file was deleted.

9 changes: 0 additions & 9 deletions src/ForkEffect.js

This file was deleted.

44 changes: 0 additions & 44 deletions src/example.js

This file was deleted.

55 changes: 55 additions & 0 deletions src/intercept.example.js
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 });
})();
99 changes: 99 additions & 0 deletions src/intercept.js
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;
11 changes: 0 additions & 11 deletions src/lib.js

This file was deleted.

Loading

0 comments on commit c1be2e7

Please sign in to comment.