@debonet/es6pacts
v3.0.0
Published
Releasable/cancellable and Reporting Promises for Javascript ES6
Downloads
83
Maintainers
Readme
es6pacts
Releasable, reporting Promises for JavaScript ES6.
TL;DR
- A Pact is a Promise that can be released from its obligation (
release,resolve,reject,fulfill) and that delivers progress reports to independent observers (.progress). - One line of composition:
fclassReleasable( Task ). Release semantics come from@debonet/es6pledges; reporting semantics come from@debonet/es6tasks. - The constructor takes a single executor argument,
( fResolve, fReject, fReport ) => xPolicy. The executor's RETURN VALUE is the release policy; the v2 second constructor argument is removed. - Progress reports stop when the pact settles. This is a library guarantee (post-settle suppression), not executor discipline.
Example
const Pact = require( "@debonet/es6pacts" );
function fpactDelay( dtm, dtmTick = 100 ){
return new Pact(( fResolve, fReject, fReport ) => {
let dtmElapsed = 0;
const interval = setInterval(() => {
dtmElapsed += dtmTick;
fReport( dtmElapsed );
if ( dtmElapsed >= dtm ){
clearInterval( interval );
fResolve( dtm );
}
}, dtmTick );
return () => clearInterval( interval );
});
}Hold the SOURCE pact and release it; cancellation flows downstream as ordinary settlement:
const pactDelay = fpactDelay( 10000 );
pactDelay
.progress(( dtmElapsed ) => console.log( "progress:", dtmElapsed ))
.then(( x ) => console.log( "result:", x ));
setTimeout(() => { pactDelay.resolve( "faster" ); }, 250 );Output:
progress: 100
progress: 200
result: faster- The release at 250ms lands mid-gap between the 200ms and 300ms ticks, so the output is stable.
- The cleanup is the executor's return value:
return () => clearInterval( interval );. It clears the interval, and post-settle suppression guarantees no report after settlement. - The usage releases the source pact, not a chain tail. Releasing a derived pact settles only that pact and skips its own handler (see Chains).
Same example, three libraries: remove the fReport executor parameter and the fReport( dtmElapsed ); line and this is fpledgeDelay (@debonet/es6pledges — releasable, no reporting). Remove instead the return () => clearInterval( interval ); line and this is ftaskDelay (@debonet/es6tasks — reporting, not releasable). All other lines are identical across the three libraries. Pact is the union, exactly as fclassReleasable( Task ) is the union of the two libraries.
Chain Windowing
Using the same fpactDelay, attachment position selects what a .progress observer sees:
const pactFirst = fpactDelay( 10000 );
const pactDone = pactFirst
.progress(( dtm ) => console.log( "first stage:", dtm ))
.then(( x ) => fpactDelay( 300 ))
.progress(( dtm ) => console.log( "whole chain:", dtm ));
setTimeout(() => pactFirst.resolve( "faster" ), 350 );
pactDone.then(( x ) => console.log( "result:", x ));Output:
first stage: 100
whole chain: 100
first stage: 200
whole chain: 200
first stage: 300
whole chain: 300
whole chain: 100
whole chain: 200
whole chain: 300
result: 300What the example shows:
- The
.progressattached BEFORE the.thenobserves onlypactFirst's stage. - The
.progressattached AFTER the.thenobserves the forwarded parent stream plus the stream of the pact the handler returns. - The release arrives mid-chain:
pactFirst.resolve( "faster" )settles the first stage, its reports stop (post-settle suppression), and the early value flows downstream as ordinary settlement.
The Executor
new Pact( fxExecutor ) takes ONE argument. Task supplies fReport as the executor's third parameter, so the full signature is ( fResolve, fReject, fReport ) => xPolicy. The return value xPolicy is the release policy:
- A function
( xReason ) => ...— the release handler. It closes overfResolve,fReject,fReport, and the executor's locals (the useEffect-cleanup idiom). - An Error instance — unconditional refusal: every
release()rejects with it. undefinedor anything else — releasable with no cleanup.
Release
pact.release( xReason ) runs a consent pipeline: the policy may clean up and consent, or refuse. On consent the pact settles by type dispatch on xReason:
PactResolve→ resolves with its causePactReject→ rejects with its cause- any other Error → rejects with it
- anything else → resolves with it
Pact.PactResolve and Pact.PactReject are statics on the class.
The release() return contract:
- FULFILLS with a settlement record
{ sStatus, x }after the pact settles, ordered after handlers registered before the call. - REJECTS only on refusal (the release handler threw or rejected) while the pact was still pending. A refused pact stays pending, keeps all handlers, and keeps reporting.
Settlement is a report that the obligation ended; nothing settles the pact without the release handler's consent. No forced settlement exists.
Settle Verbs
pact.resolve( x )≡pact.release( new PactResolve( x ))pact.reject( e )≡pact.release( new PactReject( e ))pact.fulfill( x )— alias ofresolve( x )
All refusable, same pipeline. The instance resolve / reject are distinct from the inherited statics Pact.resolve / Pact.reject, which construct already-settled pacts.
releaseOn
pact.releaseOn( signal ) requests a release when the AbortSignal fires, with signal.reason as xReason; it returns this. A bare abort() produces an AbortError, which rejects by dispatch; abort( xValue ) resolves with the value. This is a refusable request, not an unconditional deadline.
Chains
Cancel a pipeline at its SOURCE: release the base pact you hold, and cancellation flows downstream as ordinary settlement. Releasing a derived pact never affects an ancestor; it settles the derived pact (and at most one owned child: an in-flight pact returned by its own stage handler).
Reporting
pact.progress( f )registers an independent observer and returnsthis(chainable).- Every observer receives the SAME raw value passed to
fReport, in attachment order. Observer return values are ignored. - Delivery is queued on a microtask. There is no replay buffer: attach late, miss earlier reports.
- Once the pact settles, further reports are dropped by the library.
- Derived pacts forward their parent's report stream, plus the stream of any pact a handler returns. Attachment position selects the observation window (see Chain Windowing).
- Combinators (
Pact.all,any,race,allSettled) re-emit member reports as{ task : n, report : x }and suppress them once the combined pact settles. Releasing a combined pact settles the combined pact only; members are not owned and keep running.
Interlocking Guarantees
- A released pact is a settled pact, so its reports stop — suppression is central, in the library.
- A refused release leaves the pact pending, so reports keep flowing during and after the refusal.
Full Semantics
This module adds no behavior of its own; the complete contracts live upstream:
@debonet/es6pledgesspecs/v3-core.md— constructor, executor, policy, pipeline, dispatch, wrappers, release contract, verbs,releaseOn.@debonet/es6pledgesspecs/v3-chains-combinators.md— chain and combinator release.@debonet/es6tasksspecs/v3-reporting.md— observer model, microtask delivery, post-settle suppression, windowing, combinator report format.
See Also
- @debonet/es6pledges
- @debonet/es6tasks
