Socket
Socket
Sign inDemoInstall

effection

Package Overview
Dependencies
Maintainers
1
Versions
299
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

effection - npm Package Compare versions

Comparing version 0.3.0 to 0.3.1-828df92

dist-src/fork.js

8

CHANGELOG.md

@@ -9,2 +9,10 @@ # Changelog

## [0.3.1] - 2019-10-23
- unroll continuation and state classes in execution. This makes stack
traces much smaller and easier to debug:
https://github.com/thefrontside/effection.js/pull/19
- remove some dead files that were not contributing to the API
https://github.com/thefrontside/effection.js/pull/18
## [0.3.0] - 2019-10-18

@@ -11,0 +19,0 @@

409

dist-node/index.js

@@ -21,2 +21,4 @@ 'use strict';

const noop = x => x;
/**

@@ -147,226 +149,238 @@ * An execution controller that resumes or throws based

class Execution {
static of(operation) {
return new Execution(operation, x => x);
}
static start(operation) {
let execution = Execution.of(operation);
execution.start();
return execution;
}
class Fork {
get isUnstarted() {
return this.status instanceof Unstarted;
return this.state === 'unstarted';
}
get isRunning() {
return this.status instanceof Running;
return this.state === 'running';
}
get isBlocking() {
return this.isRunning || this.isWaiting;
get isWaiting() {
return this.state === 'waiting';
}
get isCompleted() {
return this.status instanceof Completed;
return this.state === 'completed';
}
get isErrored() {
return this.status instanceof Errored;
return this.state === 'errored';
}
get isHalted() {
return this.status instanceof Halted;
return this.state === 'halted';
}
get isWaiting() {
return this.status instanceof Waiting;
get isBlocking() {
return this.isRunning || this.isWaiting;
}
get hasBlockingChildren() {
return this.children.some(child => child.isBlocking);
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
get result() {
return this.status.result;
}
try {
for (var _iterator = this.children[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
let child = _step.value;
constructor(operation) {
this.operation = toGeneratorFunction(operation);
this.status = new Unstarted(this);
this.children = [];
this.continuation = ExecutionFinalized;
}
if (child.isBlocking) {
return true;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
start() {
return this.status.start();
return false;
}
resume(value) {
return this.status.resume(value);
constructor(operation, parent, sync) {
this.operation = toGeneratorFunction(operation);
this.parent = parent;
this.sync = sync;
this.children = new Set();
this.state = 'unstarted';
this.exitPrevious = noop;
this.continuation = Continuation.of(frame => {
if (frame.isErrored) {
let error = frame.result;
error.frame = frame;
throw error;
} else {
return frame;
}
});
}
throw(error) {
return this.status.throw(error);
}
halt(message) {
return this.status.halt(message);
}
then(...args) {
this.continuation = this.continuation.then(...args);
then(fn) {
this.continuation = this.continuation.then(fn);
return this;
}
catch(...args) {
this.continuation = this.continuation.catch(...args);
catch(fn) {
this.continuation = this.continuation.catch(fn);
return this;
}
finally(...args) {
this.continuation = this.continuation.finally(...args);
finally(fn) {
this.continuation = this.continuation.finally(fn);
return this;
}
}
halt(value) {
if (this.isRunning) {
this.exitPrevious();
this.iterator.return(value);
}
class Status {
constructor(execution) {
this.execution = execution;
if (this.isBlocking) {
this.finalize('halted', value);
}
}
start() {
this.cannot('start');
}
throw(error) {
if (this.isRunning) {
this.thunk(iterator => iterator.throw(error));
} else if (this.isWaiting) {
this.finalize('errored', error);
} else {
throw new Error(`
Tried to call Fork#throw() on a Fork that has already been finalized. This
should never happen and so is almost assuredly a bug in effection. All of
its users would be in your eternal debt were you to please take the time to
report this issue here:
https://github.com/thefrontside/effection.js/issues/new
resume() {
this.cannot('resume');
Thanks!`);
}
}
throw() {
this.cannot('throw');
}
resume(value) {
if (this.isUnstarted) {
this.iterator = this.operation.call(this);
this.state = 'running';
this.resume(value);
} else if (this.isRunning) {
this.thunk(iterator => iterator.next(value));
} else {
throw new Error(`
Tried to call Fork#resume() on a Fork that has already been finalized. This
should never happen and so is almost assuredly a bug in effection. All of
its users would be in your eternal debt were you to please take the time to
report this issue here:
https://github.com/thefrontside/effection.js/issues/new
halt() {
this.cannot('halt');
}
Thanks!`);
}
cannot(operationName) {
let name = this.constructor.name;
let message = `tried to perfom operation ${operationName}() on an execution with status '${name}'`;
throw new Error(`InvalidOperationError: ${message}`);
return this;
}
finalize(status) {
let execution = this.execution;
execution.status = status;
execution.continuation.call(execution);
fork(operation, sync = false) {
// console.log(`parent.fork(${operation}, ${sync})`);
let child = new Fork(operation, this, sync);
this.children.add(child);
child.resume();
return child;
}
}
join(child) {
if (!this.children.has(child)) {
return;
}
const Finalized = Status => class FinalizedStatus extends Status {
halt() {}
if (!this.isBlocking) {
throw new Error(`
Tried to call Fork#join() on a Fork that has already been finalized which means
that a sub-fork is being finalized _after_ its parent. This should never happen
and so is almost assuredly a bug in effection. All of its users would be
in your eternal debt were you to please take the time to report this issue here:
https://github.com/thefrontside/effection.js/issues/new
};
Thanks!
`);
}
class Unstarted extends Status {
start() {
let operation = this.execution.operation;
let execution = this.execution;
let iterator = operation.apply(execution);
execution.status = new Running(execution, iterator);
execution.resume();
}
if (child.isCompleted) {
if (this.isWaiting && !this.hasBlockingChildren) {
this.finalize('completed');
} else if (this.isRunning) {
this.children.delete(child);
}
let currentExecution;
function withCurrentExecution(execution, fn) {
let previousExecution = currentExecution;
try {
currentExecution = execution;
return fn();
} finally {
currentExecution = previousExecution;
if (child.sync) {
this.resume(child.result);
}
}
} else if (child.isErrored) {
this.throw(child.result);
} else if (child.isHalted) {
if (child.sync) {
this.throw(new Error(`Interupted: ${child.result}`));
} else {
if (!this.hasBlockingChildren) {
this.finalize('completed');
}
}
}
}
}
class Running extends Status {
constructor(execution, iterator, current = {
done: false
}) {
super(execution);
this.iterator = iterator;
this.current = current;
this.noop = x => x;
this.releaseControl = this.noop;
}
thunk(thunk) {
let execution = this.execution,
iterator = this.iterator;
thunk(fn) {
try {
let next = withCurrentExecution(execution, () => {
this.releaseControl();
return thunk(iterator);
});
let next = this.enter(fn);
if (next.done) {
if (execution.hasBlockingChildren) {
execution.status = new Waiting(execution, next.value);
if (this.hasBlockingChildren) {
this.state = 'waiting';
} else {
this.finalize(new Completed(execution, next.value));
this.finalize('completed', next.value);
}
} else {
let control = controllerFor(next.value);
let release = control(execution);
let controller = controllerFor(next.value); /// what happens here if control is synchronous. and resumes execution immediately?
if (typeof release === 'function') {
this.releaseControl = release;
} else {
this.releaseControl = this.noop;
}
let exit = controller(this);
this.exitPrevious = typeof exit === 'function' ? exit : noop;
}
} catch (e) {
// error was thrown, but not caught in the generator.
this.status = new Errored(execution, e);
execution.children.forEach(child => child.halt(e));
this.finalize(new Errored(execution, e));
} catch (error) {
this.finalize('errored', error);
}
}
resume(value) {
this.thunk(iterator => iterator.next(value));
}
enter(fn) {
let previouslyExecuting = Fork.currentlyExecuting;
throw(error) {
this.thunk(iterator => iterator.throw(error));
try {
Fork.currentlyExecuting = this;
this.exitPrevious();
return fn(this.iterator);
} finally {
Fork.currentlyExecuting = previouslyExecuting;
}
}
halt(value) {
let execution = this.execution,
iterator = this.iterator;
this.releaseControl();
iterator.return(value);
execution.status = new Halted(execution, value);
execution.children.forEach(child => {
child.halt(value);
finalize(state, result) {
this.state = state;
this.result = result;
this.children.forEach(child => {
this.children.delete(child);
child.halt(result);
});
this.finalize(new Halted(execution, value));
}
}
class Completed extends Finalized(Status) {
constructor(execution, result) {
super(execution);
this.result = result;
if (this.parent) {
this.parent.join(this);
} else {
this.continuation.call(this);
}
}

@@ -376,42 +390,13 @@

class Errored extends Finalized(Status) {
constructor(execution, error) {
super(execution);
this.result = error;
function fork(operation, parent = Fork.currentlyExecuting) {
if (parent) {
return parent.fork(operation);
} else {
return new Fork(operation).resume();
}
}
class Halted extends Finalized(Status) {
constructor(execution, message) {
super(execution);
this.result = message;
}
}
class Waiting extends Completed {
halt(value) {
let execution = this.execution;
execution.status = new Halted(execution, value);
execution.children.forEach(child => {
child.halt(value);
});
this.finalize(new Halted(execution, value));
}
throw(error) {
let execution = this.execution;
execution.status = new Errored(execution, error);
execution.children.forEach(child => {
child.halt(error);
});
this.finalize(new Errored(execution, error));
}
}
function controllerFor(value) {
if (isGeneratorFunction(value) || isGenerator(value)) {
return invoke(value);
return parent => parent.fork(value, true);
} else if (typeof value === 'function') {

@@ -428,50 +413,4 @@ return value;

function fork(operation) {
if (currentExecution == null) {
return Execution.start(operation);
}
let parent = currentExecution;
let child = new Execution(operation).then(() => {
if (parent.isWaiting && !parent.hasBlockingChildren) {
parent.status.finalize(new Completed(parent, parent.result));
}
}).catch(e => parent.throw(e));
parent.children.push(child);
child.start();
return child;
}
function invoke(operation) {
return parent => {
let child = new Execution(operation).then(child => {
if (child.isCompleted) {
return parent.resume(child.result);
} // call() is synchronous so the parent is waiting for the
// return value of the child. If the child is halted, and the
// parent is still running, that means its waiting on the child
// and so it's an error because the child was interupted
if (child.isHalted && parent.isRunning) {
return parent.throw(new Error(`Interupted: ${child.result}`));
}
}).catch(e => parent.throw(e));
parent.children.push(child);
child.start();
};
}
const ExecutionFinalized = Continuation.of(execution => {
if (execution.isErrored) {
let error = execution.result;
error.execution = execution;
throw error;
} else {
return execution;
}
});
exports.fork = fork;
exports.promiseOf = promiseOf;
exports.timeout = timeout;
export { timeout } from "./timeout.js";
export { fork } from "./execution.js";
export { fork } from "./fork.js";
export { promiseOf } from "./promise-of.js";

@@ -18,2 +18,4 @@ /**

const noop = x => x;
/**

@@ -148,227 +150,196 @@ * An execution controller that resumes or throws based

class Execution {
static of(operation) {
return new Execution(operation, x => x);
}
static start(operation) {
let execution = Execution.of(operation);
execution.start();
return execution;
}
class Fork {
get isUnstarted() {
return this.status instanceof Unstarted;
return this.state === 'unstarted';
}
get isRunning() {
return this.status instanceof Running;
return this.state === 'running';
}
get isBlocking() {
return this.isRunning || this.isWaiting;
get isWaiting() {
return this.state === 'waiting';
}
get isCompleted() {
return this.status instanceof Completed;
return this.state === 'completed';
}
get isErrored() {
return this.status instanceof Errored;
return this.state === 'errored';
}
get isHalted() {
return this.status instanceof Halted;
return this.state === 'halted';
}
get isWaiting() {
return this.status instanceof Waiting;
get isBlocking() {
return this.isRunning || this.isWaiting;
}
get hasBlockingChildren() {
return this.children.some(child => child.isBlocking);
}
for (let child of this.children) {
if (child.isBlocking) {
return true;
}
}
get result() {
return this.status.result;
return false;
}
constructor(operation) {
constructor(operation, parent, sync) {
this.operation = toGeneratorFunction(operation);
this.status = new Unstarted(this);
this.children = [];
this.continuation = ExecutionFinalized;
this.parent = parent;
this.sync = sync;
this.children = new Set();
this.state = 'unstarted';
this.exitPrevious = noop;
this.continuation = Continuation.of(frame => {
if (frame.isErrored) {
let error = frame.result;
error.frame = frame;
throw error;
} else {
return frame;
}
});
}
start() {
return this.status.start();
}
resume(value) {
return this.status.resume(value);
}
throw(error) {
return this.status.throw(error);
}
halt(message) {
return this.status.halt(message);
}
then() {
this.continuation = this.continuation.then(...arguments);
then(fn) {
this.continuation = this.continuation.then(fn);
return this;
}
catch() {
this.continuation = this.continuation.catch(...arguments);
catch(fn) {
this.continuation = this.continuation.catch(fn);
return this;
}
finally() {
this.continuation = this.continuation.finally(...arguments);
finally(fn) {
this.continuation = this.continuation.finally(fn);
return this;
}
}
halt(value) {
if (this.isRunning) {
this.exitPrevious();
this.iterator.return(value);
}
class Status {
constructor(execution) {
this.execution = execution;
if (this.isBlocking) {
this.finalize('halted', value);
}
}
start() {
this.cannot('start');
throw(error) {
if (this.isRunning) {
this.thunk(iterator => iterator.throw(error));
} else if (this.isWaiting) {
this.finalize('errored', error);
} else {
throw new Error("\nTried to call Fork#throw() on a Fork that has already been finalized. This\nshould never happen and so is almost assuredly a bug in effection. All of\nits users would be in your eternal debt were you to please take the time to\nreport this issue here:\nhttps://github.com/thefrontside/effection.js/issues/new\n\nThanks!");
}
}
resume() {
this.cannot('resume');
}
resume(value) {
if (this.isUnstarted) {
this.iterator = this.operation.call(this);
this.state = 'running';
this.resume(value);
} else if (this.isRunning) {
this.thunk(iterator => iterator.next(value));
} else {
throw new Error("\nTried to call Fork#resume() on a Fork that has already been finalized. This\nshould never happen and so is almost assuredly a bug in effection. All of\nits users would be in your eternal debt were you to please take the time to\nreport this issue here:\nhttps://github.com/thefrontside/effection.js/issues/new\n\nThanks!");
}
throw() {
this.cannot('throw');
return this;
}
halt() {
this.cannot('halt');
fork(operation) {
let sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
// console.log(`parent.fork(${operation}, ${sync})`);
let child = new Fork(operation, this, sync);
this.children.add(child);
child.resume();
return child;
}
cannot(operationName) {
let name = this.constructor.name;
let message = "tried to perfom operation ".concat(operationName, "() on an execution with status '").concat(name, "'");
throw new Error("InvalidOperationError: ".concat(message));
}
join(child) {
if (!this.children.has(child)) {
return;
}
finalize(status) {
let execution = this.execution;
execution.status = status;
execution.continuation.call(execution);
}
if (!this.isBlocking) {
throw new Error("\nTried to call Fork#join() on a Fork that has already been finalized which means\nthat a sub-fork is being finalized _after_ its parent. This should never happen\nand so is almost assuredly a bug in effection. All of its users would be\nin your eternal debt were you to please take the time to report this issue here:\nhttps://github.com/thefrontside/effection.js/issues/new\n\nThanks!\n");
}
}
if (child.isCompleted) {
if (this.isWaiting && !this.hasBlockingChildren) {
this.finalize('completed');
} else if (this.isRunning) {
this.children.delete(child);
const Finalized = Status => class FinalizedStatus extends Status {
halt() {}
};
class Unstarted extends Status {
start() {
let operation = this.execution.operation;
let execution = this.execution;
let iterator = operation.apply(execution);
execution.status = new Running(execution, iterator);
execution.resume();
if (child.sync) {
this.resume(child.result);
}
}
} else if (child.isErrored) {
this.throw(child.result);
} else if (child.isHalted) {
if (child.sync) {
this.throw(new Error("Interupted: ".concat(child.result)));
} else {
if (!this.hasBlockingChildren) {
this.finalize('completed');
}
}
}
}
}
let currentExecution;
function withCurrentExecution(execution, fn) {
let previousExecution = currentExecution;
try {
currentExecution = execution;
return fn();
} finally {
currentExecution = previousExecution;
}
}
class Running extends Status {
constructor(execution, iterator) {
let current = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
done: false
};
super(execution);
this.iterator = iterator;
this.current = current;
this.noop = x => x;
this.releaseControl = this.noop;
}
thunk(thunk) {
let execution = this.execution,
iterator = this.iterator;
thunk(fn) {
try {
let next = withCurrentExecution(execution, () => {
this.releaseControl();
return thunk(iterator);
});
let next = this.enter(fn);
if (next.done) {
if (execution.hasBlockingChildren) {
execution.status = new Waiting(execution, next.value);
if (this.hasBlockingChildren) {
this.state = 'waiting';
} else {
this.finalize(new Completed(execution, next.value));
this.finalize('completed', next.value);
}
} else {
let control = controllerFor(next.value);
let release = control(execution);
let controller = controllerFor(next.value); /// what happens here if control is synchronous. and resumes execution immediately?
if (typeof release === 'function') {
this.releaseControl = release;
} else {
this.releaseControl = this.noop;
}
let exit = controller(this);
this.exitPrevious = typeof exit === 'function' ? exit : noop;
}
} catch (e) {
// error was thrown, but not caught in the generator.
this.status = new Errored(execution, e);
execution.children.forEach(child => child.halt(e));
this.finalize(new Errored(execution, e));
} catch (error) {
this.finalize('errored', error);
}
}
resume(value) {
this.thunk(iterator => iterator.next(value));
}
enter(fn) {
let previouslyExecuting = Fork.currentlyExecuting;
throw(error) {
this.thunk(iterator => iterator.throw(error));
try {
Fork.currentlyExecuting = this;
this.exitPrevious();
return fn(this.iterator);
} finally {
Fork.currentlyExecuting = previouslyExecuting;
}
}
halt(value) {
let execution = this.execution,
iterator = this.iterator;
this.releaseControl();
iterator.return(value);
execution.status = new Halted(execution, value);
execution.children.forEach(child => {
child.halt(value);
finalize(state, result) {
this.state = state;
this.result = result;
this.children.forEach(child => {
this.children.delete(child);
child.halt(result);
});
this.finalize(new Halted(execution, value));
}
}
class Completed extends Finalized(Status) {
constructor(execution, result) {
super(execution);
this.result = result;
if (this.parent) {
this.parent.join(this);
} else {
this.continuation.call(this);
}
}

@@ -378,42 +349,15 @@

class Errored extends Finalized(Status) {
constructor(execution, error) {
super(execution);
this.result = error;
}
function fork(operation) {
let parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Fork.currentlyExecuting;
}
class Halted extends Finalized(Status) {
constructor(execution, message) {
super(execution);
this.result = message;
if (parent) {
return parent.fork(operation);
} else {
return new Fork(operation).resume();
}
}
class Waiting extends Completed {
halt(value) {
let execution = this.execution;
execution.status = new Halted(execution, value);
execution.children.forEach(child => {
child.halt(value);
});
this.finalize(new Halted(execution, value));
}
throw(error) {
let execution = this.execution;
execution.status = new Errored(execution, error);
execution.children.forEach(child => {
child.halt(error);
});
this.finalize(new Errored(execution, error));
}
}
function controllerFor(value) {
if (isGeneratorFunction(value) || isGenerator(value)) {
return invoke(value);
return parent => parent.fork(value, true);
} else if (typeof value === 'function') {

@@ -430,48 +374,2 @@ return value;

function fork(operation) {
if (currentExecution == null) {
return Execution.start(operation);
}
let parent = currentExecution;
let child = new Execution(operation).then(() => {
if (parent.isWaiting && !parent.hasBlockingChildren) {
parent.status.finalize(new Completed(parent, parent.result));
}
}).catch(e => parent.throw(e));
parent.children.push(child);
child.start();
return child;
}
function invoke(operation) {
return parent => {
let child = new Execution(operation).then(child => {
if (child.isCompleted) {
return parent.resume(child.result);
} // call() is synchronous so the parent is waiting for the
// return value of the child. If the child is halted, and the
// parent is still running, that means its waiting on the child
// and so it's an error because the child was interupted
if (child.isHalted && parent.isRunning) {
return parent.throw(new Error("Interupted: ".concat(child.result)));
}
}).catch(e => parent.throw(e));
parent.children.push(child);
child.start();
};
}
const ExecutionFinalized = Continuation.of(execution => {
if (execution.isErrored) {
let error = execution.result;
error.execution = execution;
throw error;
} else {
return execution;
}
});
export { fork, promiseOf, timeout };
{
"name": "effection",
"description": "Effortlessly composable structured concurrency primitive for JavaScript",
"version": "0.3.0",
"version": "0.3.1-828df92",
"license": "MIT",

@@ -6,0 +6,0 @@ "files": [

@@ -117,8 +117,7 @@ [![npm](https://img.shields.io/npm/v/effection.svg)](https://www.npmjs.com/package/effection)

In order to abstract a process so that it can take arguments, you can
use the `call` function:
You can pass arguments to an operation by invoking it.
``` javascript
import { fork, timeout, call } from 'effection';
import { fork, timeout } from 'effection';

@@ -129,19 +128,5 @@ function* waitForSeconds(durationSeconds) {

fork(function*() {
yield call(waitforseconds, 10);
});
fork(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

@@ -157,3 +142,3 @@

``` javascript
import { fork, fork } from 'effection';
import { fork } from 'effection';

@@ -160,0 +145,0 @@ fork(function*() {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc