effection
Advanced tools
Comparing version 0.3.3 to 0.4.0-8761532
@@ -9,2 +9,19 @@ # Changelog | ||
## [0.4.0] - 2019-11-20 | ||
### Added | ||
- add a monotonic `id` field to every fork to help with debugging | ||
https://github.com/thefrontside/effection.js/pull/32 | ||
### Changed | ||
- (fix) do not swallow some errors that are thrown inside of a yield | ||
point destructor: https://github.com/thefrontside/effection.js/pull/37 | ||
- Make every fork conform to the Promises A+ | ||
API. E.g. `fork(operation).then()` | ||
https://github.com/thefrontside/effection.js/pull/38 | ||
## [0.3.3] - 2019-11-04 | ||
@@ -11,0 +28,0 @@ |
@@ -33,5 +33,11 @@ 'use strict'; | ||
let noop = x => x; | ||
let noop = x => x; // return values of succeed and fail are deliberately ignored. | ||
// see https://github.com/thefrontside/effection.js/pull/44 | ||
promise.then(value => succeed(value)).catch(error => fail(error)); // this execution has passed out of scope, so we don't care | ||
promise.then(value => { | ||
succeed(value); | ||
}, error => { | ||
fail(error); | ||
}); // this execution has passed out of scope, so we don't care | ||
// what happened to the promise, so make the callbacks noops. | ||
@@ -76,75 +82,17 @@ // this effectively "unsubscribes" to the promise. | ||
/** | ||
* Function composition that implements the `Promise` API; | ||
* | ||
* // f(g(x)) | ||
* Contiunation.of(g).then(f); | ||
*/ | ||
class Continuation { | ||
static of(fn) { | ||
return new Continuation(fn); | ||
} | ||
static resolve(value) { | ||
return Continuation.of(() => value); | ||
} | ||
static reject(error) { | ||
return Continuation.of(() => { | ||
throw error; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
static empty() { | ||
return Continuation.of(x => x); | ||
} | ||
constructor(call) { | ||
this.call = call; | ||
} | ||
then(fn) { | ||
return flatMap(this, call => Continuation.of(x => { | ||
let next = fn(call(x)); | ||
if (next instanceof Continuation) { | ||
return next.call(x); | ||
} else { | ||
return next; | ||
} | ||
})); | ||
} | ||
catch(fn) { | ||
return flatMap(this, call => Continuation.of(x => { | ||
try { | ||
return call(x); | ||
} catch (e) { | ||
return fn(e); | ||
} | ||
})); | ||
} | ||
finally(fn) { | ||
return flatMap(this, call => Continuation.of(x => { | ||
try { | ||
return call(x); | ||
} finally { | ||
fn(x); | ||
} | ||
})); | ||
} | ||
return obj; | ||
} | ||
function flatMap(continuation, sequence) { | ||
let next = sequence(continuation.call); | ||
if (!(next instanceof Continuation)) { | ||
throw new Error(`chaining function must return a Continuation, not '${next}`); | ||
} | ||
return next; | ||
} | ||
class Fork { | ||
@@ -176,3 +124,3 @@ get isUnstarted() { | ||
get isBlocking() { | ||
return this.isRunning || this.isWaiting; | ||
return this.isRunning || this.isWaiting || this.isUnstarted; | ||
} | ||
@@ -212,2 +160,3 @@ | ||
constructor(operation, parent, sync) { | ||
this.id = Fork.ids++; | ||
this.operation = toGeneratorFunction(operation); | ||
@@ -219,26 +168,23 @@ this.parent = parent; | ||
this.exitPrevious = noop; | ||
this.continuation = Continuation.of(frame => { | ||
if (frame.isErrored) { | ||
let error = frame.result; | ||
error.frame = frame; | ||
throw error; | ||
} else { | ||
return frame; | ||
} | ||
} | ||
get promise() { | ||
this._promise = new Promise((resolve, reject) => { | ||
this.resolve = resolve; | ||
this.reject = reject; | ||
}); | ||
this.finalizePromise(); | ||
return this._promise; | ||
} | ||
then(fn) { | ||
this.continuation = this.continuation.then(fn); | ||
return this; | ||
then(...args) { | ||
return this.promise.then(...args); | ||
} | ||
catch(fn) { | ||
this.continuation = this.continuation.catch(fn); | ||
return this; | ||
catch(...args) { | ||
return this.promise.catch(...args); | ||
} | ||
finally(fn) { | ||
this.continuation = this.continuation.finally(fn); | ||
return this; | ||
finally(...args) { | ||
return this.promise.finally(...args); | ||
} | ||
@@ -296,6 +242,7 @@ | ||
fork(operation, sync = false) { | ||
// console.log(`parent.fork(${operation}, ${sync})`); | ||
let child = new Fork(operation, this, sync); | ||
this.children.add(child); | ||
child.resume(); | ||
setTimeout(() => { | ||
child.resume(); | ||
}, 0); | ||
return child; | ||
@@ -351,5 +298,16 @@ } | ||
thunk(fn) { | ||
let next; | ||
let previouslyExecuting = Fork.currentlyExecuting; | ||
try { | ||
let next = this.enter(fn); | ||
Fork.currentlyExecuting = this; | ||
this.exitPrevious(); | ||
try { | ||
next = fn(this.iterator); | ||
} catch (error) { | ||
this.finalize('errored', error); | ||
return; | ||
} | ||
if (next.done) { | ||
@@ -367,14 +325,2 @@ if (this.hasBlockingChildren) { | ||
} | ||
} catch (error) { | ||
this.finalize('errored', error); | ||
} | ||
} | ||
enter(fn) { | ||
let previouslyExecuting = Fork.currentlyExecuting; | ||
try { | ||
Fork.currentlyExecuting = this; | ||
this.exitPrevious(); | ||
return fn(this.iterator); | ||
} finally { | ||
@@ -395,4 +341,22 @@ Fork.currentlyExecuting = previouslyExecuting; | ||
this.parent.join(this); | ||
} | ||
this.finalizePromise(); | ||
} | ||
finalizePromise() { | ||
if (this.isCompleted && this.resolve) { | ||
this.resolve(this.result); | ||
} else if (this.isErrored && this.reject) { | ||
this.reject(this.result); | ||
} else if (this.isHalted && this.reject) { | ||
this.reject(new HaltError(this.result)); | ||
} | ||
} | ||
get root() { | ||
if (this.parent) { | ||
return this.parent.root; | ||
} else { | ||
this.continuation.call(this); | ||
return this; | ||
} | ||
@@ -403,2 +367,4 @@ } | ||
_defineProperty(Fork, "ids", 0); | ||
function fork(operation, parent = Fork.currentlyExecuting) { | ||
@@ -412,2 +378,10 @@ if (parent) { | ||
class HaltError extends Error { | ||
constructor(cause) { | ||
super("halt"); | ||
this.cause = cause; | ||
} | ||
} | ||
function controllerFor(value) { | ||
@@ -420,3 +394,3 @@ if (isGeneratorFunction(value) || isGenerator(value)) { | ||
return x => x; | ||
} else if (typeof value.then === 'function' && typeof value.catch === 'function') { | ||
} else if (typeof value.then === 'function') { | ||
return promiseOf(value); | ||
@@ -429,3 +403,2 @@ } else { | ||
exports.fork = fork; | ||
exports.promiseOf = promiseOf; | ||
exports.timeout = timeout; |
@@ -0,5 +1,6 @@ | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
import { noop } from "./noop.js"; | ||
import { promiseOf } from "./promise-of.js"; | ||
import { isGenerator, isGeneratorFunction, toGeneratorFunction } from "./generator-function.js"; | ||
import Continuation from "./continuation.js"; | ||
@@ -32,3 +33,3 @@ class Fork { | ||
get isBlocking() { | ||
return this.isRunning || this.isWaiting; | ||
return this.isRunning || this.isWaiting || this.isUnstarted; | ||
} | ||
@@ -47,2 +48,3 @@ | ||
constructor(operation, parent, sync) { | ||
this.id = Fork.ids++; | ||
this.operation = toGeneratorFunction(operation); | ||
@@ -54,26 +56,23 @@ this.parent = parent; | ||
this.exitPrevious = noop; | ||
this.continuation = Continuation.of(frame => { | ||
if (frame.isErrored) { | ||
let error = frame.result; | ||
error.frame = frame; | ||
throw error; | ||
} else { | ||
return frame; | ||
} | ||
} | ||
get promise() { | ||
this._promise = new Promise((resolve, reject) => { | ||
this.resolve = resolve; | ||
this.reject = reject; | ||
}); | ||
this.finalizePromise(); | ||
return this._promise; | ||
} | ||
then(fn) { | ||
this.continuation = this.continuation.then(fn); | ||
return this; | ||
then(...args) { | ||
return this.promise.then(...args); | ||
} | ||
catch(fn) { | ||
this.continuation = this.continuation.catch(fn); | ||
return this; | ||
catch(...args) { | ||
return this.promise.catch(...args); | ||
} | ||
finally(fn) { | ||
this.continuation = this.continuation.finally(fn); | ||
return this; | ||
finally(...args) { | ||
return this.promise.finally(...args); | ||
} | ||
@@ -131,6 +130,7 @@ | ||
fork(operation, sync = false) { | ||
// console.log(`parent.fork(${operation}, ${sync})`); | ||
let child = new Fork(operation, this, sync); | ||
this.children.add(child); | ||
child.resume(); | ||
setTimeout(() => { | ||
child.resume(); | ||
}, 0); | ||
return child; | ||
@@ -186,5 +186,16 @@ } | ||
thunk(fn) { | ||
let next; | ||
let previouslyExecuting = Fork.currentlyExecuting; | ||
try { | ||
let next = this.enter(fn); | ||
Fork.currentlyExecuting = this; | ||
this.exitPrevious(); | ||
try { | ||
next = fn(this.iterator); | ||
} catch (error) { | ||
this.finalize('errored', error); | ||
return; | ||
} | ||
if (next.done) { | ||
@@ -202,14 +213,2 @@ if (this.hasBlockingChildren) { | ||
} | ||
} catch (error) { | ||
this.finalize('errored', error); | ||
} | ||
} | ||
enter(fn) { | ||
let previouslyExecuting = Fork.currentlyExecuting; | ||
try { | ||
Fork.currentlyExecuting = this; | ||
this.exitPrevious(); | ||
return fn(this.iterator); | ||
} finally { | ||
@@ -230,4 +229,22 @@ Fork.currentlyExecuting = previouslyExecuting; | ||
this.parent.join(this); | ||
} | ||
this.finalizePromise(); | ||
} | ||
finalizePromise() { | ||
if (this.isCompleted && this.resolve) { | ||
this.resolve(this.result); | ||
} else if (this.isErrored && this.reject) { | ||
this.reject(this.result); | ||
} else if (this.isHalted && this.reject) { | ||
this.reject(new HaltError(this.result)); | ||
} | ||
} | ||
get root() { | ||
if (this.parent) { | ||
return this.parent.root; | ||
} else { | ||
this.continuation.call(this); | ||
return this; | ||
} | ||
@@ -238,2 +255,4 @@ } | ||
_defineProperty(Fork, "ids", 0); | ||
export function fork(operation, parent = Fork.currentlyExecuting) { | ||
@@ -247,2 +266,10 @@ if (parent) { | ||
class HaltError extends Error { | ||
constructor(cause) { | ||
super("halt"); | ||
this.cause = cause; | ||
} | ||
} | ||
function controllerFor(value) { | ||
@@ -255,3 +282,3 @@ if (isGeneratorFunction(value) || isGenerator(value)) { | ||
return x => x; | ||
} else if (typeof value.then === 'function' && typeof value.catch === 'function') { | ||
} else if (typeof value.then === 'function') { | ||
return promiseOf(value); | ||
@@ -258,0 +285,0 @@ } else { |
export { timeout } from "./timeout.js"; | ||
export { fork } from "./fork.js"; | ||
export { promiseOf } from "./promise-of.js"; | ||
export { fork } from "./fork.js"; |
@@ -11,5 +11,11 @@ /** | ||
let noop = x => x; | ||
let noop = x => x; // return values of succeed and fail are deliberately ignored. | ||
// see https://github.com/thefrontside/effection.js/pull/44 | ||
promise.then(value => succeed(value)).catch(error => fail(error)); // this execution has passed out of scope, so we don't care | ||
promise.then(value => { | ||
succeed(value); | ||
}, error => { | ||
fail(error); | ||
}); // this execution has passed out of scope, so we don't care | ||
// what happened to the promise, so make the callbacks noops. | ||
@@ -16,0 +22,0 @@ // this effectively "unsubscribes" to the promise. |
@@ -30,5 +30,11 @@ /** | ||
let noop = x => x; | ||
let noop = x => x; // return values of succeed and fail are deliberately ignored. | ||
// see https://github.com/thefrontside/effection.js/pull/44 | ||
promise.then(value => succeed(value)).catch(error => fail(error)); // this execution has passed out of scope, so we don't care | ||
promise.then(value => { | ||
succeed(value); | ||
}, error => { | ||
fail(error); | ||
}); // this execution has passed out of scope, so we don't care | ||
// what happened to the promise, so make the callbacks noops. | ||
@@ -77,75 +83,17 @@ // this effectively "unsubscribes" to the promise. | ||
/** | ||
* Function composition that implements the `Promise` API; | ||
* | ||
* // f(g(x)) | ||
* Contiunation.of(g).then(f); | ||
*/ | ||
class Continuation { | ||
static of(fn) { | ||
return new Continuation(fn); | ||
} | ||
static resolve(value) { | ||
return Continuation.of(() => value); | ||
} | ||
static reject(error) { | ||
return Continuation.of(() => { | ||
throw error; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
static empty() { | ||
return Continuation.of(x => x); | ||
} | ||
constructor(call) { | ||
this.call = call; | ||
} | ||
then(fn) { | ||
return flatMap(this, call => Continuation.of(x => { | ||
let next = fn(call(x)); | ||
if (next instanceof Continuation) { | ||
return next.call(x); | ||
} else { | ||
return next; | ||
} | ||
})); | ||
} | ||
catch(fn) { | ||
return flatMap(this, call => Continuation.of(x => { | ||
try { | ||
return call(x); | ||
} catch (e) { | ||
return fn(e); | ||
} | ||
})); | ||
} | ||
finally(fn) { | ||
return flatMap(this, call => Continuation.of(x => { | ||
try { | ||
return call(x); | ||
} finally { | ||
fn(x); | ||
} | ||
})); | ||
} | ||
return obj; | ||
} | ||
function flatMap(continuation, sequence) { | ||
let next = sequence(continuation.call); | ||
if (!(next instanceof Continuation)) { | ||
throw new Error("chaining function must return a Continuation, not '".concat(next)); | ||
} | ||
return next; | ||
} | ||
class Fork { | ||
@@ -177,3 +125,3 @@ get isUnstarted() { | ||
get isBlocking() { | ||
return this.isRunning || this.isWaiting; | ||
return this.isRunning || this.isWaiting || this.isUnstarted; | ||
} | ||
@@ -192,2 +140,3 @@ | ||
constructor(operation, parent, sync) { | ||
this.id = Fork.ids++; | ||
this.operation = toGeneratorFunction(operation); | ||
@@ -199,26 +148,23 @@ this.parent = parent; | ||
this.exitPrevious = noop; | ||
this.continuation = Continuation.of(frame => { | ||
if (frame.isErrored) { | ||
let error = frame.result; | ||
error.frame = frame; | ||
throw error; | ||
} else { | ||
return frame; | ||
} | ||
} | ||
get promise() { | ||
this._promise = new Promise((resolve, reject) => { | ||
this.resolve = resolve; | ||
this.reject = reject; | ||
}); | ||
this.finalizePromise(); | ||
return this._promise; | ||
} | ||
then(fn) { | ||
this.continuation = this.continuation.then(fn); | ||
return this; | ||
then() { | ||
return this.promise.then(...arguments); | ||
} | ||
catch(fn) { | ||
this.continuation = this.continuation.catch(fn); | ||
return this; | ||
catch() { | ||
return this.promise.catch(...arguments); | ||
} | ||
finally(fn) { | ||
this.continuation = this.continuation.finally(fn); | ||
return this; | ||
finally() { | ||
return this.promise.finally(...arguments); | ||
} | ||
@@ -263,6 +209,7 @@ | ||
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(); | ||
setTimeout(() => { | ||
child.resume(); | ||
}, 0); | ||
return child; | ||
@@ -304,5 +251,16 @@ } | ||
thunk(fn) { | ||
let next; | ||
let previouslyExecuting = Fork.currentlyExecuting; | ||
try { | ||
let next = this.enter(fn); | ||
Fork.currentlyExecuting = this; | ||
this.exitPrevious(); | ||
try { | ||
next = fn(this.iterator); | ||
} catch (error) { | ||
this.finalize('errored', error); | ||
return; | ||
} | ||
if (next.done) { | ||
@@ -320,14 +278,2 @@ if (this.hasBlockingChildren) { | ||
} | ||
} catch (error) { | ||
this.finalize('errored', error); | ||
} | ||
} | ||
enter(fn) { | ||
let previouslyExecuting = Fork.currentlyExecuting; | ||
try { | ||
Fork.currentlyExecuting = this; | ||
this.exitPrevious(); | ||
return fn(this.iterator); | ||
} finally { | ||
@@ -348,4 +294,22 @@ Fork.currentlyExecuting = previouslyExecuting; | ||
this.parent.join(this); | ||
} | ||
this.finalizePromise(); | ||
} | ||
finalizePromise() { | ||
if (this.isCompleted && this.resolve) { | ||
this.resolve(this.result); | ||
} else if (this.isErrored && this.reject) { | ||
this.reject(this.result); | ||
} else if (this.isHalted && this.reject) { | ||
this.reject(new HaltError(this.result)); | ||
} | ||
} | ||
get root() { | ||
if (this.parent) { | ||
return this.parent.root; | ||
} else { | ||
this.continuation.call(this); | ||
return this; | ||
} | ||
@@ -356,2 +320,4 @@ } | ||
_defineProperty(Fork, "ids", 0); | ||
function fork(operation) { | ||
@@ -367,2 +333,10 @@ let parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Fork.currentlyExecuting; | ||
class HaltError extends Error { | ||
constructor(cause) { | ||
super("halt"); | ||
this.cause = cause; | ||
} | ||
} | ||
function controllerFor(value) { | ||
@@ -375,3 +349,3 @@ if (isGeneratorFunction(value) || isGenerator(value)) { | ||
return x => x; | ||
} else if (typeof value.then === 'function' && typeof value.catch === 'function') { | ||
} else if (typeof value.then === 'function') { | ||
return promiseOf(value); | ||
@@ -383,2 +357,2 @@ } else { | ||
export { fork, promiseOf, timeout }; | ||
export { fork, timeout }; |
declare module "effection" { | ||
export type Operation = SequenceFn | Sequence | Promise<any> | Controller | undefined; | ||
export type Operation = SequenceFn | Sequence | Promise<any> | Controller | Execution<any> | undefined; | ||
export type SequenceFn = (this: Execution) => Sequence; | ||
@@ -8,6 +8,9 @@ export type Controller = (execution: Execution) => void | (() => void); | ||
export interface Execution<T = any> { | ||
export interface Execution<T = any> extends PromiseLike<any> { | ||
id: number; | ||
resume(result: T): void; | ||
throw(error: Error): void; | ||
halt(reason?: any): void; | ||
catch<R>(fn: (Error) => R): Promise<R>; | ||
finally(fn: () => void): Promise<any>; | ||
} | ||
@@ -14,0 +17,0 @@ |
{ | ||
"name": "effection", | ||
"description": "Effortlessly composable structured concurrency primitive for JavaScript", | ||
"version": "0.3.3", | ||
"version": "0.4.0-8761532", | ||
"license": "MIT", | ||
@@ -20,2 +20,3 @@ "files": [ | ||
"@babel/core": "7.4.4", | ||
"@babel/plugin-proposal-class-properties": "^7.7.0", | ||
"@babel/preset-env": "7.4.4", | ||
@@ -22,0 +23,0 @@ "@babel/register": "7.4.4", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38050
18
13
927