aperol
TypeScript icon, indicating that this package has built-in type declarations

2.2.0 • Public • Published

Aperol

Build Status

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 yielding 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.

Package Sidebar

Install

npm i aperol

Weekly Downloads

0

Version

2.2.0

License

MIT

Unpacked Size

423 kB

Total Files

51

Last publish

Collaborators

  • misak113