diff --git a/CHANGELOG.md b/CHANGELOG.md
index f618caf..b826b95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,163 +1,318 @@
-# Change Log
+# Changelog
+
All notable changes to this project will be documented in this file.
-The format is based on [Keep a Changelog](http://keepachangelog.com/)
-and this project adheres to [Semantic Versioning](http://semver.org/).
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
+
+## [v0.10.8](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.8...v0.10.8)
+
+## [v0.10.8](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.7...v0.10.8) - 2020-12-13
+
+### Commits
+
+- exported `promisify` method; [`8c9746b`](https://github.com/DigitalBrainJS/c-promise/commit/8c9746b2ba686dd7836aa6f5401297058cb3c7e2)
+- Added tests for `CanceledError.rethrow` method; [`e3d9f6f`](https://github.com/DigitalBrainJS/c-promise/commit/e3d9f6f8493a237e5007bb2ecae8d0856bc24a00)
+- spellcheck; [`895d0d6`](https://github.com/DigitalBrainJS/c-promise/commit/895d0d6bee1ea60acac56c00be64c92e10bde483)
+- spellcheck; [`aa32a7b`](https://github.com/DigitalBrainJS/c-promise/commit/aa32a7b2b72b7d79f3257cb43a894ce34315ec1c)
+
+## [v0.10.7](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.6...v0.10.7) - 2020-12-12
+
+### Commits
+
+- Added promisify method; [`b07585d`](https://github.com/DigitalBrainJS/c-promise/commit/b07585d0cc14dd00767c81dd02e04b3015752287)
+- Updated README; [`96ed673`](https://github.com/DigitalBrainJS/c-promise/commit/96ed67344eb89ba17c002d04d578880499b0b0e0)
+
+## [v0.10.6](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.5...v0.10.6) - 2020-12-10
+
+### Commits
+
+- added `weight`, `innerWeight` and `label` options for the `async` decorator; [`25a1d8d`](https://github.com/DigitalBrainJS/c-promise/commit/25a1d8d0d52c3ec9c160cb471e9f0f685ce358be)
+
+## [v0.10.5](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.4...v0.10.5) - 2020-12-08
+
+### Commits
+
+- Updated README; [`e9f1041`](https://github.com/DigitalBrainJS/c-promise/commit/e9f10415a4859805a3c6b1951ca8f923ea2b2170)
+
+## [v0.10.4](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.3...v0.10.4) - 2020-12-07
+
+### Commits
+
+- Added @progress decorator; [`b982644`](https://github.com/DigitalBrainJS/c-promise/commit/b982644bdefb428b53c976117661890a8b097fb5)
+- Updated README; [`ce89805`](https://github.com/DigitalBrainJS/c-promise/commit/ce89805191c43a95451c8484c96408c15cb380d9)
+- Updated README; [`9b5261e`](https://github.com/DigitalBrainJS/c-promise/commit/9b5261e57549555f5582cb3f4b7cebe5ca6dbdd9)
+
+## [v0.10.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.2...v0.10.3) - 2020-12-07
+
+### Commits
+
+- Fixed a bug with cleaning internal timer; [`b1b91a1`](https://github.com/DigitalBrainJS/c-promise/commit/b1b91a1650d30d1a4ee73ba2a6ad53caa57bba4c)
+- Updated CHANGELOG.md; [`459fdbf`](https://github.com/DigitalBrainJS/c-promise/commit/459fdbf0cfef848d109e35212dd68bcd32ab6018)
+
+## [v0.10.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.1...v0.10.2) - 2020-12-07
+
+### Commits
+
+- Added @canceled decorator; [`7b53ca3`](https://github.com/DigitalBrainJS/c-promise/commit/7b53ca36faf9436dfdaadb978a2a4b6976ce300b)
+
+## [v0.10.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.0...v0.10.1) - 2020-12-07
+
+### Commits
+
+- Refactored AbortController; [`6255fb1`](https://github.com/DigitalBrainJS/c-promise/commit/6255fb18eed4d59d380ec71754bf56d519963555)
+- Improved JSDoc annotation; [`5d4aafe`](https://github.com/DigitalBrainJS/c-promise/commit/5d4aafee14fac3e399c220b05b4ba0636bce7c50)
+- Updated CHANGELOG.md; [`37c4829`](https://github.com/DigitalBrainJS/c-promise/commit/37c4829be34e4c384eba12a62c96230d83e1259a)
+
+## [v0.10.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.9.1...v0.10.0) - 2020-12-06
+
+### Commits
+
+- Added decorators support; [`ed3be00`](https://github.com/DigitalBrainJS/c-promise/commit/ed3be00e4009e8367a960d6912ce8d8029771608)
+- Updated readme; [`5eb8611`](https://github.com/DigitalBrainJS/c-promise/commit/5eb861155d0f1445bdaff4512d14b744f7de996c)
+- Fixed async decorator anchor in the README; [`ab1e49c`](https://github.com/DigitalBrainJS/c-promise/commit/ab1e49c375080251755a583944043843849cd7c6)
+
+## [v0.9.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.9.0...v0.9.1) - 2020-11-30
+
+### Commits
+
+- Added generator support for the `then` method; [`ee41529`](https://github.com/DigitalBrainJS/c-promise/commit/ee415295d127915f17833fbb18b9929adf8068e2)
+- Refactored test for `CPromise.all`; [`4f28354`](https://github.com/DigitalBrainJS/c-promise/commit/4f28354ace5b0d502a45fa1eed8310c98b4cddbd)
+- Updated CHANGELOG.md; [`cfbe7cb`](https://github.com/DigitalBrainJS/c-promise/commit/cfbe7cb2ec0f10843d13886ea9ad8652f81b5b35)
+
+## [v0.9.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.8.2...v0.9.0) - 2020-11-29
+
+### Commits
+
+- Improved cancellation logic to work properly with multi leaves chains; [`cef6c54`](https://github.com/DigitalBrainJS/c-promise/commit/cef6c544ffb0d3f81bd9113c1b5704eed621e32a)
+- Updated CHANGELOG.md; [`7b2ab2c`](https://github.com/DigitalBrainJS/c-promise/commit/7b2ab2c78edaafba562fb55665f860aac535f361)
+
+## [v0.8.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.8.1...v0.8.2) - 2020-11-28
+
+### Commits
+
+- Improved README.md; [`f417c13`](https://github.com/DigitalBrainJS/c-promise/commit/f417c13d618b3d9b412166a1d491b912408e588d)
+
+## [v0.8.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.8.0...v0.8.1) - 2020-11-28
+
+### Commits
+
+- Made the promise executor optional; [`50e6649`](https://github.com/DigitalBrainJS/c-promise/commit/50e66497077c8305315efd75b52637a4489d3b14)
+
+## [v0.8.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.6...v0.8.0) - 2020-11-27
+
+### Commits
+
+- Added `canceled` method to catch CanceledError rejection; [`b5f1336`](https://github.com/DigitalBrainJS/c-promise/commit/b5f1336a42af1373c34eee35098122adb42c900e)
+- Added `canceled` method to catch CanceledError rejection; [`3d87a7f`](https://github.com/DigitalBrainJS/c-promise/commit/3d87a7f31c1a95fafae2e39c1f64047e2151458f)
+
+## [v0.7.6](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.5...v0.7.6) - 2020-11-26
+
+### Commits
+
+- refactored allSettled; [`a7a6829`](https://github.com/DigitalBrainJS/c-promise/commit/a7a68299b748d2cf7e59b9670184635e7f8cf532)
+
+## [v0.7.5](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.4...v0.7.5) - 2020-11-26
+
+### Commits
+
+- Fixed allSettled bug with options resolving; [`3fc84c3`](https://github.com/DigitalBrainJS/c-promise/commit/3fc84c3b7079d7b138f41bc43ca22ee11101ad7d)
+
+## [v0.7.4](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.3...v0.7.4) - 2020-11-26
+
+### Commits
+
+- Improved isCanceled state for then method; [`004f032`](https://github.com/DigitalBrainJS/c-promise/commit/004f032a32b95a2b090466762da3da4d9ec03f07)
+
+## [v0.7.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.2...v0.7.3) - 2020-11-25
+
+### Commits
+
+- Fixed React example anchor; [`7716058`](https://github.com/DigitalBrainJS/c-promise/commit/77160586d0c50c3d9d1d9dfd0869dbba41c81188)
+
+## [v0.7.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.1...v0.7.2) - 2020-11-25
+
+### Commits
+
+- Added React example; [`d82c8a7`](https://github.com/DigitalBrainJS/c-promise/commit/d82c8a7a7ad2a21e58ca6c86eeac47df2d42c8fd)
+
+## [v0.7.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.0...v0.7.1) - 2020-11-25
+
+### Commits
+
+- Added listen method; [`4af990f`](https://github.com/DigitalBrainJS/c-promise/commit/4af990fb76b54b75d63beb474a7a3de41d11397d)
+
+## [v0.7.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.6.1...v0.7.0) - 2020-11-24
+
+### Commits
+
+- Improved signals API; [`bdddb5e`](https://github.com/DigitalBrainJS/c-promise/commit/bdddb5e55df4c6b0eb225cfe2456c04925abeb38)
+- Updated CHANGELOG.md; [`c2b24f1`](https://github.com/DigitalBrainJS/c-promise/commit/c2b24f1d3da72fe91305bf3fd4f00ec964cdd29f)
+
+## [v0.6.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.6.0...v0.6.1) - 2020-11-22
+
+### Commits
+
+- Updated README.md; [`feabfe0`](https://github.com/DigitalBrainJS/c-promise/commit/feabfe0ee3d845670c6e54472508caf8a724211b)
+
+## [v0.6.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.5.3...v0.6.0) - 2020-11-22
+
+### Commits
+
+- Added signals support; [`ee002ea`](https://github.com/DigitalBrainJS/c-promise/commit/ee002ea1cd8aafeb03d5cdd39516ff538aca2112)
+- Added signals support; [`25e2b0a`](https://github.com/DigitalBrainJS/c-promise/commit/25e2b0adae7c3ba92e2bf043badd76f2c73e2d24)
+- Fixed jsdoc docs; [`b49520a`](https://github.com/DigitalBrainJS/c-promise/commit/b49520a6012321b346f06dbddd12e05a6d97e8eb)
+
+## [v0.5.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.5.2...v0.5.3) - 2020-10-17
+
+### Commits
+
+- Updated CHANGELOG.md; [`2736ac4`](https://github.com/DigitalBrainJS/c-promise/commit/2736ac4f801db9b092142ac13adea05a5b097246)
+- Updated CHANGELOG.md; [`990f481`](https://github.com/DigitalBrainJS/c-promise/commit/990f481bef5f4af1b50d0dfc73c68163efb3f7ee)
+
+## [v0.5.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.5.1...v0.5.2) - 2020-10-16
+
+### Commits
+
+- Fixed docs; [`e75ec21`](https://github.com/DigitalBrainJS/c-promise/commit/e75ec21986ce5ae7ffb8aba1245585bcd7a321f4)
+- Fixed bug with promise cancellation for `all` method [`c3ab73f`](https://github.com/DigitalBrainJS/c-promise/commit/c3ab73f41787e19a142893aa3dcd476545f1cc6b)
-For changes before version 0.4.0, please see the commit history
+## [v0.5.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.5.0...v0.5.1) - 2020-10-14
-## [0.10.8] - 2020-12-13
+### Commits
-### Added
-- exported `promisify` method;
-- `CanceledError.rethrow` method;
+- Fixed bug with promise cancellation for `all` method [`011ff3f`](https://github.com/DigitalBrainJS/c-promise/commit/011ff3f2967d0f6d702ce23688705b0c59285431)
-### Updated
-- all static methods lazily bound to the constructor context;
+## [v0.5.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.4.2...v0.5.0) - 2020-10-13
-## [0.10.7] - 2020-12-12
+### Commits
-### Added
-- `promisify` method;
+- Added concurrency, mapper, signatures options for `all` method; [`bcb4e63`](https://github.com/DigitalBrainJS/c-promise/commit/bcb4e63408c9caed093f433301e9ea2e89547e15)
-## [0.10.6] - 2020-12-09
+## [v0.4.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.4.1...v0.4.2) - 2020-09-25
-### Added
-- `weight`, `innerWeight` and `label` options for the `async` decorator;
+### Commits
-## [0.10.5] - 2020-12-08
+- Updated README.md; [`f96a103`](https://github.com/DigitalBrainJS/c-promise/commit/f96a1034752c25694d14fbaafb00f4e7d8c98024)
-## Updated
-- Docs;
-- JSDoc type annotations;
-- innerWeight default value logic;
+## [v0.4.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.4.0...v0.4.1) - 2020-09-24
-## [0.10.4] - 2020-12-08
+### Commits
-### Added
-- `@progress` decorator;
+- Updated README.md; [`55cf393`](https://github.com/DigitalBrainJS/c-promise/commit/55cf39375c467ed6fe887fb7ba94edd9e4fe46cf)
-## [0.10.3] - 2020-12-08
+## [v0.4.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.3.2...v0.4.0) - 2020-09-20
-## Fixed
-- a bug with cleaning internal timer;
+### Commits
-## [0.10.2] - 2020-12-08
+- Added outer abort signal support; [`ecf8905`](https://github.com/DigitalBrainJS/c-promise/commit/ecf890509362043a790a963484291e8614f40881)
+- Updated JSDoc signatures; [`cb2055c`](https://github.com/DigitalBrainJS/c-promise/commit/cb2055cec1494a62849669d806599da7f197c256)
+- Added CHANGELOG.md; [`86067d3`](https://github.com/DigitalBrainJS/c-promise/commit/86067d3306101fc857a662a8d8b979293e9f3a08)
+- Updated JSDoc signatures; [`bccee42`](https://github.com/DigitalBrainJS/c-promise/commit/bccee424084c9bdfd30074ee6065057c5a82d284)
-### Added
-- @canceled decorator
+## [v0.3.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.3.1...v0.3.2) - 2020-09-17
-## [0.10.1] - 2020-12-07
+### Commits
-### Updated
-- Renamed AbortControllerEx class back to AbortController to be recognized by third-party libraries;
+- Introduced AsyncGeneratorScope class; [`2d1f427`](https://github.com/DigitalBrainJS/c-promise/commit/2d1f427f0a8ade2049f3db879cf611316d311104)
-## [0.10.0] - 2020-12-07
+## [v0.3.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.3.0...v0.3.1) - 2020-09-15
-### Added
-- decorators support;
-- nativeController option for the CPromise constructor;
-- named export instead of default;
-- playground for decorators;
-- reason argument for the AbortController's abort method;
+### Commits
-### Removed
-- dev bundles;
+- Fixed CDN links; [`36e2c45`](https://github.com/DigitalBrainJS/c-promise/commit/36e2c454a39d8f48ce778f1f73428d5ee1efa51a)
-### Updated
-- cancellation mechanics;
+## [v0.3.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.2.1...v0.3.0) - 2020-09-15
-## [0.9.1] - 2020-11-30
+### Commits
-### Added
-- generator support for the `then` method;
-- `CPromise.resolveGenerator` method;
+- Updated README.hbs.md; [`503fc03`](https://github.com/DigitalBrainJS/c-promise/commit/503fc0358d6345d353bb83848c674fedcfe85404)
+- Removed plugin-replace; [`5be940a`](https://github.com/DigitalBrainJS/c-promise/commit/5be940a9e9eea87105054de56addab7e68e72eeb)
-### Updated
-- refactored CPromise.from method;
+## [v0.2.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.2.0...v0.2.1) - 2020-09-14
-## [0.9.0] - 2020-11-29
+### Commits
-### Added
-- Support for correct cancellation of multi-leaves promise chains;
-- `force` option for the `cancel` method;
+- Fixed all and race methods to support iterable input; [`6829b41`](https://github.com/DigitalBrainJS/c-promise/commit/6829b41a2216211bfb4602c618159166c6d8d86e)
-## [0.8.2] - 2020-11-28
+## [v0.2.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.9...v0.2.0) - 2020-09-13
-### Updated
-- Improved README.md;
+### Commits
-## [0.8.1] - 2020-11-28
+- Updated README.hbs.md; [`1a80ce5`](https://github.com/DigitalBrainJS/c-promise/commit/1a80ce57766dc42d3846c3285914a2fb6e7b943b)
+- Fixed pre-commit script; [`b6439f9`](https://github.com/DigitalBrainJS/c-promise/commit/b6439f957159274094ce91754d7e8c7f1374ca56)
+- Added debounce option for scope.progress; [`295d433`](https://github.com/DigitalBrainJS/c-promise/commit/295d43303a9f723eca0b3aacf5372703897fd892)
+- Updated README.hbs.md; [`cdaf10a`](https://github.com/DigitalBrainJS/c-promise/commit/cdaf10a42c12781f41738622dbded2efb7ace1a9)
+- Added package size badges; [`f52b1fe`](https://github.com/DigitalBrainJS/c-promise/commit/f52b1fe700861ee497fd0632a28f16a6b17afeef)
+- Fixed pre-commit script; [`f0ddee7`](https://github.com/DigitalBrainJS/c-promise/commit/f0ddee719521331e3d65e84f82e06b2dbe54ca43)
+- Updated package size badges; [`8eec56c`](https://github.com/DigitalBrainJS/c-promise/commit/8eec56c1ac4a7d6eaecc4aee9de20b5549bb66f1)
-### Updated
-- Made the promise executor optional;
+## [v0.1.9](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.8...v0.1.9) - 2020-09-11
-## [0.8.0] - 2020-11-27
+### Commits
-### Added
-- `canceled` method to catch CanceledError rejection;
-- error throwing when trying to write read-only public property;
+- Updated README.hbs.md; [`747d620`](https://github.com/DigitalBrainJS/c-promise/commit/747d620c153cca0cdea10f7e5c04f51e43fa2a7a)
-### Updated
-- Improved `isCanceled` flag processing for catch handlers
+## [v0.1.8](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.7...v0.1.8) - 2020-09-11
-## [0.7.1] - 2020-11-24
+### Commits
-### Added
-- `listen` method to listen AbortController signal;
+- Added CPromise.from method; [`e384f6d`](https://github.com/DigitalBrainJS/c-promise/commit/e384f6d20ecb29c168ca21ce91dd1b0e2f539445)
+- Added timeout test; [`b41a296`](https://github.com/DigitalBrainJS/c-promise/commit/b41a29607d9cce0c82ccaf0b30afe6511a4433da)
-## [0.7.0] - 2020-11-24
+## [v0.1.7](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.6...v0.1.7) - 2020-09-11
-### Added
-- prepend option for the `on` method;
-- handler param for ths `emitSignal` method;
-- an example of using signals;
+### Commits
-### Updated
-- reworked signal system;
+- Fixed timeout cancellation; [`3238d79`](https://github.com/DigitalBrainJS/c-promise/commit/3238d79b89d19a768d630025a6c2298411bab044)
-## [0.6.0] - 2020-11-20
+## [v0.1.6](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.5...v0.1.6) - 2020-09-10
-### Added
-- Added signals support;
-- Added pause / resume methods;
-- Added CPromise.allSettled method;
-- Added `examples` folder;
+### Commits
-### Removed
-- CPromiseScope - now every promise handler runs directly in the context of the Promise instance;
+- Updated README.md; [`2fa1bc2`](https://github.com/DigitalBrainJS/c-promise/commit/2fa1bc2b63eb065207112d86131c3fd22d3c980a)
-### Updated
-- Refactored;
+## [v0.1.5](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.4...v0.1.5) - 2020-09-10
-## [0.5.3] - 2020-10-16
+### Commits
-### Added
-- `innerWeight` option for captureProgress method
+- Fixed build script; [`47f2b17`](https://github.com/DigitalBrainJS/c-promise/commit/47f2b172bea4ec8fda8e51cb07aeb59079ea45d1)
-## [0.5.2] - 2020-10-16
+## [v0.1.4](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.3...v0.1.4) - 2020-09-10
-### Fixed
-- README examples & jsdoc notations
+### Commits
-## [0.5.1] - 2020-10-14
+- Fixed prepublishOnly script; [`d1156b9`](https://github.com/DigitalBrainJS/c-promise/commit/d1156b9ea65e67f8cab9c06c4486efbc195cb91f)
-### Fixed
-- a bug with promise cancellation for `all` method
+## [v0.1.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.2...v0.1.3) - 2020-09-10
-## [0.5.0] - 2020-10-04
+### Commits
-### Added
+- Updated .npmignore; [`3ca5cd0`](https://github.com/DigitalBrainJS/c-promise/commit/3ca5cd0cfc9e7ad56d7f1896fa9fd56439b7f69d)
+- Updated README.md; [`2f948dd`](https://github.com/DigitalBrainJS/c-promise/commit/2f948dde6e736fc8ecbd13a36524e55dd4f8a202)
-- Concurrency, mapper, signatures options for `all` method
+## [v0.1.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.1...v0.1.2) - 2020-09-10
-### Removed
+### Commits
-- AsyncGeneratorScope class
-- Resolving numbers to a delay for `CPromise.from` method
+- Updated README.md; Fixed coveralls; [`323858e`](https://github.com/DigitalBrainJS/c-promise/commit/323858e6e0640ef666a6cded4c671ec051bdc82f)
+- Updated README.md; [`e35d155`](https://github.com/DigitalBrainJS/c-promise/commit/e35d1554e38f272af4ac94aefeb9606da7f8c64f)
+- Updated README.md; [`12318cd`](https://github.com/DigitalBrainJS/c-promise/commit/12318cdf39e2d9fe4bcd3c5f27138fe1fa970506)
+- Updated README.md; [`a5c1436`](https://github.com/DigitalBrainJS/c-promise/commit/a5c1436136a96e8b67f845de96101f686afc7c12)
-## [0.4.0] - 2020-09-20
+## v0.1.1 - 2020-09-10
-### Added
+### Commits
-- Support for an outer abort signal to cancel CPromise;
+- Initial commit [`f932cb8`](https://github.com/DigitalBrainJS/c-promise/commit/f932cb8584ebd458d7961aba53f8f33c797bb650)
+- Updated README.md [`a3d3f94`](https://github.com/DigitalBrainJS/c-promise/commit/a3d3f94ad8e166d6081942bca7555d00a3f6d8e9)
+- Updated README.md [`7b15cb8`](https://github.com/DigitalBrainJS/c-promise/commit/7b15cb8e419624ea322c7b46bb4c6b9a05eb284a)
+- Updated README.md [`f13d677`](https://github.com/DigitalBrainJS/c-promise/commit/f13d6773c7eab9004b83a46d01be80a9843c3c72)
+- Updated README.md [`8bef0a5`](https://github.com/DigitalBrainJS/c-promise/commit/8bef0a5169fa2859d0102a98c4d77b8ede575e6c)
+- Updated README.md [`f4e0d36`](https://github.com/DigitalBrainJS/c-promise/commit/f4e0d36d0dbae76411ed044059759d69c89a4271)
+- Updated README.md [`db4b88b`](https://github.com/DigitalBrainJS/c-promise/commit/db4b88b1b9661e47f850f490fe023542e07a5e48)
+- Updated README.md [`06d90b2`](https://github.com/DigitalBrainJS/c-promise/commit/06d90b2cf8771b976c40288f5bed05aca360a40b)
+- Updated README.md [`4df153e`](https://github.com/DigitalBrainJS/c-promise/commit/4df153e676d0dbf42e982db7f0703b8d09f3a096)
+- Updated README.md [`ebaae6b`](https://github.com/DigitalBrainJS/c-promise/commit/ebaae6bd3452c167531932e9346107ccca75517a)
diff --git a/README.md b/README.md
index 815a985..5eae78d 100644
--- a/README.md
+++ b/README.md
@@ -22,8 +22,9 @@
- [React class component with CPromise decorators](#react-class-component-with-cpromise-decorators)
- [Signals handling](#signals-handling)
- [Using generators](#using-generators-as-an-alternative-of-ecma-async-functions)
+- [Atomic sub-chains](#atomic-sub-chains)
- [Using decorators](#using-decorators)
- - [@async](#asynctimeout-number)
+ - [@async](#asynctimeout-number-innerweight-number-label--string-weight-number)
- [@listen](#listensignal-abortsignalstringsymbol)
- [@cancel](#cancelreason-string-signal-abortsignalstringsymbol)
- [@canceled](#canceledonrejectederr-scope-context-function)
@@ -42,19 +43,30 @@
CPromise library provides an advanced version of the built-in Promise by subclassing.
You might be interested in using it if you need:
-- cancel the promise (including nested)
-- cancel async tasks inside React components
-- some way to make cancellation declarative (with decorators)
-- capture promise progress
-- pause the promise
+- cancel the promise through rejection (including nested)
+- cancel async tasks inside React components (useful to cancel internal code when components unmounts)
+ - with built-in decorators for class components
+ - with `useAsyncEffect`&`useAsyncCallback` hooks provided by [useAsyncEffect2](https://www.npmjs.com/package/use-async-effect2) package
+- cancel network requests with promises, which allows the network request to be automatically aborted when the parent async function is canceled:
+ - `fetch` use [cp-fetch](https://www.npmjs.com/package/cp-fetch)
+ - `axios` use [cp-axios](https://www.npmjs.com/package/cp-axios)
+- control cancellation flow to write really complicated async flows with cancelation ability.
+- define atomic promise chains that cannot be canceled in the middle of execution from upper chains
+- `AbortController` support for promises
+- some way to make cancellation declarative (with method decorators)
+- capture promise chain progress with defining progress impact for each promise (default 1)
+- generators support to write a flat code in the same way as `async`&`await` do, but with `CPromise` features support
+- pause/resume the promise
- pending timeout
- concurrent limitation for `all` and `allSettled` methods with `mapper` reducer
-- auto canceling internal async jobs of your React components
- advanced signal communication
In terms of the library **the cancellation means rejection with a special error subclass**.
+[Codesandbox Live Demo](https://codesandbox.io/s/c-promise2-readme-basic1-7d8u0)
````javascript
+import { CPromise } from "c-promise2";
+
const promise= new CPromise((resolve, reject, {onCancel, onPause, onResume})=>{
onCancel(()=>{
//optionally some code here to abort your long-term task (abort request, stop timers etc.)
@@ -72,7 +84,14 @@ console.log('isPromise:', promise instanceof Promise); // true
setTimeout(()=> promise.cancel(), 1000);
````
-Using decorators in React component to manage async tasks
+Log:
+````
+isPromise: true
+Failed: CanceledError: canceled
+chain isCanceled: true
+promise isCanceled: true
+````
+Using decorators in React component to manage async tasks and protect from [well-known React leak error](https://stackoverflow.com/questions/32903001/react-setstate-on-unmounted-component):
````jsx
export class FetchComponent extends React.Component {
state = {text: ""};
@@ -140,9 +159,9 @@ The package consists of pre-built umd, cjs, mjs bundles which can be found in th
- Import the library:
````javascript
-import CPromise from "c-promise2";
-// const CPromise = require("c-promise2"); // using require
-// import CPromise from "c-promise2/dev"; // development version
+import {CPromise} from "c-promise2";
+// const {CPromise} = require("c-promise2"); // using require
+// import {CPromise} from "c-promise2/dev"; // development version
const chain= CPromise.delay(1000, 'It works!').then(message => console.log('Done', message));
@@ -276,7 +295,8 @@ setTimeout(()=> promise.cancel(), 1000);
// you able to call cancel() at any time to cancel the entire chain at any stage
// the related network request will also be aborted
````
-You can use the [cp-fetch package](https://www.npmjs.com/package/cp-fetch) which provides a CPromise wrapper for fetch API.
+You can use the [cp-fetch package](https://www.npmjs.com/package/cp-fetch) which provides a ready to use
+CPromise wrapper for cross-platform fetch API.
- [Live browser example (jsfiddle.net)](https://jsfiddle.net/DigitalBrain/g0dv5L8c/5/)
@@ -612,6 +632,107 @@ CPromise.resolve().then(function*(){
})
````
+## Atomic sub-chains
+
+Sometimes you need to prevent any sub-chain from being canceled from the outside because you want to allow some
+already started asynchronous procedure to be completed before closing the following promise chains.
+To solve this challenge use `.atomic(["disabled"|"detached"|"await"])` method.
+- `'detached'` - keep the sub-chain execution running in 'background', the main chain reject immediately
+- `'await'` - wait for the sub-chain to complete and then reject the next promise in the outer chain
+- `false` considering as `'disabled'`
+- `true` considering as `'await'`
+
+Check out the difference with examples:
+Normal cancellation behaviour `.atomic('disabled')` ([Demo](https://codesandbox.io/s/c-promise2-readme-not-atomic-i9pu8)):
+````javascript
+const p = CPromise.delay(1000, 1)
+ .then((v) => {
+ console.log("p1");
+ return CPromise.delay(1000, 2);
+ })
+ .then((v) => {
+ console.log("p2");
+ return CPromise.delay(1000, 3);
+ })
+ .atomic()
+ .then((v) => {
+ console.log("p3");
+ return CPromise.delay(1000, 4);
+ })
+ .then(
+ (value) => console.log(`Done:`, value),
+ (err) => console.warn(`Fail: ${err}`)
+ );
+
+setTimeout(() => p.cancel(), 1500);
+````
+output:
+````
+p1
+Fail: CanceledError: canceled
+
+Process finished with exit code 0
+````
+`.atomic('detached')` cancellation behaviour ([Demo](https://codesandbox.io/s/c-promise2-readme-atomic-detached-nvvwo?file=/src/index.js)):
+````javascript
+const p = CPromise.delay(1000, 1)
+ .then((v) => {
+ console.log("p1");
+ return CPromise.delay(1000, 2);
+ })
+ .then((v) => {
+ console.log("p2");
+ return CPromise.delay(1000, 3);
+ })
+ .atomic('detached')
+ .then((v) => {
+ console.log("p3");
+ return CPromise.delay(1000, 4);
+ })
+ .then(
+ (value) => console.log(`Done:`, value),
+ (err) => console.warn(`Fail: ${err}`)
+ );
+
+setTimeout(() => p.cancel(), 1500);
+````
+output:
+````
+p1
+Fail: CanceledError: canceled
+p2
+````
+`.atomic('await')` cancellation behaviour ([Demo](https://codesandbox.io/s/c-promise2-readme-atomic-await-e9812)):
+````javascript
+const p = CPromise.delay(1000, 1)
+ .then((v) => {
+ console.log("p1");
+ return CPromise.delay(1000, 2);
+ })
+ .then((v) => {
+ console.log("p2");
+ return CPromise.delay(1000, 3);
+ })
+ .atomic()
+ .then((v) => {
+ console.log("p3");
+ return CPromise.delay(1000, 4);
+ })
+ .then(
+ (value) => console.log(`Done:`, value),
+ (err) => console.warn(`Fail: ${err}`)
+ );
+
+setTimeout(() => p.cancel(), 1500);
+````
+output:
+````
+p1
+p2
+Fail: CanceledError: canceled
+
+Process finished with exit code 0
+````
## Using decorators
The library supports a few types of decorators to make your code cleaner.
@@ -740,6 +861,7 @@ the user canceled the promise immediately after the promise was resolved,
## Related projects
- [cp-axios](https://www.npmjs.com/package/cp-axios) - a simple axios wrapper that provides an advanced cancellation api
- [cp-fetch](https://www.npmjs.com/package/cp-fetch) - fetch with timeouts and request cancellation
+- [use-async-effect2](https://www.npmjs.com/package/use-async-effect2) - cancel async code in functional React components
## API Reference
@@ -867,8 +989,9 @@ CPromise class
* [.reject(err)](#module_CPromise..CPromise+reject) ⇒ CPromise
* [.pause()](#module_CPromise..CPromise+pause) ⇒ Boolean
* [.resume()](#module_CPromise..CPromise+resume) ⇒ Boolean
+ * [.atomic([type])](#module_CPromise..CPromise+atomic) ⇒
* [.cancel([reason], [force])](#module_CPromise..CPromise+cancel)
- * [.emitSignal(type, [data], [handler], [validator])](#module_CPromise..CPromise+emitSignal) ⇒ Boolean
+ * [.emitSignal(type, [data], [handler], [locator])](#module_CPromise..CPromise+emitSignal) ⇒ Boolean
* [.delay(ms)](#module_CPromise..CPromise+delay) ⇒ CPromise
* [.then(onFulfilled, [onRejected])](#module_CPromise..CPromise+then) ⇒ CPromise
* [.catch(onRejected, [filter])](#module_CPromise..CPromise+catch) ⇒ CPromise
@@ -889,7 +1012,7 @@ CPromise class
* [.allSettled(iterable, options)](#module_CPromise..CPromise.allSettled) ⇒ CPromise
* [.from(thing, [options])](#module_CPromise..CPromise.from) ⇒ CPromise
* [.promisify(originalFn, [options])](#module_CPromise..CPromise.promisify) ⇒ function
- * [.resolveGenerator(generatorFn, [options])](#module_CPromise..CPromise.resolveGenerator) ⇒ CPromise
+ * [.run(generatorFn, [options])](#module_CPromise..CPromise.run) ⇒ CPromise
@@ -1116,6 +1239,18 @@ Pause promise
Resume promise
**Kind**: instance method of [CPromise
](#module_CPromise..CPromise)
+
+
+#### cPromise.atomic([type]) ⇒
+Make promise chain atomic (non-cancellable for external signals)
+
+**Kind**: instance method of [CPromise
](#module_CPromise..CPromise)
+**Returns**: CPromise
+
+| Param | Type |
+| --- | --- |
+| [type] | number
\| boolean
\| "disabled"
\| "detached"
\| "await"
|
+
#### cPromise.cancel([reason], [force])
@@ -1130,7 +1265,7 @@ throws the CanceledError that cause promise chain cancellation
-#### cPromise.emitSignal(type, [data], [handler], [validator]) ⇒ Boolean
+#### cPromise.emitSignal(type, [data], [handler], [locator]) ⇒ Boolean
Emit a signal of the specific type
**Kind**: instance method of [CPromise
](#module_CPromise..CPromise)
@@ -1140,7 +1275,7 @@ Emit a signal of the specific type
| type | Signal
|
| [data] | \*
|
| [handler] | SignalHandler
|
-| [validator] | SignalValidator
|
+| [locator] | SignalLocator
|
@@ -1370,20 +1505,21 @@ Converts callback styled function|GeneratorFn|AsyncFn to CPromise async function
| originalFn | function
\| GeneratorFunction
\| AsyncFunction
|
| [options] | PromisifyOptions
\| function
\| Boolean
|
-
+
-#### CPromise.resolveGenerator(generatorFn, [options]) ⇒ CPromise
+#### CPromise.run(generatorFn, [options]) ⇒ CPromise
Resolves the generator to an CPromise instance
**Kind**: static method of [CPromise
](#module_CPromise..CPromise)
-| Param | Type |
-| --- | --- |
-| generatorFn | GeneratorFunction
|
-| [options] | Object
|
-| [options.args] | Array
|
-| [options.resolveSignatures] | Boolean
|
-| [options.context] | \*
|
+| Param | Type | Description |
+| --- | --- | --- |
+| generatorFn | GeneratorFunction
| |
+| [options] | Object
| |
+| [options.args] | Array
| |
+| [options.resolveSignatures] | Boolean
| resolve extra signatures (like arrays with CPromise.all) |
+| [options.scopeArg] | Boolean
| pass the CPromise scope as the first argument to the generator function |
+| [options.context] | \*
| |
@@ -1462,9 +1598,9 @@ If value is a number it will be considered as the value for timeout option
If va
| type | Signal
|
| scope | CPromise
|
-
+
-### CPromise~SignalValidator ⇒ Boolean
+### CPromise~SignalLocator ⇒ Boolean
**Kind**: inner typedef of [CPromise
](#module_CPromise)
**this**: {CPromise}
@@ -1506,8 +1642,10 @@ If value is a number it will be considered as the value for timeout option
If va
| Name | Type | Description |
| --- | --- | --- |
-| multiArgs | Boolean
| aggregate all passed arguments to an array |
-| finalize | PromisifyFinalizeFn
| aggregate all passed arguments to an array |
+| [multiArgs] | Boolean
| aggregate all passed arguments to an array |
+| [finalize] | PromisifyFinalizeFn
| aggregate all passed arguments to an array |
+| [fnType] | "plain"
\| "generator"
\| "async"
| |
+| [scopeArg] | boolean
| pass the CPromise scope as the first argument to the generator function |
## License
diff --git a/jsdoc2md/README.hbs.md b/jsdoc2md/README.hbs.md
index 81895f5..07600c4 100644
--- a/jsdoc2md/README.hbs.md
+++ b/jsdoc2md/README.hbs.md
@@ -22,8 +22,9 @@
- [React class component with CPromise decorators](#react-class-component-with-cpromise-decorators)
- [Signals handling](#signals-handling)
- [Using generators](#using-generators-as-an-alternative-of-ecma-async-functions)
+- [Atomic sub-chains](#atomic-sub-chains)
- [Using decorators](#using-decorators)
- - [@async](#asynctimeout-number)
+ - [@async](#asynctimeout-number-innerweight-number-label--string-weight-number)
- [@listen](#listensignal-abortsignalstringsymbol)
- [@cancel](#cancelreason-string-signal-abortsignalstringsymbol)
- [@canceled](#canceledonrejectederr-scope-context-function)
@@ -42,19 +43,30 @@
CPromise library provides an advanced version of the built-in Promise by subclassing.
You might be interested in using it if you need:
-- cancel the promise (including nested)
-- cancel async tasks inside React components
-- some way to make cancellation declarative (with decorators)
-- capture promise progress
-- pause the promise
+- cancel the promise through rejection (including nested)
+- cancel async tasks inside React components (useful to cancel internal code when components unmounts)
+ - with built-in decorators for class components
+ - with `useAsyncEffect`&`useAsyncCallback` hooks provided by [useAsyncEffect2](https://www.npmjs.com/package/use-async-effect2) package
+- cancel network requests with promises, which allows the network request to be automatically aborted when the parent async function is canceled:
+ - `fetch` use [cp-fetch](https://www.npmjs.com/package/cp-fetch)
+ - `axios` use [cp-axios](https://www.npmjs.com/package/cp-axios)
+- control cancellation flow to write really complicated async flows with cancelation ability.
+- define atomic promise chains that cannot be canceled in the middle of execution from upper chains
+- `AbortController` support for promises
+- some way to make cancellation declarative (with method decorators)
+- capture promise chain progress with defining progress impact for each promise (default 1)
+- generators support to write a flat code in the same way as `async`&`await` do, but with `CPromise` features support
+- pause/resume the promise
- pending timeout
- concurrent limitation for `all` and `allSettled` methods with `mapper` reducer
-- auto canceling internal async jobs of your React components
- advanced signal communication
In terms of the library **the cancellation means rejection with a special error subclass**.
+[Codesandbox Live Demo](https://codesandbox.io/s/c-promise2-readme-basic1-7d8u0)
````javascript
+import { CPromise } from "c-promise2";
+
const promise= new CPromise((resolve, reject, {onCancel, onPause, onResume})=>{
onCancel(()=>{
//optionally some code here to abort your long-term task (abort request, stop timers etc.)
@@ -72,7 +84,14 @@ console.log('isPromise:', promise instanceof Promise); // true
setTimeout(()=> promise.cancel(), 1000);
````
-Using decorators in React component to manage async tasks
+Log:
+````
+isPromise: true
+Failed: CanceledError: canceled
+chain isCanceled: true
+promise isCanceled: true
+````
+Using decorators in React component to manage async tasks and protect from [well-known React leak error](https://stackoverflow.com/questions/32903001/react-setstate-on-unmounted-component):
````jsx
export class FetchComponent extends React.Component {
state = {text: ""};
@@ -140,9 +159,9 @@ The package consists of pre-built umd, cjs, mjs bundles which can be found in th
- Import the library:
````javascript
-import CPromise from "c-promise2";
-// const CPromise = require("c-promise2"); // using require
-// import CPromise from "c-promise2/dev"; // development version
+import {CPromise} from "c-promise2";
+// const {CPromise} = require("c-promise2"); // using require
+// import {CPromise} from "c-promise2/dev"; // development version
const chain= CPromise.delay(1000, 'It works!').then(message => console.log('Done', message));
@@ -276,7 +295,8 @@ setTimeout(()=> promise.cancel(), 1000);
// you able to call cancel() at any time to cancel the entire chain at any stage
// the related network request will also be aborted
````
-You can use the [cp-fetch package](https://www.npmjs.com/package/cp-fetch) which provides a CPromise wrapper for fetch API.
+You can use the [cp-fetch package](https://www.npmjs.com/package/cp-fetch) which provides a ready to use
+CPromise wrapper for cross-platform fetch API.
- [Live browser example (jsfiddle.net)](https://jsfiddle.net/DigitalBrain/g0dv5L8c/5/)
@@ -612,6 +632,107 @@ CPromise.resolve().then(function*(){
})
````
+## Atomic sub-chains
+
+Sometimes you need to prevent any sub-chain from being canceled from the outside because you want to allow some
+already started asynchronous procedure to be completed before closing the following promise chains.
+To solve this challenge use `.atomic(["disabled"|"detached"|"await"])` method.
+- `'detached'` - keep the sub-chain execution running in 'background', the main chain reject immediately
+- `'await'` - wait for the sub-chain to complete and then reject the next promise in the outer chain
+- `false` considering as `'disabled'`
+- `true` considering as `'await'`
+
+Check out the difference with examples:
+Normal cancellation behaviour `.atomic('disabled')` ([Demo](https://codesandbox.io/s/c-promise2-readme-not-atomic-i9pu8)):
+````javascript
+const p = CPromise.delay(1000, 1)
+ .then((v) => {
+ console.log("p1");
+ return CPromise.delay(1000, 2);
+ })
+ .then((v) => {
+ console.log("p2");
+ return CPromise.delay(1000, 3);
+ })
+ .atomic()
+ .then((v) => {
+ console.log("p3");
+ return CPromise.delay(1000, 4);
+ })
+ .then(
+ (value) => console.log(`Done:`, value),
+ (err) => console.warn(`Fail: ${err}`)
+ );
+
+setTimeout(() => p.cancel(), 1500);
+````
+output:
+````
+p1
+Fail: CanceledError: canceled
+
+Process finished with exit code 0
+````
+`.atomic('detached')` cancellation behaviour ([Demo](https://codesandbox.io/s/c-promise2-readme-atomic-detached-nvvwo?file=/src/index.js)):
+````javascript
+const p = CPromise.delay(1000, 1)
+ .then((v) => {
+ console.log("p1");
+ return CPromise.delay(1000, 2);
+ })
+ .then((v) => {
+ console.log("p2");
+ return CPromise.delay(1000, 3);
+ })
+ .atomic('detached')
+ .then((v) => {
+ console.log("p3");
+ return CPromise.delay(1000, 4);
+ })
+ .then(
+ (value) => console.log(`Done:`, value),
+ (err) => console.warn(`Fail: ${err}`)
+ );
+
+setTimeout(() => p.cancel(), 1500);
+````
+output:
+````
+p1
+Fail: CanceledError: canceled
+p2
+````
+`.atomic('await')` cancellation behaviour ([Demo](https://codesandbox.io/s/c-promise2-readme-atomic-await-e9812)):
+````javascript
+const p = CPromise.delay(1000, 1)
+ .then((v) => {
+ console.log("p1");
+ return CPromise.delay(1000, 2);
+ })
+ .then((v) => {
+ console.log("p2");
+ return CPromise.delay(1000, 3);
+ })
+ .atomic()
+ .then((v) => {
+ console.log("p3");
+ return CPromise.delay(1000, 4);
+ })
+ .then(
+ (value) => console.log(`Done:`, value),
+ (err) => console.warn(`Fail: ${err}`)
+ );
+
+setTimeout(() => p.cancel(), 1500);
+````
+output:
+````
+p1
+p2
+Fail: CanceledError: canceled
+
+Process finished with exit code 0
+````
## Using decorators
The library supports a few types of decorators to make your code cleaner.
@@ -740,6 +861,7 @@ the user canceled the promise immediately after the promise was resolved,
## Related projects
- [cp-axios](https://www.npmjs.com/package/cp-axios) - a simple axios wrapper that provides an advanced cancellation api
- [cp-fetch](https://www.npmjs.com/package/cp-fetch) - fetch with timeouts and request cancellation
+- [use-async-effect2](https://www.npmjs.com/package/use-async-effect2) - cancel async code in functional React components
## API Reference
diff --git a/lib/c-promise.js b/lib/c-promise.js
index 3179c8c..5c9cdb0 100644
--- a/lib/c-promise.js
+++ b/lib/c-promise.js
@@ -38,6 +38,27 @@ const SIGNAL_CANCEL = Symbol('SIGNAL_CANCEL');
const SIGNAL_PAUSE = Symbol('SIGNAL_PAUSE');
const SIGNAL_RESUME = Symbol('SIGNAL_RESUME');
+const ATOMIC_TYPE_DISABLED = 0;
+const ATOMIC_TYPE_DETACHED = 1;
+const ATOMIC_TYPE_AWAIT = 2;
+
+const atomicMap2= {
+ 'disabled': ATOMIC_TYPE_DISABLED,
+ 'detached': ATOMIC_TYPE_DETACHED,
+ 'await': ATOMIC_TYPE_AWAIT,
+}
+
+const atomicMap= new Map([
+ ['disabled', ATOMIC_TYPE_DISABLED],
+ ['detached', ATOMIC_TYPE_DETACHED],
+ ['await', ATOMIC_TYPE_AWAIT],
+ [false, ATOMIC_TYPE_DISABLED],
+ [true, ATOMIC_TYPE_AWAIT],
+ [ATOMIC_TYPE_DISABLED, ATOMIC_TYPE_DISABLED],
+ [ATOMIC_TYPE_DETACHED, ATOMIC_TYPE_DETACHED],
+ [ATOMIC_TYPE_AWAIT, ATOMIC_TYPE_AWAIT],
+]);
+
const promiseAssocStore = new WeakMap();
const controllersStore = new WeakMap();
@@ -135,7 +156,9 @@ class CPromise extends Promise {
label: '',
weight: 1,
value: undefined,
- nativeController
+ nativeController,
+ atomic: ATOMIC_TYPE_DISABLED,
+ canceledWith: null
};
this.onCancel = this.onCancel.bind(this);
@@ -648,6 +671,10 @@ class CPromise extends Promise {
resolve();
}
+ if(!isRejected && shadow.canceledWith){
+ complete(shadow.canceledWith, true);
+ }
+
if (!isThenable(value)) {
complete(value, isRejected);
return;
@@ -728,6 +755,24 @@ class CPromise extends Promise {
});
}
+ /**
+ * Make promise chain atomic (non-cancellable for external signals)
+ * @param {number|boolean|"disabled"|"detached"|"await"} [type]
+ * @returns CPromise
+ */
+
+ atomic(type = ATOMIC_TYPE_AWAIT) {
+ const _type = atomicMap.get(type);
+
+ if (_type === undefined) {
+ throw Error(`Unknown atomic type '${type}'`);
+ }
+
+ this[_shadow].atomic = _type;
+
+ return this;
+ }
+
/**
* throws the CanceledError that cause promise chain cancellation
* @param {String|Error} [reason]
@@ -736,10 +781,25 @@ class CPromise extends Promise {
cancel(reason, force = false) {
return this.emitSignal(SIGNAL_CANCEL, {err: CanceledError.from(reason), force}, function ({err}) {
- this.reject(err);
+ !this[_shadow].canceledWith && this.reject(err);
return true;
- }, ({force}, type, scope, isRoot) => {
- return (!isRoot && !force && scope[_shadow].leafsCount > 1);
+ }, ({err, force}, type, scope, isRoot) => {
+
+ const shadow = scope[_shadow];
+ const {atomic}= shadow;
+
+ if (atomic === ATOMIC_TYPE_DETACHED) {
+ return false;
+ }
+
+ if(atomic === ATOMIC_TYPE_AWAIT){
+ shadow.canceledWith= err;
+ return true;
+ }
+
+ if (!isRoot && !force && scope[_shadow].leafsCount > 1) {
+ return false;
+ }
});
}
@@ -757,7 +817,7 @@ class CPromise extends Promise {
*/
/**
- * @typedef {Function} SignalValidator
+ * @typedef {Function} SignalLocator
* @param {*} data
* @param {Signal} type
* @param {CPromise} scope
@@ -771,27 +831,31 @@ class CPromise extends Promise {
* @param {Signal} type
* @param {*} [data]
* @param {SignalHandler} [handler]
- * @param {SignalValidator} [validator]
+ * @param {SignalLocator} [locator]
* @returns {Boolean}
*/
- emitSignal(type, data, handler, validator) {
+ emitSignal(type, data, handler, locator) {
const emit = (scope, isRoot) => {
const shadow = scope[_shadow];
if (!shadow.isPending) return false;
- if (validator && validator.call(scope, data, type, scope, isRoot)) {
+ const locatorResult = locator ? locator.call(scope, data, type, scope, isRoot) : undefined;
+
+ if (locatorResult === false) {
return false;
}
- let {parent, innerChain} = shadow;
+ if (locatorResult !== true) {
+ let {parent, innerChain} = shadow;
- if (parent && emit(parent, false)) {
- return true;
- }
+ if (parent && emit(parent, false)) {
+ return true;
+ }
- if (innerChain && emit(innerChain, false)) {
- return true;
+ if (innerChain && emit(innerChain, false)) {
+ return true;
+ }
}
return !!(scope.emitHook('signal', type, data) || handler && handler.call(scope, data, type, scope));
@@ -847,7 +911,7 @@ class CPromise extends Promise {
}) : onFulfilled;
return isGeneratorFunction(cb) ?
- this.constructor.resolveGenerator(cb, {
+ this.constructor.run(cb, {
resolveSignatures: true,
args: [value]
}) : cb.call(promise, value, promise)
@@ -1301,7 +1365,7 @@ class CPromise extends Promise {
}
} else if (type === 'function') {
if (isGeneratorFunction(thing)) {
- return this.resolveGenerator(thing, args)
+ return this.run(thing, args)
}
}
@@ -1319,8 +1383,10 @@ class CPromise extends Promise {
/**
* @typedef {Object} PromisifyOptions
- * @property {Boolean} multiArgs aggregate all passed arguments to an array
- * @property {PromisifyFinalizeFn} finalize aggregate all passed arguments to an array
+ * @property {Boolean} [multiArgs] aggregate all passed arguments to an array
+ * @property {PromisifyFinalizeFn} [finalize] aggregate all passed arguments to an array
+ * @property {"plain"|"generator"|"async"} [fnType]
+ * @property {boolean} [scopeArg] pass the CPromise scope as the first argument to the generator function
*/
/**
@@ -1341,14 +1407,15 @@ class CPromise extends Promise {
options !== undefined && validateOptions(options, {
multiArgs: validators.boolean,
finalize: validators.plainFunction,
- fnType: validators.string
+ fnType: validators.string,
+ scopeArg: validators.boolean
});
}
- const {multiArgs, finalize} = options || {};
+ const {multiArgs, finalize, fnType, scopeArg} = options || {};
const context = this;
- switch (getFnType(originalFn)) {
+ switch (fnType || getFnType(originalFn)) {
case "plain":
return (...args) => {
return new this((resolve, reject, scope) => {
@@ -1365,8 +1432,9 @@ class CPromise extends Promise {
}
case "generator":
return function (...args) {
- return context.resolveGenerator(originalFn, {
+ return context.run(originalFn, {
context: this,
+ scopeArg,
args
});
}
@@ -1384,12 +1452,13 @@ class CPromise extends Promise {
* @param {GeneratorFunction} generatorFn
* @param {Object} [options]
* @param {Array} [options.args]
- * @param {Boolean} [options.resolveSignatures]
+ * @param {Boolean} [options.resolveSignatures] resolve extra signatures (like arrays with CPromise.all)
+ * @param {Boolean} [options.scopeArg] pass the CPromise scope as the first argument to the generator function
* @param {*} [options.context]
* @returns {CPromise}
*/
- static resolveGenerator(generatorFn, {args, resolveSignatures, context} = {}) {
+ static run(generatorFn, {args, resolveSignatures, context, scopeArg= false} = {}) {
return new this((resolve, reject, scope) => {
let generator;
@@ -1397,18 +1466,22 @@ class CPromise extends Promise {
context = scope;
}
- switch (args ? args.length : 0) {
- case 0:
- generator = generatorFn.call(context, scope);
- break;
- case 1:
- generator = generatorFn.call(context, args[0], scope);
- break;
- case 2:
- generator = generatorFn.call(context, args[0], args[1], scope);
- break;
- default:
- generator = generatorFn.apply(context, [...args, scope]);
+ if(!scopeArg){
+ generator = generatorFn.apply(context, args);
+ }else{
+ switch (args ? args.length : 0) {
+ case 0:
+ generator = generatorFn.call(context, scope);
+ break;
+ case 1:
+ generator = generatorFn.call(context, scope, args[0]);
+ break;
+ case 2:
+ generator = generatorFn.call(context, scope, args[0], args[1]);
+ break;
+ default:
+ generator = generatorFn.apply(context, [scope, ...args]);
+ }
}
if (!isGenerator(generator)) {
@@ -1738,7 +1811,7 @@ const decorators = {
decorator.descriptor = {
value: function (...args) {
- let promise = context.resolveGenerator(originalFn, {
+ let promise = context.run(originalFn, {
context: this,
args
});
diff --git a/package-lock.json b/package-lock.json
index 9d63ddc..98a4b47 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -780,6 +780,34 @@
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
},
+ "auto-changelog": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.2.1.tgz",
+ "integrity": "sha512-XlykJfZrXlWUAADBqGoN1elmntrRcx7oEymyYB3NRPEZxv0TfYHfivmwzejUMnwAdXKCgbQPo7GV5ULs3jwpfw==",
+ "dev": true,
+ "requires": {
+ "commander": "^5.0.0",
+ "handlebars": "^4.7.3",
+ "lodash.uniqby": "^4.7.0",
+ "node-fetch": "^2.6.0",
+ "parse-github-url": "^1.0.2",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
+ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@@ -2609,6 +2637,12 @@
"integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=",
"dev": true
},
+ "lodash.uniqby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
+ "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=",
+ "dev": true
+ },
"log-driver": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz",
@@ -2797,6 +2831,12 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
+ "node-fetch": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
+ "dev": true
+ },
"node-preload": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
@@ -6688,6 +6728,12 @@
"callsites": "^3.0.0"
}
},
+ "parse-github-url": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz",
+ "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==",
+ "dev": true
+ },
"parse-json": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz",
diff --git a/package.json b/package.json
index 61724de..64f80c8 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,8 @@
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"prepublishOnly": "npm run build && npm run test:coverage && npm run docs",
"postversion": "git push && git push --tags",
+ "changelog": "auto-changelog -p",
+ "version": "npm run changelog && git add CHANGELOG.md",
"build": "rollup -c",
"build:watch": "nodemon --watch lib/ --exec \\\"npm run build\\\"",
"dev": "cross-env NODE_ENV=development \"npm run test:watch\"",
@@ -42,6 +44,12 @@
"pre-commit": "npm run docs ; git add ."
}
},
+ "auto-changelog": {
+ "output": "CHANGELOG.md",
+ "template": "keepachangelog",
+ "unreleased": true,
+ "commitLimit": false
+ },
"repository": "https://github.com/DigitalBrainJS/c-promise.git",
"bugs": {
"url": "https://github.com/DigitalBrainJS/c-promise/issues"
@@ -119,6 +127,7 @@
"@rollup/plugin-commonjs": "^15.0.0",
"@rollup/plugin-multi-entry": "^4.0.0",
"@rollup/plugin-node-resolve": "^9.0.0",
+ "auto-changelog": "^2.2.1",
"coveralls": "^3.1.0",
"cross-env": "^7.0.2",
"husky": "^4.3.0",
diff --git a/test/tests/CPromise.js b/test/tests/CPromise.js
index 646269d..88a423c 100644
--- a/test/tests/CPromise.js
+++ b/test/tests/CPromise.js
@@ -5,802 +5,868 @@ const {CanceledError} = CPromise;
const delay = (ms, value, options) => new CPromise(resolve => setTimeout(() => resolve(value), ms), options);
const makePromise = (ms, value, handler) => {
- return new CPromise((resolve, reject, scope) => {
- const timer = setTimeout(resolve, ms, value);
- scope.onCancel(() => {
- clearTimeout(timer);
- handler && handler(scope);
- })
- });
+ return new CPromise((resolve, reject, scope) => {
+ const timer = setTimeout(resolve, ms, value);
+ scope.onCancel(() => {
+ clearTimeout(timer);
+ handler && handler(scope);
+ })
+ });
};
module.exports = {
- constructor: {
- 'should create instance of CancelablePromise': function () {
- const promise = new CPromise((resolve, reject) => {
- });
- assert(promise instanceof CPromise);
- assert(promise instanceof Promise);
- }
- },
+ constructor: {
+ 'should create instance of CancelablePromise': function () {
+ const promise = new CPromise((resolve, reject) => {
+ });
+ assert(promise instanceof CPromise);
+ assert(promise instanceof Promise);
+ }
+ },
- 'should support cancellation by the external signal': async function () {
- const controller = new CPromise.AbortController();
+ 'should support cancellation by the external signal': async function () {
+ const controller = new CPromise.AbortController();
- const timestamp = Date.now();
- const time = () => Date.now() - timestamp;
+ const timestamp = Date.now();
+ const time = () => Date.now() - timestamp;
- setTimeout(() => controller.abort(), 55);
+ setTimeout(() => controller.abort(), 55);
- return new CPromise((resolve, reject) => {
- setTimeout(resolve, 100);
- }, {signal: controller.signal}).then(() => {
- throw Error('not cancelled');
- }, (err) => {
- if (!CPromise.isCanceledError(err)) {
- if (time() < 50) {
- throw Error('Early cancellation');
- }
- throw err;
- }
- })
+ return new CPromise((resolve, reject) => {
+ setTimeout(resolve, 100);
+ }, {signal: controller.signal}).then(() => {
+ throw Error('not cancelled');
+ }, (err) => {
+ if (!CPromise.isCanceledError(err)) {
+ if (time() < 50) {
+ throw Error('Early cancellation');
+ }
+ throw err;
+ }
+ })
+ },
+
+ 'cancellation': {
+ 'should reject the promise with CanceledError': async function () {
+ const promise = new CPromise((resolve, reject) => {
+ setTimeout(resolve, 1000, 123);
+ });
+
+ const timestamp = Date.now();
+ const timeout = 100;
+
+ setTimeout(() => {
+ promise.cancel();
+ }, timeout);
+
+ await promise.then(() => {
+ assert.fail('promise has not been canceled');
+ }, (err) => {
+ if (err instanceof CPromise.CanceledError) {
+ if (Date.now() - timestamp < timeout) {
+ assert.fail('early cancellation detected')
+ }
+ return;
+ }
+ throw err;
+ });
},
- 'cancellation': {
- 'should reject the promise with CanceledError': async function () {
- const promise = new CPromise((resolve, reject) => {
- setTimeout(resolve, 1000, 123);
- });
-
- const timestamp = Date.now();
- const timeout = 100;
-
- setTimeout(() => {
- promise.cancel();
- }, timeout);
-
- await promise.then(() => {
- assert.fail('promise has not been canceled');
- }, (err) => {
- if (err instanceof CPromise.CanceledError) {
- if (Date.now() - timestamp < timeout) {
- assert.fail('early cancellation detected')
- }
- return;
- }
- throw err;
- });
- },
-
- 'should reject the promise chain with CanceledError': async function () {
- let currentChain = 1;
-
- const chain = delay(100)
- .then(() => {
- currentChain = 2
- return delay(100)
- })
- .then(() => {
- currentChain = 3
- return delay(100)
- })
- .then(() => {
- currentChain = 4
- return delay(100)
- })
- .then(() => {
- currentChain = 5
- })
-
- const timestamp = Date.now();
- const timeout = 250;
- const targetChainIndex = 3;
-
- setTimeout(() => {
- chain.cancel();
- }, timeout);
-
- await chain.then(() => {
- assert.fail('promise has not been canceled');
- }, (err) => {
- if (err instanceof CPromise.CanceledError) {
- if (Date.now() - timestamp < timeout - 5) {
- assert.fail('early cancellation detected')
- }
- if (currentChain !== targetChainIndex) {
- assert.equal(currentChain, targetChainIndex, 'wrong chain raised the error')
- }
- return;
- }
- throw err;
- });
- },
-
- 'throwing the CanceledError inside the promise': {
- "should lead to chains cancellation": async function () {
- let canceled = false;
- let signaled = false;
-
- return CPromise.delay(10, 123).then((value, {signal, onCancel}) => {
- onCancel((reason) => {
- assert.equal(reason.message, 'test');
- canceled = true;
- });
-
- signal.addEventListener('abort', () => {
- signaled = true;
- })
-
- return CPromise.delay(20).then(() => {
- throw new CPromise.CanceledError('test');
- });
- }).then(() => {
- assert.fail("has not been rejected");
- }, (err) => {
- assert.equal(err.message, 'test');
- assert.ok(canceled, "not cancelled");
- assert.ok(signaled, "not signaled");
- assert.ok(err instanceof CPromise.CanceledError);
- })
- }
- },
+ 'should reject the promise chain with CanceledError': async function () {
+ let currentChain = 1;
- 'should cancel only isolated leaves': async function () {
- let rootCanceled = false;
- let firstCanceled = false;
- let secondCanceled = false;
-
- const root = makePromise(1000, null, () => {
- rootCanceled = true;
- });
-
- const firstLeaf = root.then(() => makePromise(1000)).then(() => {
- assert.fail('first promise leaf was not canceled');
- }).canceled(() => {
- firstCanceled = true;
- });
-
- const secondLeaf = root.then(() => makePromise(1000)).then(() => {
- assert.fail('second promise leaf was not canceled');
- }).canceled(() => {
- secondCanceled = true;
- });
-
- firstLeaf.cancel();
- await firstLeaf;
- assert.equal(firstCanceled, true);
- assert.equal(secondCanceled, false);
- assert.equal(rootCanceled, false);
- secondLeaf.cancel();
- await secondLeaf;
- assert.equal(firstCanceled, true);
- assert.equal(secondCanceled, true);
- assert.equal(rootCanceled, true);
- await root.canceled();
- },
+ const chain = delay(100)
+ .then(() => {
+ currentChain = 2
+ return delay(100)
+ })
+ .then(() => {
+ currentChain = 3
+ return delay(100)
+ })
+ .then(() => {
+ currentChain = 4
+ return delay(100)
+ })
+ .then(() => {
+ currentChain = 5
+ })
- 'should cancel all leaves if the force option is set to true': async function () {
- let rootCanceled = false;
- let firstCanceled = false;
- let secondCanceled = false;
-
- const root = makePromise(1000, null, () => {
- rootCanceled = true;
- });
-
- const firstLeaf = root.then(() => makePromise(1000)).then(() => {
- assert.fail('first promise leaf was not canceled');
- }).canceled(() => {
- firstCanceled = true;
- });
-
- const secondLeaf = root.then(() => makePromise(1000)).then(() => {
- assert.fail('second promise leaf was not canceled');
- }).canceled(() => {
- secondCanceled = true;
- });
-
- firstLeaf.cancel('', true);
- await firstLeaf;
- assert.equal(firstCanceled, true);
- assert.equal(secondCanceled, true);
- assert.equal(rootCanceled, true);
+ const timestamp = Date.now();
+ const timeout = 250;
+ const targetChainIndex = 3;
+
+ setTimeout(() => {
+ chain.cancel();
+ }, timeout);
+
+ await chain.then(() => {
+ assert.fail('promise has not been canceled');
+ }, (err) => {
+ if (err instanceof CPromise.CanceledError) {
+ if (Date.now() - timestamp < timeout - 5) {
+ assert.fail('early cancellation detected')
+ }
+ if (currentChain !== targetChainIndex) {
+ assert.equal(currentChain, targetChainIndex, 'wrong chain raised the error')
+ }
+ return;
}
+ throw err;
+ });
},
- 'progress capturing': {
- 'should return correct chain progress': async function () {
- const chain = delay(100)
- .then(() => {
- assertProgress(0.2)
- return delay(100)
- })
- .then(() => {
- assertProgress(0.4)
- return delay(100)
- })
- .then(() => {
- assertProgress(0.6)
- return delay(100)
- })
- .then(() => {
- assertProgress(0.8)
- });//.captureProgress();
-
- const assertProgress = (expected) => {
- assert.equal(chain.progress(), expected);
- }
-
- assertProgress(0);
-
- await chain.then(() => {
- assertProgress(1);
- })
- }
+ 'throwing the CanceledError inside the promise': {
+ "should lead to chains cancellation": async function () {
+ let canceled = false;
+ let signaled = false;
+
+ return CPromise.delay(10, 123).then((value, {signal, onCancel}) => {
+ onCancel((reason) => {
+ assert.equal(reason.message, 'test');
+ canceled = true;
+ });
+
+ signal.addEventListener('abort', () => {
+ signaled = true;
+ })
+
+ return CPromise.delay(20).then(() => {
+ throw new CPromise.CanceledError('test');
+ });
+ }).then(() => {
+ assert.fail("has not been rejected");
+ }, (err) => {
+ assert.equal(err.message, 'test');
+ assert.ok(canceled, "not cancelled");
+ assert.ok(signaled, "not signaled");
+ assert.ok(err instanceof CPromise.CanceledError);
+ })
+ }
},
- 'suspension': {
- 'should support pause and resume methods': async function() {
- let timestamp = Date.now();
- let pauseEmitted, resumeEmitted;
- const passed = () => {
- return Date.now() - timestamp;
- }
- const chain = new CPromise((resolve, reject, {onPause, onResume}) => {
- setTimeout(resolve, 500);
- onPause(() => {
- pauseEmitted = true;
- });
-
- onResume(() => {
- resumeEmitted = true;
- });
- }).then(() => {
- assert.ok(passed() > 1200, `early completion (${passed()}ms)`);
- assert.ok(pauseEmitted, 'pause event has not been emitted');
- assert.ok(resumeEmitted, 'resume event has not been emitted');
- });
-
- setTimeout(() => {
- chain.pause();
- setTimeout(() => {
- chain.resume();
- }, 1000);
- }, 300);
-
- return chain;
- }
+ 'should cancel only isolated leaves': async function () {
+ let rootCanceled = false;
+ let firstCanceled = false;
+ let secondCanceled = false;
+
+ const root = makePromise(1000, null, () => {
+ rootCanceled = true;
+ });
+
+ const firstLeaf = root.then(() => makePromise(1000)).then(() => {
+ assert.fail('first promise leaf was not canceled');
+ }).canceled(() => {
+ firstCanceled = true;
+ });
+
+ const secondLeaf = root.then(() => makePromise(1000)).then(() => {
+ assert.fail('second promise leaf was not canceled');
+ }).canceled(() => {
+ secondCanceled = true;
+ });
+
+ firstLeaf.cancel();
+ await firstLeaf;
+ assert.equal(firstCanceled, true);
+ assert.equal(secondCanceled, false);
+ assert.equal(rootCanceled, false);
+ secondLeaf.cancel();
+ await secondLeaf;
+ assert.equal(firstCanceled, true);
+ assert.equal(secondCanceled, true);
+ assert.equal(rootCanceled, true);
+ await root.canceled();
},
- 'timeout': {
- 'should cancel the chain with timeout reason': async function () {
- const p = new CPromise(function (resolve, reject) {
- setTimeout(resolve, 1000);
- });
-
- return p
- .timeout(100)
- .then(() => {
- assert.fail('chain was not cancelled');
- }, err => {
- assert.ok(err instanceof CanceledError);
- assert.equal(err.message, 'timeout');
- })
- }
- },
+ 'should cancel all leaves if the force option is set to true': async function () {
+ let rootCanceled = false;
+ let firstCanceled = false;
+ let secondCanceled = false;
+
+ const root = makePromise(1000, null, () => {
+ rootCanceled = true;
+ });
+
+ const firstLeaf = root.then(() => makePromise(1000)).then(() => {
+ assert.fail('first promise leaf was not canceled');
+ }).canceled(() => {
+ firstCanceled = true;
+ });
+
+ const secondLeaf = root.then(() => makePromise(1000)).then(() => {
+ assert.fail('second promise leaf was not canceled');
+ }).canceled(() => {
+ secondCanceled = true;
+ });
+
+ firstLeaf.cancel('', true);
+ await firstLeaf;
+ assert.equal(firstCanceled, true);
+ assert.equal(secondCanceled, true);
+ assert.equal(rootCanceled, true);
+ }
+ },
+
+ 'progress capturing': {
+ 'should return correct chain progress': async function () {
+ const chain = delay(100)
+ .then(() => {
+ assertProgress(0.2)
+ return delay(100)
+ })
+ .then(() => {
+ assertProgress(0.4)
+ return delay(100)
+ })
+ .then(() => {
+ assertProgress(0.6)
+ return delay(100)
+ })
+ .then(() => {
+ assertProgress(0.8)
+ });//.captureProgress();
+
+ const assertProgress = (expected) => {
+ assert.equal(chain.progress(), expected);
+ }
+
+ assertProgress(0);
- 'CPromise#then': {
- 'should support generators': async function(){
- return CPromise.resolve()
- .then(function*(){
- const result1= yield makePromise(100, 123);
- const result2= yield makePromise(100, 456);
- return result1 + result2;
- })
- .then(function*(value){
- assert.equal(value, 123 + 456);
- })
+ await chain.then(() => {
+ assertProgress(1);
+ })
+ }
+ },
+
+ 'suspension': {
+ 'should support pause and resume methods': async function () {
+ let timestamp = Date.now();
+ let pauseEmitted, resumeEmitted;
+ const passed = () => {
+ return Date.now() - timestamp;
+ }
+ const chain = new CPromise((resolve, reject, {onPause, onResume}) => {
+ setTimeout(resolve, 500);
+ onPause(() => {
+ pauseEmitted = true;
+ });
+
+ onResume(() => {
+ resumeEmitted = true;
+ });
+ }).then(() => {
+ assert.ok(passed() > 1200, `early completion (${passed()}ms)`);
+ assert.ok(pauseEmitted, 'pause event has not been emitted');
+ assert.ok(resumeEmitted, 'resume event has not been emitted');
+ });
+
+ setTimeout(() => {
+ chain.pause();
+ setTimeout(() => {
+ chain.resume();
+ }, 1000);
+ }, 300);
+
+ return chain;
+ }
+ },
+
+ 'timeout': {
+ 'should cancel the chain with timeout reason': async function () {
+ const p = new CPromise(function (resolve, reject) {
+ setTimeout(resolve, 1000);
+ });
+
+ return p
+ .timeout(100)
+ .then(() => {
+ assert.fail('chain was not cancelled');
+ }, err => {
+ assert.ok(err instanceof CanceledError);
+ assert.equal(err.message, 'timeout');
+ })
+ }
+ },
+
+ 'CPromise#then': {
+ 'should support generators': async function () {
+ return CPromise.resolve()
+ .then(function* () {
+ const result1 = yield makePromise(100, 123);
+ const result2 = yield makePromise(100, 456);
+ return result1 + result2;
+ })
+ .then(function* (value) {
+ assert.equal(value, 123 + 456);
+ })
+ }
+ },
+
+ 'CPromise#Symbol(toCPromise)': {
+ 'should be invoked to convert the object to an CPromise instance': async function () {
+ const toCPromise = Symbol.for('toCPromise');
+ let invoked = false;
+ const obj = {
+ [toCPromise]: function (CPromise) {
+ invoked = true;
+ return new CPromise((resolve) => resolve(123));
}
- },
+ };
- 'CPromise#Symbol(toCPromise)': {
- 'should be invoked to convert the object to an CPromise instance': async function () {
- const toCPromise = Symbol.for('toCPromise');
- let invoked = false;
- const obj = {
- [toCPromise]: function (CPromise) {
- invoked = true;
- return new CPromise((resolve) => resolve(123));
- }
- };
+ const promise = CPromise.from(obj);
- const promise = CPromise.from(obj);
+ assert.ok(invoked);
+ assert.ok(promise instanceof CPromise);
- assert.ok(invoked);
- assert.ok(promise instanceof CPromise);
+ return promise.then(value => {
+ assert.equal(value, 123);
+ })
+ }
+ },
+
+ 'CPromise#setProgress()': {
+ 'should set the value of the promise progress': function (done) {
+ const p = new CPromise(function (resolve, reject) {
+ let progress = 0;
+ let i = 0;
+ const timer = setInterval(() => {
+ progress += 0.2;
+ this.progress(progress, {description: 'test', value: i++});
+ if (progress >= 1) {
+ clearInterval(timer);
+ resolve('done');
+ }
+
+ }, 10);
+ });
+
+ const expect = [0.2, 0.4, 0.6, 0.8, 1];
+ let index = 0;
+
+ p.on('progress', (actualProgress, scope, data) => {
+ const expected = expect[index];
+
+ assert.equal(actualProgress, expected);
+ assert.deepStrictEqual(data, {description: 'test', value: index});
+ index++;
+ })
+
+ p.then(result => {
+ assert.equal(result, 'done');
+ done();
+ }).catch(done);
+ }
+ },
+
+ 'CPromise#canceled()': {
+ 'should catch the CanceledError rejection': async function () {
+ let onCanceledCalled = false;
+ const chain = CPromise.delay(500)
+ .canceled(() => {
+ onCanceledCalled = true;
+ })
+ .catch((err) => {
+ assert.fail(`should not throw: ${err}`);
+ })
- return promise.then(value => {
- assert.equal(value, 123);
- })
- }
- },
+ setTimeout(() => chain.cancel(), 0);
- 'CPromise#setProgress()': {
- 'should set the value of the promise progress': function (done) {
- const p = new CPromise(function (resolve, reject) {
- let progress = 0;
- let i = 0;
- const timer = setInterval(() => {
- progress += 0.2;
- this.progress(progress, {description: 'test', value: i++});
- if (progress >= 1) {
- clearInterval(timer);
- resolve('done');
- }
-
- }, 10);
- });
-
- const expect = [0.2, 0.4, 0.6, 0.8, 1];
- let index = 0;
-
- p.on('progress', (actualProgress, scope, data) => {
- const expected = expect[index];
-
- assert.equal(actualProgress, expected);
- assert.deepStrictEqual(data, {description: 'test', value: index});
- index++;
- })
+ return chain.then((value) => {
+ assert.equal(value, undefined);
+ assert.equal(onCanceledCalled, true, 'onCanceledCalled was not called');
+ assert.equal(chain.isCanceled, true, `isCanceled is not true`)
+ });
+ }
+ },
- p.then(result => {
- assert.equal(result, 'done');
- done();
- }).catch(done);
+ 'CPromise.from': {
+ 'should convert thing to a CPromise instance': async function () {
+ let isCanceled = false;
+ const thenable = {
+ then() {
+ },
+ cancel() {
+ isCanceled = true;
}
- },
+ }
- 'CPromise#canceled()': {
- 'should catch the CanceledError rejection': async function(){
- let onCanceledCalled= false;
- const chain= CPromise.delay(500)
- .canceled(()=>{
- onCanceledCalled= true;
- })
- .catch((err) => {
- assert.fail(`should not throw: ${err}`);
- })
-
- setTimeout(()=> chain.cancel(), 0);
-
- return chain.then((value)=>{
- assert.equal(value, undefined);
- assert.equal(onCanceledCalled, true, 'onCanceledCalled was not called');
- assert.equal(chain.isCanceled, true, `isCanceled is not true`)
- });
- }
- },
+ const chain = CPromise.from(thenable).then(() => {
+ assert.fail('not canceled');
+ }, (err) => {
+ assert.ok(err instanceof CanceledError);
+ assert.ok(isCanceled)
+ });
- 'CPromise.from': {
- 'should convert thing to a CPromise instance': async function () {
- let isCanceled = false;
- const thenable = {
- then() {
- },
- cancel() {
- isCanceled = true;
- }
- }
-
- const chain = CPromise.from(thenable).then(() => {
- assert.fail('not canceled');
- }, (err) => {
- assert.ok(err instanceof CanceledError);
- assert.ok(isCanceled)
- });
-
- chain.cancel();
-
- return chain;
- },
+ chain.cancel();
- 'generator': {
- 'should resolve generator to a CPromise': function () {
- assert.ok(CPromise.from(function* () {
- }) instanceof CPromise);
- },
- 'should resolve internal chains': async function () {
- const timestamp = Date.now();
- const time = () => Date.now() - timestamp;
-
- const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value));
-
- return CPromise.from(function* () {
- const resolved1 = yield CPromise.delay(105, 123);
- assert.ok(time() >= 100);
- assert.equal(resolved1, 123);
- const resolved2 = yield delay(100, 456)
- assert.equal(resolved2, 456);
- });
- },
-
- 'should reject the promise if generator thrown an error': async function () {
- const timestamp= Date.now();
- const time = () => Date.now() - timestamp;
- return CPromise.from(function* () {
- const timestamp = Date.now();
- const time = () => Date.now() - timestamp;
- yield CPromise.delay(105);
- throw Error('test');
- }).then(() => {
- assert.ok(time() >= 100, 'early throw detected');
- assert.fail('the generator did not throw an error')
- }, (err) => {
- assert.equal(err.message, 'test');
- })
- },
- 'should support cancellation': async function () {
- let thrown = false;
- let canceledInternals = false;
-
- const delay = (ms) => new CPromise((resolve, reject, {onCancel}) => {
- onCancel(() => {
- canceledInternals = true;
- });
- setTimeout(resolve, ms);
- });
-
- const chain = CPromise.from(function* () {
- yield CPromise.delay(100);
- try {
- yield delay(100);
- } catch (err) {
- thrown = true;
- assert.ok(err instanceof CanceledError, 'error is not an instanceof CanceledError');
- }
-
- yield CPromise.delay(100);
-
- if (!thrown) {
- assert.fail('The canceled error was not thrown');
- }
- });
-
- setTimeout(() => {
- chain.cancel();
- }, 150);
-
- return chain;
- },
- 'should support progress capturing': async function () {
- const expected = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1];
- let index = 0;
-
- const chain = CPromise.from(function* () {
- this.innerWeight(10)
- let i = 10;
- while (--i > 0) {
- yield CPromise.delay(100);
- }
- }).progress(value => {
- assert.equal(value, expected[index]);
- index++;
- });
-
- return chain;
- },
-
- 'should proxy signals': async function(){
- const chain= CPromise.from(function*(){
- yield new CPromise((resolve, reject, scope)=>{
- scope.on('signal', (type, data)=>{
- assert.equal(type, 'test');
- assert.equal(data, 123);
- resolve();
- })
- });
- });
-
- setTimeout(()=>{
- chain.emitSignal('test', 123);
- }, 0);
-
- return chain;
- }
- }
+ return chain;
},
- 'CPromise.all': {
- 'should be resolved with an array of inner chain values': async function () {
- const v1 = 123;
- const v2 = 456;
- const timestamp = Date.now();
- const time = () => Date.now() - timestamp;
- return CPromise.all([
- CPromise.delay(55, v1),
- CPromise.delay(105, v2)
- ]).then((values) => {
- assert.ok(time() >= 100);
- assert.deepStrictEqual(values, [v1, v2]);
- })
- },
+ 'generator': {
+ 'should resolve generator to a CPromise': function () {
+ assert.ok(CPromise.from(function* () {
+ }) instanceof CPromise);
+ },
+ 'should resolve internal chains': async function () {
+ const timestamp = Date.now();
+ const time = () => Date.now() - timestamp;
- 'should cancel pending chains on reject': async function () {
- const message = 'test';
- let canceledCounter = 0;
- return CPromise.all([
- CPromise.reject(new Error(message)),
- makePromise(50, 123, () => canceledCounter++),
- makePromise(100, 456, () => canceledCounter++),
- ]).then(() => {
- assert.fail('does not throw');
- }, err => {
- assert.equal(err.message, message);
- }).then(() => {
- assert.equal(canceledCounter, 2);
- })
- },
+ const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value));
+
+ return CPromise.from(function* () {
+ const resolved1 = yield CPromise.delay(105, 123);
+ assert.ok(time() >= 100);
+ assert.equal(resolved1, 123);
+ const resolved2 = yield delay(100, 456)
+ assert.equal(resolved2, 456);
+ });
+ },
- 'should support concurrency': async function () {
- let pending = 0;
-
- return CPromise.all(function* () {
- for (let i = 0; i < 5; i++) {
- pending++;
- yield makePromise(500, i).then((v) => {
- pending--;
- assert.ok(pending < 2);
- return v;
- });
- }
- }, {concurrency: 2}).then((values) => {
- assert.deepStrictEqual(values, [0, 1, 2, 3, 4]);
+ 'should reject the promise if generator thrown an error': async function () {
+ const timestamp = Date.now();
+ const time = () => Date.now() - timestamp;
+ return CPromise.from(function* () {
+ const timestamp = Date.now();
+ const time = () => Date.now() - timestamp;
+ yield CPromise.delay(105);
+ throw Error('test');
+ }).then(() => {
+ assert.ok(time() >= 100, 'early throw detected');
+ assert.fail('the generator did not throw an error')
+ }, (err) => {
+ assert.equal(err.message, 'test');
+ })
+ },
+ 'should support cancellation': async function () {
+ let thrown = false;
+ let canceledInternals = false;
+
+ const delay = (ms) => new CPromise((resolve, reject, {onCancel}) => {
+ onCancel(() => {
+ canceledInternals = true;
+ });
+ setTimeout(resolve, ms);
+ });
+
+ const chain = CPromise.from(function* () {
+ yield CPromise.delay(100);
+ try {
+ yield delay(100);
+ } catch (err) {
+ thrown = true;
+ assert.ok(err instanceof CanceledError, 'error is not an instanceof CanceledError');
+ }
+
+ yield CPromise.delay(100);
+
+ if (!thrown) {
+ assert.fail('The canceled error was not thrown');
+ }
+ });
+
+ setTimeout(() => {
+ chain.cancel();
+ }, 150);
+
+ return chain;
+ },
+ 'should support progress capturing': async function () {
+ const expected = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1];
+ let index = 0;
+
+ const chain = CPromise.from(function* () {
+ this.innerWeight(10)
+ let i = 10;
+ while (--i > 0) {
+ yield CPromise.delay(100);
+ }
+ }).progress(value => {
+ assert.equal(value, expected[index]);
+ index++;
+ });
+
+ return chain;
+ },
+
+ 'should proxy signals': async function () {
+ const chain = CPromise.from(function* () {
+ yield new CPromise((resolve, reject, scope) => {
+ scope.on('signal', (type, data) => {
+ assert.equal(type, 'test');
+ assert.equal(data, 123);
+ resolve();
})
- },
+ });
+ });
- 'should proxy signals': async function(){
- const chain= CPromise.all([
- new CPromise((resolve, reject, scope)=>{
- scope.on('signal', (type, data)=>{
- assert.equal(type, 'test');
- assert.equal(data, 123);
- resolve();
- })
- }),
- new CPromise((resolve, reject, scope)=>{
- scope.on('signal', (type, data)=>{
- assert.equal(type, 'test');
- assert.equal(data, 123);
- resolve();
- })
- })
- ]);
-
- setTimeout(()=>{
- chain.emitSignal('test', 123);
- }, 0);
-
- return chain;
- }
- },
+ setTimeout(() => {
+ chain.emitSignal('test', 123);
+ }, 0);
- 'CPromise.race': {
- 'should return a promise that fulfills or rejects as soon as one of the promises settled': async function () {
- const v1 = 123;
- const v2 = 456;
- const timestamp = Date.now();
- const time = () => Date.now() - timestamp;
- return CPromise.race([
- CPromise.delay(55, v1),
- CPromise.delay(100, v2)
- ]).then((value) => {
- assert.ok(time() >= 50 && time() < 100);
- assert.equal(value, v1);
- })
- },
+ return chain;
+ }
+ }
+ },
+
+ 'CPromise.all': {
+ 'should be resolved with an array of inner chain values': async function () {
+ const v1 = 123;
+ const v2 = 456;
+ const timestamp = Date.now();
+ const time = () => Date.now() - timestamp;
+ return CPromise.all([
+ CPromise.delay(55, v1),
+ CPromise.delay(105, v2)
+ ]).then((values) => {
+ assert.ok(time() >= 100);
+ assert.deepStrictEqual(values, [v1, v2]);
+ })
+ },
- 'should cancel other pending chains on settled': async function () {
- let canceledCounter = 0;
- return CPromise.race([
- makePromise(50, 123, () => canceledCounter++),
- makePromise(100, 456, () => canceledCounter++),
- makePromise(150, 789, () => canceledCounter++),
- ]).then(() => {
- assert.equal(canceledCounter, 2);
- });
- },
+ 'should cancel pending chains on reject': async function () {
+ const message = 'test';
+ let canceledCounter = 0;
+ return CPromise.all([
+ CPromise.reject(new Error(message)),
+ makePromise(50, 123, () => canceledCounter++),
+ makePromise(100, 456, () => canceledCounter++),
+ ]).then(() => {
+ assert.fail('does not throw');
+ }, err => {
+ assert.equal(err.message, message);
+ }).then(() => {
+ assert.equal(canceledCounter, 2);
+ })
+ },
- 'should proxy signals': async function(){
- return new Promise(resolve=>{
- let counter=0;
- const handle= ()=>{
- if (++counter === 2) {
- resolve();
- }
- }
-
- const chain= CPromise.race([
- new CPromise((resolve, reject, scope)=>{
- scope.on('signal', (type, data)=>{
- assert.equal(type, 'test');
- assert.equal(data, 123);
- handle();
- })
- }),
- new CPromise((resolve, reject, scope)=>{
- scope.on('signal', (type, data)=>{
- assert.equal(type, 'test');
- assert.equal(data, 123);
- handle();
- })
- })
- ]);
-
- setTimeout(()=>{
- chain.emitSignal('test', 123);
- }, 0);
- });
+ 'should support concurrency': async function () {
+ let pending = 0;
+
+ return CPromise.all(function* () {
+ for (let i = 0; i < 5; i++) {
+ pending++;
+ yield makePromise(500, i).then((v) => {
+ pending--;
+ assert.ok(pending < 2);
+ return v;
+ });
}
+ }, {concurrency: 2}).then((values) => {
+ assert.deepStrictEqual(values, [0, 1, 2, 3, 4]);
+ })
},
- 'CPromise.allSettled': async function(){
- const err= new Error('test1');
- return CPromise.allSettled([
- delay(100, 123),
- CPromise.reject(err),
- CPromise.resolve(456)
- ]).then(results=>{
- assert.deepStrictEqual(results, [
- {status: 'fulfilled', value: 123},
- {status: 'rejected', reason: err},
- {status: 'fulfilled', value: 456}
- ]);
+ 'should proxy signals': async function () {
+ const chain = CPromise.all([
+ new CPromise((resolve, reject, scope) => {
+ scope.on('signal', (type, data) => {
+ assert.equal(type, 'test');
+ assert.equal(data, 123);
+ resolve();
+ })
+ }),
+ new CPromise((resolve, reject, scope) => {
+ scope.on('signal', (type, data) => {
+ assert.equal(type, 'test');
+ assert.equal(data, 123);
+ resolve();
+ })
})
- },
+ ]);
- 'CPromise.on': {
- 'should add new listener': function () {
- const ee= new CPromise(resolve=>{});
- assert.equal(ee.listenersCount('test'), 0);
- ee.on('test', function(){});
- assert.equal(ee.listenersCount('test'), 1);
- ee.on('test', function(){});
- assert.equal(ee.listenersCount('test'), 2);
- }
+ setTimeout(() => {
+ chain.emitSignal('test', 123);
+ }, 0);
+
+ return chain;
+ }
+ },
+
+ 'CPromise.race': {
+ 'should return a promise that fulfills or rejects as soon as one of the promises settled': async function () {
+ const v1 = 123;
+ const v2 = 456;
+ const timestamp = Date.now();
+ const time = () => Date.now() - timestamp;
+ return CPromise.race([
+ CPromise.delay(55, v1),
+ CPromise.delay(100, v2)
+ ]).then((value) => {
+ assert.ok(time() >= 50 && time() < 100);
+ assert.equal(value, v1);
+ })
},
- 'CPromise.off': {
- 'should remove the listener': function () {
- const ee= new CPromise(resolve=>{});
- const listener1= function(){};
- const listener2= function(){};
- ee.on('test', listener1);
- assert.equal(ee.listenersCount('test'), 1);
- ee.on('test', listener2);
- assert.equal(ee.listenersCount('test'), 2);
- ee.off('test', listener1);
- assert.equal(ee.listenersCount('test'), 1);
- ee.off('test', listener2);
- assert.equal(ee.listenersCount('test'), 0);
- }
+ 'should cancel other pending chains on settled': async function () {
+ let canceledCounter = 0;
+ return CPromise.race([
+ makePromise(50, 123, () => canceledCounter++),
+ makePromise(100, 456, () => canceledCounter++),
+ makePromise(150, 789, () => canceledCounter++),
+ ]).then(() => {
+ assert.equal(canceledCounter, 2);
+ });
},
- 'CPromise.emit': {
- 'should emit the event listeners': function () {
- const ee= new CPromise(resolve=>{});
- let invoked1, invoked2;
- const listener1= function(...data){
- invoked1= true;
- assert.deepStrictEqual(data, [1, 2, 3]);
- };
- const listener2= function(...data){
- invoked2= true;
- assert.deepStrictEqual(data, [1, 2, 3]);
- };
- ee.on('test', listener1);
- ee.on('test', listener2);
-
- ee.emit('test', 1, 2, 3);
-
- assert.ok(invoked1);
- assert.ok(invoked2);
+ 'should proxy signals': async function () {
+ return new Promise(resolve => {
+ let counter = 0;
+ const handle = () => {
+ if (++counter === 2) {
+ resolve();
+ }
}
- },
- 'CPromise.promisify': {
- 'callback styled function': {
- 'should handle resolving with a single argument': async function () {
- const fn = CPromise.promisify(function (arg0, cb) {
- assert.strictEqual(arg0, 123);
- setTimeout(cb, 100, undefined, 456);
- });
+ const chain = CPromise.race([
+ new CPromise((resolve, reject, scope) => {
+ scope.on('signal', (type, data) => {
+ assert.equal(type, 'test');
+ assert.equal(data, 123);
+ handle();
+ })
+ }),
+ new CPromise((resolve, reject, scope) => {
+ scope.on('signal', (type, data) => {
+ assert.equal(type, 'test');
+ assert.equal(data, 123);
+ handle();
+ })
+ })
+ ]);
- assert.ok(typeof fn === 'function');
+ setTimeout(() => {
+ chain.emitSignal('test', 123);
+ }, 0);
+ });
+ }
+ },
+
+ 'CPromise.allSettled': async function () {
+ const err = new Error('test1');
+ return CPromise.allSettled([
+ delay(100, 123),
+ CPromise.reject(err),
+ CPromise.resolve(456)
+ ]).then(results => {
+ assert.deepStrictEqual(results, [
+ {status: 'fulfilled', value: 123},
+ {status: 'rejected', reason: err},
+ {status: 'fulfilled', value: 456}
+ ]);
+ })
+ },
+
+ 'CPromise.on': {
+ 'should add new listener': function () {
+ const ee = new CPromise(resolve => {
+ });
+ assert.equal(ee.listenersCount('test'), 0);
+ ee.on('test', function () {
+ });
+ assert.equal(ee.listenersCount('test'), 1);
+ ee.on('test', function () {
+ });
+ assert.equal(ee.listenersCount('test'), 2);
+ }
+ },
+
+ 'CPromise.off': {
+ 'should remove the listener': function () {
+ const ee = new CPromise(resolve => {
+ });
+ const listener1 = function () {
+ };
+ const listener2 = function () {
+ };
+ ee.on('test', listener1);
+ assert.equal(ee.listenersCount('test'), 1);
+ ee.on('test', listener2);
+ assert.equal(ee.listenersCount('test'), 2);
+ ee.off('test', listener1);
+ assert.equal(ee.listenersCount('test'), 1);
+ ee.off('test', listener2);
+ assert.equal(ee.listenersCount('test'), 0);
+ }
+ },
+
+ 'CPromise.emit': {
+ 'should emit the event listeners': function () {
+ const ee = new CPromise(resolve => {
+ });
+ let invoked1, invoked2;
+ const listener1 = function (...data) {
+ invoked1 = true;
+ assert.deepStrictEqual(data, [1, 2, 3]);
+ };
+ const listener2 = function (...data) {
+ invoked2 = true;
+ assert.deepStrictEqual(data, [1, 2, 3]);
+ };
+ ee.on('test', listener1);
+ ee.on('test', listener2);
+
+ ee.emit('test', 1, 2, 3);
+
+ assert.ok(invoked1);
+ assert.ok(invoked2);
+ }
+ },
- const promise = fn(123);
+ 'CPromise.promisify': {
+ 'callback styled function': {
+ 'should handle resolving with a single argument': async function () {
+ const fn = CPromise.promisify(function (arg0, cb) {
+ assert.strictEqual(arg0, 123);
+ setTimeout(cb, 100, undefined, 456);
+ });
- assert.ok(promise instanceof CPromise);
+ assert.ok(typeof fn === 'function');
- return promise.then(value => {
- assert.strictEqual(value, 456);
- })
- },
+ const promise = fn(123);
- 'should handle resolving with multiple arguments': async function () {
- const fn = CPromise.promisify(function (arg0, cb) {
- assert.strictEqual(arg0, 123);
- setTimeout(cb, 100, undefined, 456, 789);
- });
+ assert.ok(promise instanceof CPromise);
- assert.ok(typeof fn === 'function');
+ return promise.then(value => {
+ assert.strictEqual(value, 456);
+ })
+ },
- const promise = fn(123);
+ 'should handle resolving with multiple arguments': async function () {
+ const fn = CPromise.promisify(function (arg0, cb) {
+ assert.strictEqual(arg0, 123);
+ setTimeout(cb, 100, undefined, 456, 789);
+ });
- assert.ok(promise instanceof CPromise);
+ assert.ok(typeof fn === 'function');
- return promise.then(value => {
- assert.deepStrictEqual(value, [456, 789]);
- })
- },
+ const promise = fn(123);
- 'should handle a single argument as an array when multiArgs option activated': async function () {
- const fn = CPromise.promisify(function (arg0, cb) {
- assert.strictEqual(arg0, 123);
- setTimeout(cb, 100, undefined, 456);
- }, {multiArgs: true});
+ assert.ok(promise instanceof CPromise);
- assert.ok(typeof fn === 'function');
+ return promise.then(value => {
+ assert.deepStrictEqual(value, [456, 789]);
+ })
+ },
- const promise = fn(123);
+ 'should handle a single argument as an array when multiArgs option activated': async function () {
+ const fn = CPromise.promisify(function (arg0, cb) {
+ assert.strictEqual(arg0, 123);
+ setTimeout(cb, 100, undefined, 456);
+ }, {multiArgs: true});
- assert.ok(promise instanceof CPromise);
+ assert.ok(typeof fn === 'function');
- return promise.then(value => {
- assert.deepStrictEqual(value, [456]);
- })
- },
+ const promise = fn(123);
- 'should handle rejection': async function () {
- const fn = CPromise.promisify(function (arg0, cb) {
- assert.strictEqual(arg0, 123);
- setTimeout(cb, 100, new Error('test'));
- });
+ assert.ok(promise instanceof CPromise);
- const promise = fn(123);
+ return promise.then(value => {
+ assert.deepStrictEqual(value, [456]);
+ })
+ },
- return promise.then(value => {
- assert.fail(`doesn't throw`);
- }).catch(err => {
- assert.ok(err.message, 'test');
- })
- }
- },
+ 'should handle rejection': async function () {
+ const fn = CPromise.promisify(function (arg0, cb) {
+ assert.strictEqual(arg0, 123);
+ setTimeout(cb, 100, new Error('test'));
+ });
- 'should support async function decoration': async function () {
- const fn = CPromise.promisify(async function (arg0) {
- return delay(100, 123);
- });
+ const promise = fn(123);
- const promise = fn(123);
+ return promise.then(value => {
+ assert.fail(`doesn't throw`);
+ }).catch(err => {
+ assert.ok(err.message, 'test');
+ })
+ }
+ },
- assert.ok(promise instanceof CPromise);
+ 'should support async function decoration': async function () {
+ const fn = CPromise.promisify(async function (arg0) {
+ return delay(100, 123);
+ });
- return promise.then(value => {
- assert.deepStrictEqual(value, 123);
- })
- },
+ const promise = fn(123);
- 'should support generator function decoration': async function () {
- const fn = CPromise.promisify(function* (arg0) {
- return yield delay(100, 123);
- });
+ assert.ok(promise instanceof CPromise);
- const promise = fn(123);
+ return promise.then(value => {
+ assert.deepStrictEqual(value, 123);
+ })
+ },
- assert.ok(promise instanceof CPromise);
+ 'should support generator function decoration': async function () {
+ const fn = CPromise.promisify(function* (arg0) {
+ return yield delay(100, 123);
+ });
- return promise.then(value => {
- assert.deepStrictEqual(value, 123);
- })
- }
+ const promise = fn(123);
+
+ assert.ok(promise instanceof CPromise);
+
+ return promise.then(value => {
+ assert.deepStrictEqual(value, 123);
+ })
+ }
+ },
+
+ 'CPromise#atomic': {
+ 'atomic("detached")': {
+ 'should protect promise chain from canceling from outside and keep it running in the background': async () => {
+ let atomicChainCompleted = false;
+ const chain = CPromise.delay(200, 1)
+ .then(v => CPromise.delay(200, v + 2))
+ .then(result => {
+ atomicChainCompleted = true;
+ assert.strictEqual(result, 3);
+ }, err => {
+ assert.fail(`Unexpected rejecting: ${err}`);
+ })
+ .atomic('detached')
+ .then(v => CPromise.delay(1000))
+ .then(v => {
+ assert.fail('Unexpected resolving');
+ }, err => {
+ assert.ok(atomicChainCompleted === false);
+ assert.ok(err instanceof CanceledError);
+ })
+
+ setTimeout(() => {
+ chain.cancel();
+ }, 50);
+
+ return chain;
+ }
+ },
+
+ 'atomic("await")': {
+ 'must protect the promise chain from being canceled from the outside with waiting for it to complete': async () => {
+ let atomicChainCompleted = false;
+ const chain = CPromise.delay(200, 1)
+ .then(v => CPromise.delay(200, v + 2))
+ .then(result => {
+ atomicChainCompleted = true;
+ assert.strictEqual(result, 3);
+ }, err => {
+ assert.fail(`Unexpected rejecting: ${err}`);
+ })
+ .atomic('await')
+ .then(v => CPromise.delay(1000))
+ .then(v => {
+ assert.fail('Unexpected resolving');
+ }, err => {
+ assert.ok(atomicChainCompleted === true);
+ assert.ok(err instanceof CanceledError);
+ assert.strictEqual(err.message, 'test');
+ })
+
+ setTimeout(() => {
+ chain.cancel('test');
+ }, 50);
+
+ return chain;
+ }
}
+ }
};