diff --git a/README.md b/README.md
index 9b1f2ed..16c21b5 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
## SYNOPSIS :sparkles:
-CPromise is a subclass of the Promise provided by the environment with some extra features
+CPromise is built on top of the native Promise provided by the environment with some extra features
like cancellation, timeouts and progress capturing.
In terms of the library **the cancellation means rejection of the deepest promise in
@@ -22,6 +22,23 @@ You may face with a challenge when you need to cancel some long-term asynchronou
operation before it will be completed with success or failure, just because the result
has lost its relevance to you.
+## Features / Advantages
+- there are no any dependencies (except [native] Promise), built-in `AbortController`
+- browser support
+- supports two ways to make your promise internal code cancellable:
+ - `onCancel` callbacks (clear timers, abort requests)
+ - `signal` provided by the [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) (to wrap API like
+[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) method)
+- :fire: supports cancellation of the whole chain - rejects the deepest pending promise in the chain
+- :fire: supports generator to CPromise resolving (something similar like [co](https://www.npmjs.com/package/co) library does);
+- :fire: progress capturing with result scaling to handle progress of the whole chain (including nested promise chains), useful for long-term operations
+- ability to set the `weight` for each promise in the chain to manage the impact on chain progress
+- ability to attach meta info on each setting of the progress
+- the `delay` method to return promise that will be resolved with the value after timeout
+- static methods `all`, `race` support cancellation and will cancel all other pending
+ promises after the result promise settled
+- the `catch` method supports error class filtering
+
## Live Example
This is how an abortable fetch ([live example](https://jsfiddle.net/DigitalBrain/c6njyrt9/10/)) with a timeout might look like
@@ -72,22 +89,6 @@ callbacks attached by `onCancel(cb)` method and/or propagate with signal from `A
These api can be used simultaneously. The `cancel([reason])` method is synchronous and can be called any time.
If cancellation failed (the chain has been already fulfilled) it will return `false`.
-## Features / Advantages
-- there are no any dependencies (except [native] Promise)
-- browser support
-- :fire: supports cancellation of the whole chain - rejects the deepest pending promise in the chain
-- supports onCancel event handler to abort some internal work (clear timers, close requests etc.)
-- supports built-in signal interface for API that supports it (like fetch method)
-- :fire: supports generator to CPromise resolving (something similar like [co](https://www.npmjs.com/package/co) library does);
-- proper handling of `CanceledError` errors manually thrown inside the chain
-- :fire: progress capturing with result scaling to handle progress of the whole chain (including nested promise chains), useful for long-term operations
-- ability to set the `weight` for each promise in the chain
-- ability to attach meta info on each setting of the progress
-- the `delay` method to return promise that will be resolved with the value after timeout
-- static methods `all`, `race` support cancellation and will cancel all other pending
- promises after the result promise settled
-- the `catch` method supports error class filtering
-
## Installation :hammer:
- Install for node.js using npm/yarn:
@@ -241,6 +242,11 @@ Cancellable Promise with extra features
* [CPromise](#module_CPromise)
+ * [~AsyncGeneratorScope](#module_CPromise..AsyncGeneratorScope)
+ * [new AsyncGeneratorScope(scope)](#new_module_CPromise..AsyncGeneratorScope_new)
+ * [.scope](#module_CPromise..AsyncGeneratorScope+scope) ⇒ CPromiseScope
+ * [.totalWeight](#module_CPromise..AsyncGeneratorScope+totalWeight) ⇒ Number
+ * [.captureProgress(totalWeight, throttle)](#module_CPromise..AsyncGeneratorScope+captureProgress)
* [~CPromiseScope](#module_CPromise..CPromiseScope) ⇐ TinyEventEmitter
* [new CPromiseScope(resolve, reject, options)](#new_module_CPromise..CPromiseScope_new)
* _instance_
@@ -288,6 +294,51 @@ Cancellable Promise with extra features
* [~CPromiseExecutorFn](#module_CPromise..CPromiseExecutorFn) : function
* [~CPromiseOptions](#module_CPromise..CPromiseOptions) : Object
\| String
\| Number
+
+
+### CPromise~AsyncGeneratorScope
+Scope for generator resolvers
+
+**Kind**: inner class of [CPromise
](#module_CPromise)
+
+* [~AsyncGeneratorScope](#module_CPromise..AsyncGeneratorScope)
+ * [new AsyncGeneratorScope(scope)](#new_module_CPromise..AsyncGeneratorScope_new)
+ * [.scope](#module_CPromise..AsyncGeneratorScope+scope) ⇒ CPromiseScope
+ * [.totalWeight](#module_CPromise..AsyncGeneratorScope+totalWeight) ⇒ Number
+ * [.captureProgress(totalWeight, throttle)](#module_CPromise..AsyncGeneratorScope+captureProgress)
+
+
+
+#### new AsyncGeneratorScope(scope)
+Creates a new AsyncGeneratorScope instance
+
+
+| Param | Type |
+| --- | --- |
+| scope | CPromiseScope
|
+
+
+
+#### asyncGeneratorScope.scope ⇒ CPromiseScope
+Promise scope related to the generator
+
+**Kind**: instance property of [AsyncGeneratorScope
](#module_CPromise..AsyncGeneratorScope)
+
+
+#### asyncGeneratorScope.totalWeight ⇒ Number
+total weight of the inner chains produced by the generator
+
+**Kind**: instance property of [AsyncGeneratorScope
](#module_CPromise..AsyncGeneratorScope)
+
+
+#### asyncGeneratorScope.captureProgress(totalWeight, throttle)
+**Kind**: instance method of [AsyncGeneratorScope
](#module_CPromise..AsyncGeneratorScope)
+
+| Param | Type | Description |
+| --- | --- | --- |
+| totalWeight | Number
| total weight if generator inner chains |
+| throttle | Number
| |
+
### CPromise~CPromiseScope ⇐ TinyEventEmitter
diff --git a/jsdoc2md/README.hbs.md b/jsdoc2md/README.hbs.md
index 7209643..ef0ac4b 100644
--- a/jsdoc2md/README.hbs.md
+++ b/jsdoc2md/README.hbs.md
@@ -6,7 +6,7 @@
## SYNOPSIS :sparkles:
-CPromise is a subclass of the Promise provided by the environment with some extra features
+CPromise is built on top of the native Promise provided by the environment with some extra features
like cancellation, timeouts and progress capturing.
In terms of the library **the cancellation means rejection of the deepest promise in
@@ -22,6 +22,23 @@ You may face with a challenge when you need to cancel some long-term asynchronou
operation before it will be completed with success or failure, just because the result
has lost its relevance to you.
+## Features / Advantages
+- there are no any dependencies (except [native] Promise), built-in `AbortController`
+- browser support
+- supports two ways to make your promise internal code cancellable:
+ - `onCancel` callbacks (clear timers, abort requests)
+ - `signal` provided by the [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) (to wrap API like
+[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) method)
+- :fire: supports cancellation of the whole chain - rejects the deepest pending promise in the chain
+- :fire: supports generator to CPromise resolving (something similar like [co](https://www.npmjs.com/package/co) library does);
+- :fire: progress capturing with result scaling to handle progress of the whole chain (including nested promise chains), useful for long-term operations
+- ability to set the `weight` for each promise in the chain to manage the impact on chain progress
+- ability to attach meta info on each setting of the progress
+- the `delay` method to return promise that will be resolved with the value after timeout
+- static methods `all`, `race` support cancellation and will cancel all other pending
+ promises after the result promise settled
+- the `catch` method supports error class filtering
+
## Live Example
This is how an abortable fetch ([live example](https://jsfiddle.net/DigitalBrain/c6njyrt9/10/)) with a timeout might look like
@@ -72,22 +89,6 @@ callbacks attached by `onCancel(cb)` method and/or propagate with signal from `A
These api can be used simultaneously. The `cancel([reason])` method is synchronous and can be called any time.
If cancellation failed (the chain has been already fulfilled) it will return `false`.
-## Features / Advantages
-- there are no any dependencies (except [native] Promise)
-- browser support
-- :fire: supports cancellation of the whole chain - rejects the deepest pending promise in the chain
-- supports onCancel event handler to abort some internal work (clear timers, close requests etc.)
-- supports built-in signal interface for API that supports it (like fetch method)
-- :fire: supports generator to CPromise resolving (something similar like [co](https://www.npmjs.com/package/co) library does);
-- proper handling of `CanceledError` errors manually thrown inside the chain
-- :fire: progress capturing with result scaling to handle progress of the whole chain (including nested promise chains), useful for long-term operations
-- ability to set the `weight` for each promise in the chain
-- ability to attach meta info on each setting of the progress
-- the `delay` method to return promise that will be resolved with the value after timeout
-- static methods `all`, `race` support cancellation and will cancel all other pending
- promises after the result promise settled
-- the `catch` method supports error class filtering
-
## Installation :hammer:
- Install for node.js using npm/yarn:
diff --git a/lib/c-promise.js b/lib/c-promise.js
index f64c340..3aaa398 100644
--- a/lib/c-promise.js
+++ b/lib/c-promise.js
@@ -63,28 +63,72 @@ function isGenerator(thing) {
const iterableToArray = Array.from ? Array.from : (iterable) => Array.prototype.slice.call(iterable);
-function resolveGenerator(generatorFn, args) {
- return new this((resolve, reject, scope) => {
- let totalWeight = 1;
+/**
+ * Scope for generator resolvers
+ */
- const context = {
- scope,
- captureProgress: (weight) => {
- if (typeof weight !== 'number') {
- throw TypeError('weight must be a number');
- }
- if (weight < 1) {
- throw Error('weight must be grater than 0');
- }
- totalWeight = weight;
- }
+class AsyncGeneratorScope {
+ /**
+ * Creates a new AsyncGeneratorScope instance
+ * @param {CPromiseScope} scope
+ */
+ constructor(scope) {
+ this[_shadow]= {
+ totalWeight: 1,
+ scope
+ }
+ }
+
+ /**
+ * Promise scope related to the generator
+ * @return {CPromiseScope}
+ */
+
+ get scope(){
+ return this[_shadow].scope;
+ }
+
+ /**
+ * total weight of the inner chains produced by the generator
+ * @return {Number}
+ */
+
+ get totalWeight(){
+ return this[_shadow].totalWeight;
+ }
+
+ set totalWeight(value){
+ if (typeof value !== 'number') {
+ throw TypeError('weight must be a number');
}
+ if (value < 1) {
+ throw Error('weight must be grater than 0');
+ }
+
+ this[_shadow].totalWeight = value;
+ }
+
+ /**
+ *
+ * @param {Number} totalWeight total weight if generator inner chains
+ * @param {Number} throttle
+ */
+ captureProgress(totalWeight, throttle) {
+ this.totalWeight= totalWeight;
+ throttle && this[_scope].throttleProgress(totalWeight);
+ }
+}
+
+function resolveGenerator(generatorFn, args) {
+ return new this((resolve, reject, scope) => {
if (args !== undefined && !Array.isArray(args)) {
throw TypeError('args must be an array');
}
- const generator = generatorFn.apply(context, args);
+ const generatorScope= new AsyncGeneratorScope(scope);
+
+ const generator = generatorFn.apply(generatorScope, args);
if (!isGenerator(generator)) {
return reject(new TypeError('function must return a generator object'));
@@ -101,7 +145,7 @@ function resolveGenerator(generatorFn, args) {
});
const setProgress = (value, _scope, data) => {
- progress = (value * weight + sum) / (totalWeight ? totalWeight : 1);
+ progress = (value * weight + sum) / generatorScope.totalWeight;
if (progress > 1) {
progress = 1;
}
@@ -137,13 +181,8 @@ function resolveGenerator(generatorFn, args) {
return resolve(r.value);
}
- if (!totalWeight) {
- totalWeight = scope.weight();
- }
-
promise = this.from(r.value);
-
sum += weight;
weight = promise.isChain ? 1 : promise.weight();
diff --git a/package.json b/package.json
index 265de17..2922134 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "c-promise2",
"version": "0.3.1",
- "description": "Cancelable promise chain with progress capturing and other extra features",
+ "description": "Promise with cancellation, progress capturing and other extra features.",
"author": {
"name": "Dmitriy Mozgovoy",
"email": "robotshara@gmail.com",
@@ -73,6 +73,7 @@
"promise",
"cancelable",
"cancellable",
+ "timeout",
"progress",
"cancel",
"abortable",
diff --git a/test/tests/CPromise.js b/test/tests/CPromise.js
index c014c5e..8f15f93 100644
--- a/test/tests/CPromise.js
+++ b/test/tests/CPromise.js
@@ -285,8 +285,8 @@ module.exports = {
const timestamp = Date.now();
const time = () => Date.now() - timestamp;
return CPromise.all([
- CPromise.delay(50, v1),
- CPromise.delay(100, v2)
+ CPromise.delay(55, v1),
+ CPromise.delay(105, v2)
]).then((values)=>{
assert.ok(time() >= 100);
assert.deepStrictEqual(values, [v1, v2]);
@@ -316,7 +316,7 @@ module.exports = {
const timestamp = Date.now();
const time = () => Date.now() - timestamp;
return CPromise.race([
- CPromise.delay(50, v1),
+ CPromise.delay(55, v1),
CPromise.delay(100, v2)
]).then((value)=>{
assert.ok(time() >= 50 && time() < 100);