effection
Advanced tools
Comparing version 0.0.2 to 0.1.0-e08d61f
{ | ||
"name": "effection", | ||
"version": "0.0.2", | ||
"description": "A structured concurrency library that makes working with side-effects in JavaScript lovely.", | ||
"keywords": [ | ||
"concurrency", | ||
"structured-concurrency", | ||
"async", | ||
"side-effects" | ||
], | ||
"homepage": "https://github.com/cowboyd/effection.js#readme", | ||
"bugs": { | ||
"url": "https://github.com/cowboyd/effection.js/issues" | ||
}, | ||
"description": "Effortlessly composable structured concurrency primitive for JavaScript", | ||
"version": "0.1.0-e08d61f", | ||
"license": "MIT", | ||
"author": "Charles Lowell <charles@frontside.io>, Taras Mankovski <taras@frontside.io>", | ||
"files": [ | ||
"src", | ||
"README.md", | ||
"dist" | ||
"dist-*/", | ||
"bin/", | ||
"index.d.ts" | ||
], | ||
"main": "dist/effection.cjs.js", | ||
"module": "dist/effection.es.js", | ||
"repository": "git+ssh://git@github.com/cowboyd/effection.js.git", | ||
"scripts": { | ||
"lint": "eslint ./", | ||
"test": "mocha --recursive -r tests/setup tests", | ||
"coverage": "nyc --reporter=html --reporter=text npm run test", | ||
"coveralls": "nyc report --reporter=text-lcov | coveralls" | ||
}, | ||
"esnext": "dist-src/index.js", | ||
"main": "dist-node/index.js", | ||
"module": "dist-web/index.js", | ||
"pika": true, | ||
"sideEffects": false, | ||
"repository": "http://github.com/cowboyd/effection.js", | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"@babel/core": "7.0.0", | ||
"@babel/preset-env": "^7.0.0", | ||
"@babel/register": "^7.0.0", | ||
"babel-eslint": "^10.0.1", | ||
"coveralls": "3.0.2", | ||
"eslint": "^5.7.0", | ||
"eslint-plugin-prefer-let": "^1.0.1", | ||
"expect": "^23.4.0", | ||
"mocha": "^5.2.0", | ||
"nyc": "13.1.0", | ||
"rollup": "^0.63.4", | ||
"rollup-plugin-babel": "4.0.0-beta.7", | ||
"rollup-plugin-filesize": "4.0.1", | ||
"rollup-plugin-node-resolve": "3.3.0" | ||
"@babel/core": "7.4.4", | ||
"@babel/preset-env": "7.4.4", | ||
"@babel/register": "7.4.4", | ||
"@pika/pack": "0.3.7", | ||
"@pika/plugin-build-node": "0.3.16", | ||
"@pika/plugin-build-web": "0.3.16", | ||
"@pika/plugin-copy-assets": "^0.3.16", | ||
"@pika/plugin-standard-pkg": "0.3.16", | ||
"@types/mocha": "^5.2.6", | ||
"babel-eslint": "10.0.1", | ||
"eslint": "5.16.0", | ||
"eslint-plugin-prefer-let": "1.0.1", | ||
"expect": "24.7.1", | ||
"jest-mock": "^24.8.0", | ||
"mocha": "6.1.4", | ||
"ts-expect": "^1.1.0", | ||
"ts-node": "^8.1.0", | ||
"typescript": "^3.6.4" | ||
}, | ||
"dependencies": {}, | ||
"nyc": { | ||
"exclude": [ | ||
"**/tests" | ||
] | ||
} | ||
"private": false | ||
} |
175
README.md
@@ -8,5 +8,176 @@ [![npm](https://img.shields.io/npm/v/effection.svg)](https://www.npmjs.com/package/effection) | ||
# effection | ||
# ❤ Effection ❤️ | ||
Effortlessly composable structured concurrency primitive for | ||
JavaScript | ||
A structured concurrency library that makes working with side-effects lovely. | ||
See [examples](examples/) | ||
To run an example with NAME: | ||
``` text | ||
$ node -r ./tests/setup examples/NAME | ||
``` | ||
## Structured Concurrency and Effects | ||
> Note: For an general introduction to the concept of structured | ||
> concurrency, and why it is so important, see [this excellent primer | ||
> on the subject][1] by Nathaniel Smith. | ||
There's an entire hive of bugs that occur when asynchronous processes | ||
outlive their welcome. The concept of structured concurrency eliminates | ||
these altogether by providing the following guarantees: | ||
1. A process is considered pending (unfinished) while it is either | ||
running or when it has _any_ child process that is pending. | ||
2. If an error occurs in a process that is not caught, then that error | ||
propagates to the parent process. | ||
3. If a process finishes in error or by halting, then all of its child | ||
process are immediately halted. | ||
For example, if you have the following processes: | ||
``` text | ||
+ - parent | ||
| | ||
+ --- child A | ||
| | ||
+ --- child B | ||
``` | ||
We can make the following assertions based on these guarantees : | ||
a. If the code associated with `parent` finishes running, but _either_ `A` | ||
_or_ `B` are pending, then `parent` is still considered pending. | ||
b. If `parent` is halted, then _both_ `A` and `B` are halted. | ||
c. If an error is raised in `parent`, then _both_ `A` and `B` are | ||
halted. | ||
d. if an error is raised in `A`, then that error is also raised in | ||
`parent`, _and_ child `B` is halted. | ||
scenario `d` is of particular importance. It means that if a child | ||
throws an error and its parent doesn't catch it, then all of its | ||
siblings are immediately halted. | ||
## Execution | ||
The process primitive is the `Execution`. To create (and start) an | ||
`Execution`, use the `execute` function and pass it a generator. This | ||
simplest example waits for 1 second, then prints out "hello world" to | ||
the console. | ||
``` javascript | ||
import { execute, timeout } from 'effection'; | ||
let process = execute(function*() { | ||
yield timeout(1000); | ||
return 'hello world'; | ||
}); | ||
process.isRunning //=> true | ||
// 1000ms passes | ||
// process.isRunning //=> false | ||
// process.result //=> 'hello world' | ||
``` | ||
Child processes can be composed freely. So instead of yielding for | ||
1000 ms, we could instead, yield 10 times for 100ms. | ||
``` javascript | ||
execute(function*() { | ||
yield function*() { | ||
for (let i = 0; i < 10; i++) { | ||
yield timeout(100); | ||
} | ||
} | ||
return 'hello world'; | ||
}) | ||
``` | ||
And in fact, processes can be easily and arbitrarly deeply nested: | ||
``` javascript | ||
let process = execute(function*() { | ||
return yield function*() { | ||
return yield function*() { | ||
return yield function*() { | ||
return 'hello world'; | ||
} | ||
}; | ||
}; | ||
}); | ||
process.isCompleted //=> true | ||
process.result //=> "hello world" | ||
``` | ||
In order to abstract a process so that it can take arguments, you can | ||
use the `call` function: | ||
``` javascript | ||
import { execute, timeout, call } from 'effection'; | ||
function* waitForSeconds(durationSeconds) { | ||
yield timeout(durationSeconds * 1000); | ||
} | ||
execute(function*() { | ||
yield call(waitforseconds, 10); | ||
}); | ||
``` | ||
More likely though, you would want to define a higher-order function | ||
that took your argument and returned a generator: | ||
``` javascript | ||
function waitForSeconds(durationSeconds) { | ||
return function*() { | ||
yield timeout(durationSeconds * 1000); | ||
} | ||
} | ||
``` | ||
### Asynchronous Execution | ||
Sometimes you want to execute some processes in parallel and not | ||
necessarily block further execution on them. You still want the | ||
guarantees associated with structured concurrency however. For | ||
example, you might want to create a couple of different servers as | ||
part of your main process. To do this, you would use the `fork` method | ||
on the execution: | ||
``` javascript | ||
import { execute } from 'effection'; | ||
execute(function*() { | ||
this.fork(createFileServer); | ||
this.fork(createHttpServer); | ||
}); | ||
``` | ||
Even though it exits almost immediately, the main process is not | ||
considered completed until _both_ servers shutdown. More importantly | ||
though, if we shutdown the main process, then both servers will be | ||
halted. | ||
## Development | ||
yarn install | ||
``` text | ||
$ yarn | ||
``` | ||
run tests: | ||
``` text | ||
$ yarn test | ||
``` | ||
[1]: https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
38954
15
1188
183
19
2
2
2