aperol
v2.2.0
Published
JS library for asynchronous processing of side effects in action based application.
Downloads
39
Maintainers
Readme
Aperol
JS library for asynchronous processing of side effects in action based application.
Install
npm install aperol --save
Usage
Basic with redux
const wait = (timeout) => new Promise((resolve) => setTimeout(resolve, timeout));
const { createModelSaga, put } = require('aperol');
const appReducer = require('./appReducer');
const appSaga = {
reducer(model, action) {
switch (action.type) {
case 'SET_GREETING':
return { ...model, message: 'hello world' };
default:
return model;
}
},
async *updater(model, action) {
switch (action.type) {
case 'GREET_WITH_DELAY':
await wait(1e3); // wait 1 second
// dispatch GREET action to store after delay
yield put({ type: 'GREET', message: model.message });
break;
}
},
};
const modelSaga = createModelSaga(appSaga);
const store = createStore(appReducer, applyMiddleware(modelSaga.middleware));
Observing continual side-effects
const { ObservableSubscribed, put, observe } = require('aperol');
function repeat(interval) {
return new Observable((observer) => {
const handler = setInterval(() => observer.next());
return () => clearInterval(handler);
});
}
const appSaga = {
reducer(model, action) {
switch (action.type) {
case 'GREET':
return { ...model, count: model.count + 1 };
case ObservableSubscribed:
if (action.sourceAction.type === 'GREET_REPEATABLE') {
return { ...model, greetingSubscription: action.subscription };
}
default:
return model;
}
},
async *updater(model, action) {
switch (action.type) {
case 'GREET_REPEATABLE':
yield observe(repeat(1e3) // repeat every 1 second
.map(function () {
// dispatch GREET action to store repeatable
yield put({ type: 'GREET' });
}));
break;
case 'GREET':
if (model.count > 10) {
// dispatch GREETED_10_TIMES action to store after every 10th greeting
yield put({ type: 'GREETED_10_TIMES' });
}
break;
case 'STOP_GREETING':
// When no more needed subscribing side-effect greeting
model.greetingSubscription.unsubscribe();
break;
}
},
};
// ... app code
Combining more sagas
const { combineSagas } = require('aperol');
const appSaga = combineSagas({
greetSaga,
otherCoolSaga,
});
const modelSaga = createModelSaga(appSaga);
// ... app code
Destroy for backend
When you plan to use aperol in node.js on backend it should be destroyed model saga when you no more needs it for user.
// It will unsubscribe all Observable subscriptions
modelSaga.destroy();
Profiler
You can start saga models with profiler which is measuring time of every single action processed by sagas
const modelSaga = createModelSaga(appSaga, {
profiler: {
thresholdMs: 1e3, // Show warnings when action execution reached 1 second
onWarning: (message, action) => console.log(message, action), // Optional callback far warning. Default is console.warn function.
},
});
ES5 transpiled and bundled
If you do not transpile code or not using bundler (like webpack) and need to have pre-transpiled code, import file aperol/dist/es5
instead.
Notes
library automatically polyfill Observable if not available in global Symbol context with zen-observable
library automatically polyfill asyncIterator if not available in global Symbol context
Motivation
Many other projects like redux-saga
& simple libraries like prism
already supports side-effects, continual processing etc.
However there are some deal breakers which motivates me to write self library. Here are the main points:
- using asyncIterators with support for async/await instead of
yield
ing promises (like in redux-saga), - model should be functional mainteined (like a redux state)
- functional model allows you to use already existing dev tools for redux
- Be less robust (then redux-saga)
- Better static typing (with TypeScript)
- Allow use same library for server-side rendering
TypeScript support
Library is written in TypeScript & we are supporting it for you. It can be found all compiled *.d.ts
files in aperol/dist/
folder. By importing module with node
module resolution configuration the library is typed automatically.
import { ISaga } from 'aperol';
const mySaga: ISaga = ...my typed saga :)
For Async Iterator types support add "esnext" flag to "libs" property of tsconfig.json
Nightly builds
New ideas, unconfirmed issueses & pull requests are always available in nightly build branch next
. The corresponding built of npm package is also available under npm tag next
in npm registry.
npm install aperol@next --save
Conclusion
This library was involved because there was no standardized pure-functional way how handle asynchronous side effects in redux based application.
Also missing standard way how to handle continual side-effects.
Aperol was inspired in some existing libraries like a prism or redux-saga.
Aperol uses the last syntactic sugar from ES2016/TypeScript like a async/await
, iterator
, asyncIterator
etc. For using is strictly recommended using transpilers like a TypeScript or Babel.