Skip to content

Instantly share code, notes, and snippets.

@mrozbarry
Last active June 29, 2018 19:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrozbarry/1936ffa25cf67474ebb282b4ee0aca99 to your computer and use it in GitHub Desktop.
Save mrozbarry/1936ffa25cf67474ebb282b4ee0aca99 to your computer and use it in GitHub Desktop.
A tight functional-reactive update loop for Node.JS, inspired by Elm, Hyperapp, and Redux
const nodeEnv = {
defer: (fn) => process.nextTick(fn),
};
const browserEnv = {
defer: (fn) => setTimeout(fn, 0),
};
const create = (update, subscriptions = [], env = browserEnv) => {
let state = update(undefined, {});
const actionQueue = [];
const waitForIdle = () => new Promise((resolve) => {
if (actionQueue.length === 0) {
resolve();
return;
}
setTimeout(() => {
waitForIdle().then(resolve);
}, 1);
});
const nextAction = () => {
if (actionQueue.length === 0) return;
const messageAndParams = actionQueue.shift();
state = update(state, messageAndParams, effect);
env.defer(nextAction);
};
const effect = (message, params) => {
const starved = actionQueue.length === 0;
actionQueue.push({ message, params });
if (starved) {
nextAction();
}
};
const getState = () => {
return state;
};
const waitForState = (condition) => new Promise((resolve) => {
if (condition(state)) {
resolve(state);
return;
}
setTimeout(() => waitForState(condition).then(resolve), 1);
});
const addSubscription = (subscription) => {
subscription(effect);
};
subscriptions.forEach((subscription) => {
addSubscription(subscription);
});
return {
getState,
waitForIdle,
waitForState,
effect,
};
};
window.FrpApp = { create, nodeEnv, browserEnv }
const App = window.FrpApp;
const messagesReducer = (messages = [], { message, params }, trigger) => {
switch (message) {
case 'ADD_MESSAGE':
return [...messages, params.message];
case 'REMOVE_MESSAGES':
return messages.filter(message => message!== params.message);
}
}
const update = (state = {}, { message, params }, trigger) => {
return {
messages: messagesReducer(state.messages, { message, params }, trigger),
}
};
const thatReallyBlanksMyBlankSub = count => (trigger) => {
const fetchIt = (remaining) => {
if (remaining === 0) return;
fetch('https://api.chew.pro/trbmb')
.then((response) => response.json())
.then((quotes) => trigger('ADD_MESSAGE', { message: quotes[0] }))
.then(() => {
setTimeout(fetchIt, 10000, remaining - 1);
});
}
fetchIt(count)
};
const instance = App.create(update, [thatReallyBlanksMyBlankSub(5)]);
instance
.waitForState((state) => state.messages.length === 5)
.then((state) => console.log('State snapshot', { ...state });// Should have 5 different That really _____s my ____. quotes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment