A small flow-control library based on ES2015 Generators.
This project is strongly inspired by the co library and the redux-saga effects.
GPRoutine is very small yet (less than 0.6KB gzipped), it also hasn't any dependency. You can install it with:
yarn add gproutine
# or with npm
npm i -S gproutine
It has most of the co and bluebird.coroutine features, like:
- Await for array of promises
- Yield another generators
- Resolve object properties
- Add yield handlers (like Bluebird.coroutine)
It also adds some own features, like:
- Written in ES2015 syntax
- Very Small
- Custom actions and effects
NOTE: GPRoutine will not have support for thunk actions (I think)
This library allows to write non-blocking code in a more readable synchronous-like way (like co).
function asyncFunc() {
Promise.resolve(true)
.then((result) => {
console.log(result) // shows "true"
})
}
asyncFunc() // call the function
You can use this syntax in the current NodeJS version without transpiling, but with most browsers you'll need to use Babel.
async function asyncFunc() {
const result = await Promise.resolve(true)
console.log(result)
}
asyncFunc() // call the async function
This syntax is very similar to the async functions, but it need to be wrapped with a couroutine library (see bluebird, co and Q).
As advantage, this is the most testable option.
import { handler } from "gproutine"
import co from "co"
function* asyncFunc() {
const result = yield Promise.resolve(true)
console.log(result) // shows "true"
}
handler(asyncFunc) // call the function with gproutine
co(asyncFunc) // it can also be called with co
You can add more handlers for your yields (like Bluebird does).
For example, if you want to yield a number to known if it's a even number, you can write a new Yield Handler.
import { handler, yieldHandlers } from "gproutine"
yieldHandlers.add((input) => {
// must return a promise
if (typeof input === "number") return Promise.resolve(input % 2 === 0)
})
function* asyncFunc() {
console.log(yield 2) // show "true"
console.log(yield 5) // show "false"
}
handler(asyncFunc)
It's not just another coroutines library, as I already said, this is inspired by the redux-saga pattern, that also uses generators. This feature lets you write easily testable code.
Example:
/* asyncFunc.js */
import { handler, actions, effect } from "gproutine"
// add the "say" action to the actions Map
actions.set("say", (str) => console.log(str))
export function* asyncFunc() {
// call the "say" action (see above)
// this will await if a Promise is returned
yield effect("say", "hello world")
}
handler(asyncFunc) // run the code
As an effect is just a plain object, you can test this code very easily (using mocha and chai):
/* asyncFunc.spec.js */
import { expect } from "chai"
import { effect } from "gproutine"
import { asyncFunc } from "./asyncFunc"
describe("asyncFunc", () => {
it("should say hello world", (done) => {
const iterator = asyncFunc()
const expected = effect("say", "hello world")
expect(iterator.next()).to.be.equal(expected)
done()
})
})
So basically, an action is the function that is called. And the effect is a request to call an action.
Gustavo Paulo - gpaulo00
MIT