effection
Advanced tools
Comparing version 0.3.0-0b2d0a6 to 0.3.0-cb3050a
@@ -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-0b2d0a6", | ||
"version": "0.3.0-cb3050a", | ||
"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*() { |
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
36934
996
168
1