├── .gitignore ├── .npmignore ├── .travis.yml ├── API.md ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── examples ├── signals.js └── suspend.js ├── jsdoc2md └── API.hbs.md ├── lib ├── abort-controller.js ├── c-promise.js ├── canceled-error.js ├── decorators.js ├── env.js ├── umd-wrapper.js ├── utils.js └── validator.js ├── package-lock.json ├── package.json ├── playground ├── basic.js ├── fetch-timeout.html ├── fetch-timeout2.html ├── fetch.html ├── generator.html ├── generator.js ├── index.html ├── rollup.config.js ├── simple.js └── src │ ├── App.js │ ├── TestComponent.js │ ├── decorators.js │ ├── index.js │ └── styles.css ├── public └── demo.gif ├── rollup.config.js └── test ├── bin └── headtest.js ├── rollup.config.js ├── runner.js ├── src └── decorators.js └── tests ├── CPromise.js └── CanceledError.js /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage/ 2 | /.nyc_output/ 3 | /dist/ 4 | /node_modules/ 5 | /trash/ 6 | /.idea/ 7 | /test/tests/decorators.js 8 | /test/tests/decorators.legacy.js 9 | /playground/dist/ 10 | /playground/build/ 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !dist/** 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "10" 5 | env: 6 | - NODE_ENV=TEST 7 | script: 8 | - npm run test:coverage 9 | after_success: npm run coveralls 10 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | ## API Reference 2 | 3 | Cancellable Promise with extra features 4 | 5 | 6 | 7 | ### CPromise.CanceledError : CanceledError 8 | CanceledError class 9 | 10 | **Kind**: static property of [CPromise](#module_CPromise) 11 | 12 | 13 | ### CPromise.AbortController : AbortController \| AbortControllerEx 14 | Refers to the AbortController class (native if available) 15 | 16 | **Kind**: static property of [CPromise](#module_CPromise) 17 | 18 | 19 | ### CPromise.AbortControllerEx : AbortControllerEx 20 | AbortControllerEx class 21 | 22 | **Kind**: static property of [CPromise](#module_CPromise) 23 | 24 | 25 | ### CPromise.E\_REASON\_CANCELED 26 | Generic cancellation reason 27 | 28 | **Kind**: static property of [CPromise](#module_CPromise) 29 | 30 | 31 | ### CPromise.E\_REASON\_DISPOSED 32 | Cancellation reason for the case when the instance will be disposed 33 | 34 | **Kind**: static property of [CPromise](#module_CPromise) 35 | 36 | 37 | ### CPromise.E\_REASON\_TIMEOUT 38 | Timeout cancellation reason 39 | 40 | **Kind**: static property of [CPromise](#module_CPromise) 41 | 42 | 43 | ### CPromise.E\_REASON\_UNMOUNTED 44 | React specific canceled reason 45 | 46 | **Kind**: static property of [CPromise](#module_CPromise) 47 | 48 | 49 | ### CPromise.async : function 50 | async decorator 51 | 52 | **Kind**: static property of [CPromise](#module_CPromise) 53 | 54 | 55 | ### CPromise.listen : function 56 | listen decorator 57 | 58 | **Kind**: static property of [CPromise](#module_CPromise) 59 | 60 | 61 | ### CPromise.cancel : function 62 | cancel decorator 63 | 64 | **Kind**: static property of [CPromise](#module_CPromise) 65 | 66 | 67 | ### CPromise.ReactComponent : function 68 | cancel decorator 69 | 70 | **Kind**: static property of [CPromise](#module_CPromise) 71 | 72 | 73 | ### CPromise.atomic : function 74 | make CPromise function atomic 75 | 76 | **Kind**: static property of [CPromise](#module_CPromise) 77 | 78 | 79 | ### CPromise.done : function 80 | append `done` chain to the resulting promise of the decorated method 81 | 82 | **Kind**: static property of [CPromise](#module_CPromise) 83 | 84 | 85 | ### CPromise.timeout : function 86 | timeout decorator 87 | 88 | **Kind**: static property of [CPromise](#module_CPromise) 89 | 90 | 91 | ### CPromise.innerWeight : function 92 | innerWeight decorator 93 | 94 | **Kind**: static property of [CPromise](#module_CPromise) 95 | 96 | 97 | ### CPromise.label : function 98 | label decorator 99 | 100 | **Kind**: static property of [CPromise](#module_CPromise) 101 | 102 | 103 | ### CPromise.canceled : function 104 | label decorator 105 | 106 | **Kind**: static property of [CPromise](#module_CPromise) 107 | 108 | 109 | ### CPromise.progress : function 110 | progress decorator 111 | 112 | **Kind**: static property of [CPromise](#module_CPromise) 113 | 114 | 115 | ### CPromise.promisify : function 116 | **Kind**: static property of [CPromise](#module_CPromise) 117 | 118 | 119 | ### CPromise~CPromise : object 120 | Creates a new CPromise instance 121 | 122 | **Kind**: inner namespace of [CPromise](#module_CPromise) 123 | **Extends**: Promise 124 | 125 | | Param | Type | Description | 126 | | --- | --- | --- | 127 | | [executor] | CPromiseExecutorFn | promise executor function that will be invoked in the context of the new CPromise instance | 128 | | [options] | CPromiseOptions | | 129 | 130 | 131 | * [~CPromise](#module_CPromise..CPromise) : object 132 | * _instance_ 133 | * [.signal](#module_CPromise..CPromise+signal) : AbortSignal 134 | * [.isPending](#module_CPromise..CPromise+isPending) ⇒ Boolean 135 | * [.isCanceled](#module_CPromise..CPromise+isCanceled) ⇒ Boolean 136 | * [.isCaptured](#module_CPromise..CPromise+isCaptured) ⇒ Boolean 137 | * [.isPaused](#module_CPromise..CPromise+isPaused) ⇒ Boolean 138 | * [.isRejected](#module_CPromise..CPromise+isRejected) ⇒ Boolean 139 | * [.parent](#module_CPromise..CPromise+parent) ⇒ CPromise \| null 140 | * [.onCancel(listener)](#module_CPromise..CPromise+onCancel) ⇒ CPromise 141 | * [.onPause(listener)](#module_CPromise..CPromise+onPause) ⇒ CPromise 142 | * [.onResume(listener)](#module_CPromise..CPromise+onResume) ⇒ CPromise 143 | * [.onCapture(listener)](#module_CPromise..CPromise+onCapture) ⇒ CPromise 144 | * [.onDone(listener)](#module_CPromise..CPromise+onDone) 145 | * [.onSignal(listener)](#module_CPromise..CPromise+onSignal) 146 | * [.onSignal(signal, listener)](#module_CPromise..CPromise+onSignal) 147 | * [.totalWeight([weight])](#module_CPromise..CPromise+totalWeight) ⇒ Number \| CPromise 148 | * [.innerWeight([weight])](#module_CPromise..CPromise+innerWeight) ⇒ Number \| CPromise 149 | * [.progress([value], [data], [scope])](#module_CPromise..CPromise+progress) ⇒ Number \| CPromise 150 | * [.propagate(type, data, [scope])](#module_CPromise..CPromise+propagate) ⇒ CPromise 151 | * [.captureProgress([options])](#module_CPromise..CPromise+captureProgress) ⇒ CPromise 152 | * [.scopes([pendingOnly])](#module_CPromise..CPromise+scopes) ⇒ Array.<CPromise> 153 | * [.timeout([ms])](#module_CPromise..CPromise+timeout) ⇒ Number \| CPromise 154 | * [.weight([weight])](#module_CPromise..CPromise+weight) ⇒ Number \| CPromise 155 | * [.label([label])](#module_CPromise..CPromise+label) ⇒ Number \| CPromise 156 | * [.resolve(value)](#module_CPromise..CPromise+resolve) ⇒ CPromise 157 | * [.reject(err)](#module_CPromise..CPromise+reject) ⇒ CPromise 158 | * [.pause(data)](#module_CPromise..CPromise+pause) ⇒ Boolean 159 | * [.resume(data)](#module_CPromise..CPromise+resume) ⇒ Boolean 160 | * [.atomic([type])](#module_CPromise..CPromise+atomic) ⇒ 161 | * [.cancel([reason], [forced])](#module_CPromise..CPromise+cancel) 162 | * [.emitSignal(type, [data], [handler], [locator])](#module_CPromise..CPromise+emitSignal) ⇒ Boolean 163 | * [.delay(ms)](#module_CPromise..CPromise+delay) ⇒ CPromise 164 | * [.aggregate([weight])](#module_CPromise..CPromise+aggregate) ⇒ CPromise 165 | * [.then(onFulfilled, [onRejected])](#module_CPromise..CPromise+then) ⇒ CPromise 166 | * [.catch(onRejected, [filter])](#module_CPromise..CPromise+catch) ⇒ CPromise 167 | * [.finally(onFinally)](#module_CPromise..CPromise+finally) ⇒ Promise.<(T\|void)> 168 | * [.done(doneHandler)](#module_CPromise..CPromise+done) ⇒ CPromise 169 | * [.canceled([onCanceled])](#module_CPromise..CPromise+canceled) ⇒ CPromise 170 | * [.listen(signal)](#module_CPromise..CPromise+listen) ⇒ CPromise 171 | * [.on(type, listener, [prepend])](#module_CPromise..CPromise+on) ⇒ CPromise 172 | * [.hasListener(event, listener)](#module_CPromise..CPromise+hasListener) ⇒ boolean 173 | * [.off(type, listener)](#module_CPromise..CPromise+off) ⇒ CPromise 174 | * [.listenersCount(type)](#module_CPromise..CPromise+listenersCount) ⇒ Number 175 | * [.hasListeners(type)](#module_CPromise..CPromise+hasListeners) ⇒ Boolean 176 | * [.once(type, listener)](#module_CPromise..CPromise+once) ⇒ CPromise 177 | * [.emit(type, ...args)](#module_CPromise..CPromise+emit) ⇒ CPromise 178 | * [.emitHook(type, ...args)](#module_CPromise..CPromise+emitHook) ⇒ Boolean 179 | * [.toString([entireChain])](#module_CPromise..CPromise+toString) ⇒ string 180 | * _static_ 181 | * [.version](#module_CPromise..CPromise.version) ⇒ string 182 | * [.versionNumber](#module_CPromise..CPromise.versionNumber) ⇒ number 183 | * [.isCanceledError(thing)](#module_CPromise..CPromise.isCanceledError) ⇒ boolean 184 | * [.delay(ms, value, [options])](#module_CPromise..CPromise.delay) ⇒ CPromise 185 | * [.all(iterable, [options])](#module_CPromise..CPromise.all) ⇒ CPromise 186 | * [.race(pending)](#module_CPromise..CPromise.race) ⇒ CPromise 187 | * [.allSettled(iterable, [options])](#module_CPromise..CPromise.allSettled) ⇒ CPromise 188 | * [.retry(fn, [options])](#module_CPromise..CPromise.retry) ⇒ CPromise 189 | * [.resolve([thing], [options])](#module_CPromise..CPromise.resolve) ⇒ CPromise 190 | * [.promisify(originalFn, [options])](#module_CPromise..CPromise.promisify) ⇒ function 191 | * [.run(generatorFn, [options])](#module_CPromise..CPromise.run) ⇒ CPromise 192 | * [.async([options])](#module_CPromise..CPromise.async) 193 | * [.listen([signals])](#module_CPromise..CPromise.listen) 194 | * [.cancel([reason], signal)](#module_CPromise..CPromise.cancel) 195 | * [.canceled(onCanceledChain)](#module_CPromise..CPromise.canceled) 196 | * [.progress(onProgressHandler)](#module_CPromise..CPromise.progress) 197 | * [.ReactComponent(options)](#module_CPromise..CPromise.ReactComponent) 198 | * [.timeout(ms)](#module_CPromise..CPromise.timeout) 199 | * [.label(str)](#module_CPromise..CPromise.label) 200 | * [.innerWeight(weight)](#module_CPromise..CPromise.innerWeight) 201 | * [.atomic(atomicType)](#module_CPromise..CPromise.atomic) 202 | * [.done(doneHandler)](#module_CPromise..CPromise.done) 203 | * [.isPromisifiedFn(fn)](#module_CPromise..CPromise.isPromisifiedFn) ⇒ \* \| boolean 204 | * [.isCPromise(thing, [anyVersion])](#module_CPromise..CPromise.isCPromise) ⇒ boolean 205 | 206 | 207 | 208 | #### cPromise.signal : AbortSignal 209 | get promise abort signal object 210 | 211 | **Kind**: instance property of [CPromise](#module_CPromise..CPromise) 212 | 213 | 214 | #### cPromise.isPending ⇒ Boolean 215 | indicates if the promise is pending 216 | 217 | **Kind**: instance property of [CPromise](#module_CPromise..CPromise) 218 | 219 | 220 | #### cPromise.isCanceled ⇒ Boolean 221 | indicates if the promise is pending 222 | 223 | **Kind**: instance property of [CPromise](#module_CPromise..CPromise) 224 | 225 | 226 | #### cPromise.isCaptured ⇒ Boolean 227 | indicates if the promise progress is captured 228 | 229 | **Kind**: instance property of [CPromise](#module_CPromise..CPromise) 230 | 231 | 232 | #### cPromise.isPaused ⇒ Boolean 233 | indicates if the promise chain is paused 234 | 235 | **Kind**: instance property of [CPromise](#module_CPromise..CPromise) 236 | 237 | 238 | #### cPromise.isRejected ⇒ Boolean 239 | indicates if the promise is rejected 240 | 241 | **Kind**: instance property of [CPromise](#module_CPromise..CPromise) 242 | 243 | 244 | #### cPromise.parent ⇒ CPromise \| null 245 | get parent promise 246 | 247 | **Kind**: instance property of [CPromise](#module_CPromise..CPromise) 248 | 249 | 250 | #### cPromise.onCancel(listener) ⇒ CPromise 251 | registers the listener for cancel event 252 | 253 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 254 | 255 | | Param | Type | 256 | | --- | --- | 257 | | listener | OnCancelListener | 258 | 259 | 260 | 261 | #### cPromise.onPause(listener) ⇒ CPromise 262 | registers the listener for pause event 263 | 264 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 265 | 266 | | Param | Type | 267 | | --- | --- | 268 | | listener | OnPauseListener | 269 | 270 | 271 | 272 | #### cPromise.onResume(listener) ⇒ CPromise 273 | registers the listener for resume event 274 | 275 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 276 | 277 | | Param | Type | 278 | | --- | --- | 279 | | listener | OnResumeListener | 280 | 281 | 282 | 283 | #### cPromise.onCapture(listener) ⇒ CPromise 284 | registers the listener for capture event 285 | 286 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 287 | 288 | | Param | Type | 289 | | --- | --- | 290 | | listener | OnCaptureListener | 291 | 292 | 293 | 294 | #### cPromise.onDone(listener) 295 | registers the listener for done event 296 | 297 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 298 | 299 | | Param | Type | 300 | | --- | --- | 301 | | listener | CPDoneListener | 302 | 303 | 304 | 305 | #### cPromise.onSignal(listener) 306 | registers the listener for done event 307 | 308 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 309 | 310 | | Param | Type | 311 | | --- | --- | 312 | | listener | CPSignalListener | 313 | 314 | 315 | 316 | #### cPromise.onSignal(signal, listener) 317 | registers the listener for done event 318 | 319 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 320 | 321 | | Param | Type | 322 | | --- | --- | 323 | | signal | Signal | 324 | | listener | CPSignalListener | 325 | 326 | 327 | 328 | #### cPromise.totalWeight([weight]) ⇒ Number \| CPromise 329 | Set or get the total weight of the inner chains 330 | 331 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 332 | 333 | | Param | Type | 334 | | --- | --- | 335 | | [weight] | Number | 336 | 337 | 338 | 339 | #### cPromise.innerWeight([weight]) ⇒ Number \| CPromise 340 | Set or get the total weight of the inner chains 341 | 342 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 343 | 344 | | Param | Type | 345 | | --- | --- | 346 | | [weight] | Number | 347 | 348 | 349 | 350 | #### cPromise.progress([value], [data], [scope]) ⇒ Number \| CPromise 351 | Set promise progress 352 | 353 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 354 | 355 | | Param | Type | Description | 356 | | --- | --- | --- | 357 | | [value] | Number | a number between [0, 1] | 358 | | [data] | \* | any data to send for progress event listeners | 359 | | [scope] | CPromise | CPromise scope | 360 | 361 | 362 | 363 | #### cPromise.propagate(type, data, [scope]) ⇒ CPromise 364 | emit propagate event that will propagate through each promise scope in the chain (bubbling) 365 | 366 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 367 | 368 | | Param | Type | Default | Description | 369 | | --- | --- | --- | --- | 370 | | type | String \| symbol | | some type to identify the data kind | 371 | | data | \* | | some data | 372 | | [scope] | CPromise | | CPromise scope | 373 | 374 | 375 | 376 | #### cPromise.captureProgress([options]) ⇒ CPromise 377 | capture initial progress state of the chain 378 | 379 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 380 | 381 | | Param | Type | Description | 382 | | --- | --- | --- | 383 | | [options] | Object | | 384 | | options.throttle | Number | set min interval for firing progress event | 385 | | options.innerWeight | Number | set weight of the nested promises | 386 | 387 | 388 | 389 | #### cPromise.scopes([pendingOnly]) ⇒ Array.<CPromise> 390 | Returns all parent scopes that are in pending state 391 | 392 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 393 | 394 | | Param | Type | Default | 395 | | --- | --- | --- | 396 | | [pendingOnly] | boolean | false | 397 | 398 | 399 | 400 | #### cPromise.timeout([ms]) ⇒ Number \| CPromise 401 | timeout before the promise will be canceled 402 | 403 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 404 | 405 | | Param | Type | Description | 406 | | --- | --- | --- | 407 | | [ms] | Number | timeout in ms | 408 | 409 | 410 | 411 | #### cPromise.weight([weight]) ⇒ Number \| CPromise 412 | Sets the promise weight in progress capturing process 413 | 414 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 415 | **Returns**: Number \| CPromise - returns weight if no arguments were specified 416 | 417 | | Param | Type | Description | 418 | | --- | --- | --- | 419 | | [weight] | Number | any number greater or equal 0 | 420 | 421 | 422 | 423 | #### cPromise.label([label]) ⇒ Number \| CPromise 424 | Sets the promise label 425 | 426 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 427 | **Returns**: Number \| CPromise - returns weight if no arguments were specified 428 | 429 | | Param | Type | Description | 430 | | --- | --- | --- | 431 | | [label] | String | any string | 432 | 433 | 434 | 435 | #### cPromise.resolve(value) ⇒ CPromise 436 | Resolves the promise with given value 437 | 438 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 439 | 440 | | Param | 441 | | --- | 442 | | value | 443 | 444 | 445 | 446 | #### cPromise.reject(err) ⇒ CPromise 447 | Rejects the promise with given error 448 | 449 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 450 | 451 | | Param | 452 | | --- | 453 | | err | 454 | 455 | 456 | 457 | #### cPromise.pause(data) ⇒ Boolean 458 | Pause promise 459 | 460 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 461 | 462 | | Param | Type | 463 | | --- | --- | 464 | | data | \* | 465 | 466 | 467 | 468 | #### cPromise.resume(data) ⇒ Boolean 469 | Resume promise 470 | 471 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 472 | 473 | | Param | Type | 474 | | --- | --- | 475 | | data | \* | 476 | 477 | 478 | 479 | #### cPromise.atomic([type]) ⇒ 480 | Make promise chain atomic (non-cancellable for external signals) 481 | 482 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 483 | **Returns**: CPromise 484 | 485 | | Param | Type | 486 | | --- | --- | 487 | | [type] | AtomicType | 488 | 489 | 490 | 491 | #### cPromise.cancel([reason], [forced]) 492 | throws the CanceledError that cause promise chain cancellation 493 | 494 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 495 | 496 | | Param | Type | Default | 497 | | --- | --- | --- | 498 | | [reason] | String \| Error | | 499 | | [forced] | Boolean | false | 500 | 501 | 502 | 503 | #### cPromise.emitSignal(type, [data], [handler], [locator]) ⇒ Boolean 504 | Emit a signal of the specific type 505 | 506 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 507 | 508 | | Param | Type | 509 | | --- | --- | 510 | | type | Signal | 511 | | [data] | \* | 512 | | [handler] | SignalHandler | 513 | | [locator] | SignalLocator | 514 | 515 | 516 | 517 | #### cPromise.delay(ms) ⇒ CPromise 518 | Returns a chain that will be resolved after specified timeout 519 | 520 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 521 | 522 | | Param | Type | 523 | | --- | --- | 524 | | ms | Number | 525 | 526 | 527 | 528 | #### cPromise.aggregate([weight]) ⇒ CPromise 529 | Aggregate promise chain into one promise 530 | 531 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 532 | 533 | | Param | Type | Default | 534 | | --- | --- | --- | 535 | | [weight] | number | 1 | 536 | 537 | 538 | 539 | #### cPromise.then(onFulfilled, [onRejected]) ⇒ CPromise 540 | returns a CPromise. It takes up to two arguments: callback functions for the success and failure cases of the Promise. 541 | 542 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 543 | 544 | | Param | Type | 545 | | --- | --- | 546 | | onFulfilled | onFulfilled | 547 | | [onRejected] | onRejected | 548 | 549 | 550 | 551 | #### cPromise.catch(onRejected, [filter]) ⇒ CPromise 552 | Catches rejection with optionally specified Error class 553 | 554 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 555 | 556 | | Param | Type | 557 | | --- | --- | 558 | | onRejected | function | 559 | | [filter] | Error | 560 | 561 | 562 | 563 | #### cPromise.finally(onFinally) ⇒ Promise.<(T\|void)> 564 | Add handler that will be invoked when promise settled 565 | 566 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 567 | 568 | | Param | Type | 569 | | --- | --- | 570 | | onFinally | CPFinallyHandler | 571 | 572 | 573 | 574 | #### cPromise.done(doneHandler) ⇒ CPromise 575 | Add a handler that will be called after the promise has been fulfilled, but unlike `finally`, the returned plain value will not be ignored 576 | 577 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 578 | 579 | | Param | Type | 580 | | --- | --- | 581 | | doneHandler | CPDoneHandler | 582 | 583 | 584 | 585 | #### cPromise.canceled([onCanceled]) ⇒ CPromise 586 | Catches CancelError rejection 587 | 588 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 589 | 590 | | Param | Type | 591 | | --- | --- | 592 | | [onCanceled] | function | 593 | 594 | 595 | 596 | #### cPromise.listen(signal) ⇒ CPromise 597 | Listen for abort signal 598 | 599 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 600 | 601 | | Param | Type | 602 | | --- | --- | 603 | | signal | AbortSignal | 604 | 605 | 606 | 607 | #### cPromise.on(type, listener, [prepend]) ⇒ CPromise 608 | adds a new listener 609 | 610 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 611 | 612 | | Param | Type | Default | 613 | | --- | --- | --- | 614 | | type | EventType | | 615 | | listener | function | | 616 | | [prepend] | Boolean | false | 617 | 618 | 619 | 620 | #### cPromise.hasListener(event, listener) ⇒ boolean 621 | Check whether the listener is already registered to the specific event 622 | 623 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 624 | 625 | | Param | Type | 626 | | --- | --- | 627 | | event | EventType | 628 | | listener | function | 629 | 630 | 631 | 632 | #### cPromise.off(type, listener) ⇒ CPromise 633 | removes the listener 634 | 635 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 636 | 637 | | Param | Type | 638 | | --- | --- | 639 | | type | EventType | 640 | | listener | function | 641 | 642 | 643 | 644 | #### cPromise.listenersCount(type) ⇒ Number 645 | returns listeners count of the specific event type 646 | 647 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 648 | 649 | | Param | Type | 650 | | --- | --- | 651 | | type | EventType | 652 | 653 | 654 | 655 | #### cPromise.hasListeners(type) ⇒ Boolean 656 | checks if there are listeners of a specific type 657 | 658 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 659 | 660 | | Param | Type | 661 | | --- | --- | 662 | | type | String \| Symbol | 663 | 664 | 665 | 666 | #### cPromise.once(type, listener) ⇒ CPromise 667 | add 'once' listener 668 | 669 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 670 | 671 | | Param | Type | 672 | | --- | --- | 673 | | type | EventType | 674 | | listener | function | 675 | 676 | 677 | 678 | #### cPromise.emit(type, ...args) ⇒ CPromise 679 | emits the event 680 | 681 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 682 | 683 | | Param | Type | 684 | | --- | --- | 685 | | type | EventType | 686 | | ...args | | 687 | 688 | 689 | 690 | #### cPromise.emitHook(type, ...args) ⇒ Boolean 691 | Emits event as a hook. If some listener return true, this method will immediately return true as the result. Else false will be retuned 692 | 693 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 694 | 695 | | Param | Type | 696 | | --- | --- | 697 | | type | EventType | 698 | | ...args | | 699 | 700 | 701 | 702 | #### cPromise.toString([entireChain]) ⇒ string 703 | Render promise to String 704 | 705 | **Kind**: instance method of [CPromise](#module_CPromise..CPromise) 706 | 707 | | Param | Type | Default | Description | 708 | | --- | --- | --- | --- | 709 | | [entireChain] | boolean | false | render the entire promise chain | 710 | 711 | 712 | 713 | #### CPromise.version ⇒ string 714 | CPromise version string 715 | 716 | **Kind**: static property of [CPromise](#module_CPromise..CPromise) 717 | 718 | 719 | #### CPromise.versionNumber ⇒ number 720 | CPromise version number 721 | 722 | **Kind**: static property of [CPromise](#module_CPromise..CPromise) 723 | 724 | 725 | #### CPromise.isCanceledError(thing) ⇒ boolean 726 | Checks if thing is an CanceledError instance 727 | 728 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 729 | 730 | | Param | 731 | | --- | 732 | | thing | 733 | 734 | 735 | 736 | #### CPromise.delay(ms, value, [options]) ⇒ CPromise 737 | Returns a CPromise that will be resolved after specified timeout 738 | 739 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 740 | 741 | | Param | Type | Default | Description | 742 | | --- | --- | --- | --- | 743 | | ms | Number | | delay before resolve the promise with specified value | 744 | | value | | | | 745 | | [options] | object | | | 746 | | [options.progressTick] | number | 1000 | progress timer tick, must be >= 100ms | 747 | 748 | 749 | 750 | #### CPromise.all(iterable, [options]) ⇒ CPromise 751 | Returns a single CPromise that resolves to an array of the results of the input promises. If one fails then other promises will be canceled immediately 752 | 753 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 754 | 755 | | Param | Type | 756 | | --- | --- | 757 | | iterable | Iterable \| Generator \| GeneratorFunction \| array | 758 | | [options] | CPAllOptions | 759 | 760 | **Example** 761 | ```js 762 | CPromise.all(function*(){ yield axios.get(url1); yield axios.get(url2); yield axios.get(url3); }, {concurrency: 1}).then(console.log) 763 | ``` 764 | 765 | 766 | #### CPromise.race(pending) ⇒ CPromise 767 | returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise. Other pending promises will be canceled immediately 768 | 769 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 770 | 771 | | Param | Type | 772 | | --- | --- | 773 | | pending | Iterable | 774 | 775 | 776 | 777 | #### CPromise.allSettled(iterable, [options]) ⇒ CPromise 778 | returns a promise that resolves after all of the given promises have either fulfilled or rejected 779 | 780 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 781 | 782 | | Param | Type | 783 | | --- | --- | 784 | | iterable | Iterable \| Generator \| GeneratorFunction | 785 | | [options] | CPAllOptions | 786 | 787 | 788 | 789 | #### CPromise.retry(fn, [options]) ⇒ CPromise 790 | Retry async operation 791 | 792 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 793 | 794 | | Param | Type | Default | 795 | | --- | --- | --- | 796 | | fn | CPGeneratorRetryFunction \| CPRetryFunction | | 797 | | [options] | Object | | 798 | | [options.args] | Array | | 799 | | [options.retries] | Number | | 800 | | [options.delayWeight] | Number | | 801 | | [options.delay] | Number \| CPRetryDelayResolver | | 802 | | [options.scopeArg] | Boolean | false | 803 | 804 | 805 | 806 | #### CPromise.resolve([thing], [options]) ⇒ CPromise 807 | Converts thing to CPromise using the following rules: - CPromise instance returns as is - Objects with special method defined with key `Symbol.for('toCPromise')` will be converted using this method The result will be cached for future calls - Thenable wraps into a new CPromise instance, if thenable has the `cancel` method it will be used for canceling - Generator function will be resolved to CPromise - Array will be resoled via `CPromise.all`, arrays with one element (e.g. `[[1000]]`) will be resolved via `CPromise.race` This method returns null if the conversion failed. 808 | 809 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 810 | 811 | | Param | Type | 812 | | --- | --- | 813 | | [thing] | \* | 814 | | [options] | resolveOptionsObject \| Boolean | 815 | 816 | 817 | 818 | #### CPromise.promisify(originalFn, [options]) ⇒ function 819 | Converts callback styled function|GeneratorFn|AsyncFn to CPromise async function 820 | 821 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 822 | 823 | | Param | Type | 824 | | --- | --- | 825 | | originalFn | function \| GeneratorFunction \| AsyncFunction | 826 | | [options] | PromisifyOptions \| function \| Boolean | 827 | 828 | 829 | 830 | #### CPromise.run(generatorFn, [options]) ⇒ CPromise 831 | Resolves the generator to an CPromise instance 832 | 833 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 834 | 835 | | Param | Type | Description | 836 | | --- | --- | --- | 837 | | generatorFn | GeneratorFunction | | 838 | | [options] | Object | | 839 | | [options.args] | Array | | 840 | | [options.resolveSignatures] | Boolean | resolve extra signatures (like arrays with CPromise.all) | 841 | | [options.scopeArg] | Boolean | pass the CPromise scope as the first argument to the generator function | 842 | | [options.context] | \* | | 843 | 844 | 845 | 846 | #### CPromise.async([options]) 847 | Decorator to make CPromise async function from generator, ECMA async or callback-styled method 848 | 849 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 850 | 851 | | Param | Type | 852 | | --- | --- | 853 | | [options] | object | 854 | | [options.timeout] | number | 855 | | [options.label] | string | 856 | | [options.innerWeight] | number | 857 | | [options.weight] | number | 858 | | [options.listen] | AbortControllerId \| AbortController \| AbortSignal \| Array.<(AbortControllerId\|AbortController\|AbortSignal)> | 859 | | [options.atomic] | AtomicType | 860 | 861 | 862 | 863 | #### CPromise.listen([signals]) 864 | Decorator to subscribe CPromise async method to the internal or external controller 865 | 866 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 867 | 868 | | Param | Type | 869 | | --- | --- | 870 | | [signals] | AbortControllerId \| AbortController \| AbortSignal \| Array.<(AbortControllerId\|AbortController\|AbortSignal)> | 871 | 872 | 873 | 874 | #### CPromise.cancel([reason], signal) 875 | Decorator to cancel internal or external abort controller before the decorated function invocation. Can be used as a plain function by passing a object context with `.call` or `.apply` methods 876 | 877 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 878 | 879 | | Param | Type | 880 | | --- | --- | 881 | | [reason] | string | 882 | | signal | AbortControllerId \| AbortController | 883 | 884 | **Example** 885 | ```js 886 | el.onclick= ()=> cancel.call(this, reason, 'myControllerId'); - to use the decorator as a plain function 887 | ``` 888 | 889 | 890 | #### CPromise.canceled(onCanceledChain) 891 | Decorator to add an `onCanceled` rejection handler to the resulting promise of the decorated method 892 | 893 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 894 | 895 | | Param | Type | 896 | | --- | --- | 897 | | onCanceledChain | function \| GeneratorFunction | 898 | 899 | 900 | 901 | #### CPromise.progress(onProgressHandler) 902 | Decorator to subscribe the handler to the `onProgress` event of the resulting promise 903 | 904 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 905 | 906 | | Param | Type | 907 | | --- | --- | 908 | | onProgressHandler | ProgressDecoratorHandler | 909 | 910 | 911 | 912 | #### CPromise.ReactComponent(options) 913 | Decorate class as React component 914 | 915 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 916 | 917 | | Param | Type | 918 | | --- | --- | 919 | | options | boolean \| ReactComponentDecoratorOptions | 920 | 921 | 922 | 923 | #### CPromise.timeout(ms) 924 | Decorator to set timeout for the resulting promise of the decorated function 925 | 926 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 927 | 928 | | Param | Type | 929 | | --- | --- | 930 | | ms | number | 931 | 932 | 933 | 934 | #### CPromise.label(str) 935 | Decorator to set label for the resulting promise of the decorated function 936 | 937 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 938 | 939 | | Param | Type | 940 | | --- | --- | 941 | | str | string | 942 | 943 | 944 | 945 | #### CPromise.innerWeight(weight) 946 | Decorator to set innerWeight for the resulting promise of the decorated function 947 | 948 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 949 | 950 | | Param | Type | 951 | | --- | --- | 952 | | weight | number | 953 | 954 | 955 | 956 | #### CPromise.atomic(atomicType) 957 | Decorator to set timeout for the resulting promise of the decorated function 958 | 959 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 960 | 961 | | Param | Type | 962 | | --- | --- | 963 | | atomicType | AtomicType | 964 | 965 | 966 | 967 | #### CPromise.done(doneHandler) 968 | append `done` chain to the resulting promise of the decorated method 969 | 970 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 971 | 972 | | Param | Type | 973 | | --- | --- | 974 | | doneHandler | CPDecoratorDoneHandler | 975 | 976 | 977 | 978 | #### CPromise.isPromisifiedFn(fn) ⇒ \* \| boolean 979 | Returns promisification strategy that was used to the original function 980 | 981 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 982 | 983 | | Param | Type | 984 | | --- | --- | 985 | | fn | function | 986 | 987 | 988 | 989 | #### CPromise.isCPromise(thing, [anyVersion]) ⇒ boolean 990 | Check whether object is CPromise instance 991 | 992 | **Kind**: static method of [CPromise](#module_CPromise..CPromise) 993 | 994 | | Param | Type | Default | 995 | | --- | --- | --- | 996 | | thing | \* | | 997 | | [anyVersion] | boolean | false | 998 | 999 | 1000 | 1001 | ### CPromise~EventType : String \| Symbol 1002 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1003 | 1004 | 1005 | ### CPromise~CPromiseExecutorFn : function 1006 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1007 | **this**: CPromise 1008 | 1009 | | Param | Type | 1010 | | --- | --- | 1011 | | resolve | function | 1012 | | reject | function | 1013 | | scope | CPromise | 1014 | 1015 | 1016 | 1017 | ### CPromise~PromiseOptionsObject : Object 1018 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1019 | **Properties** 1020 | 1021 | | Name | Type | Default | Description | 1022 | | --- | --- | --- | --- | 1023 | | label | String | | | 1024 | | timeout | Number | | | 1025 | | weight | Number | | | 1026 | | [nativeController] | Boolean | false | prefer native AbortController class as the internal signal | 1027 | 1028 | 1029 | 1030 | ### CPromise~CPromiseOptions : PromiseOptionsObject \| String \| Number 1031 | If value is a number it will be considered as the value for timeout option If value is a string it will be considered as a label 1032 | 1033 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1034 | 1035 | 1036 | ### CPromise~OnCancelListener : function 1037 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1038 | 1039 | | Param | Type | 1040 | | --- | --- | 1041 | | reason | CanceledError | 1042 | 1043 | 1044 | 1045 | ### CPromise~OnPauseListener : function 1046 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1047 | 1048 | 1049 | ### CPromise~OnResumeListener : function 1050 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1051 | 1052 | 1053 | ### CPromise~OnCaptureListener : function 1054 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1055 | 1056 | | Param | Type | 1057 | | --- | --- | 1058 | | CPromise | scope | 1059 | 1060 | 1061 | 1062 | ### CPromise~CPDoneListener ⇒ CPromise 1063 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1064 | 1065 | | Param | Type | 1066 | | --- | --- | 1067 | | value | \* | 1068 | | isRejected | boolean | 1069 | 1070 | 1071 | 1072 | ### CPromise~CPSignalListener ⇒ Boolean 1073 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1074 | 1075 | | Param | Type | 1076 | | --- | --- | 1077 | | type | Signal | 1078 | | data | \* | 1079 | 1080 | 1081 | 1082 | ### CPromise~AtomicType : number \| boolean \| "disabled" \| "detached" \| "await" 1083 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1084 | 1085 | 1086 | ### CPromise~Signal : String \| Symbol 1087 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1088 | 1089 | 1090 | ### CPromise~SignalHandler ⇒ Boolean 1091 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1092 | **this**: {CPromise} 1093 | 1094 | | Param | Type | 1095 | | --- | --- | 1096 | | data | \* | 1097 | | type | Signal | 1098 | | scope | CPromise | 1099 | 1100 | 1101 | 1102 | ### CPromise~SignalLocator ⇒ Boolean 1103 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1104 | **this**: {CPromise} 1105 | 1106 | | Param | Type | 1107 | | --- | --- | 1108 | | data | \* | 1109 | | type | Signal | 1110 | | scope | CPromise | 1111 | | isRoot | Boolean | 1112 | 1113 | 1114 | 1115 | ### CPromise~CPFinallyHandler : function 1116 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1117 | **this**: CPromise 1118 | 1119 | | Param | Type | 1120 | | --- | --- | 1121 | | settledValue | \* | 1122 | | isRejected | boolean | 1123 | | scope | CPromise | 1124 | 1125 | 1126 | 1127 | ### CPromise~CPDoneHandler : function 1128 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1129 | **this**: CPromise 1130 | 1131 | | Param | Type | 1132 | | --- | --- | 1133 | | settledValue | \* | 1134 | | isRejected | boolean | 1135 | | scope | CPromise | 1136 | 1137 | 1138 | 1139 | ### CPromise~CPAllOptions : object 1140 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1141 | **Properties** 1142 | 1143 | | Name | Type | Description | 1144 | | --- | --- | --- | 1145 | | [concurrency] | number | limit concurrency of promise being run simultaneously | 1146 | | [mapper] | function | function to map each element | 1147 | | [ignoreResults] | boolean | do not collect results | 1148 | | [signatures] | boolean | use advanced signatures for vales resolving | 1149 | 1150 | 1151 | 1152 | ### CPromise~CPRetryFunction ⇒ \* 1153 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1154 | 1155 | | Param | Type | 1156 | | --- | --- | 1157 | | attempt | number | 1158 | | args | array | 1159 | 1160 | 1161 | 1162 | ### CPromise~CPGeneratorRetryFunction ⇒ \* 1163 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1164 | 1165 | | Param | Type | 1166 | | --- | --- | 1167 | | scope | CPromise | 1168 | | attempt | number | 1169 | | args | array | 1170 | 1171 | 1172 | 1173 | ### CPromise~CPRetryDelayResolver ⇒ number 1174 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1175 | **Returns**: number - a delay in ms before next attempt 1176 | 1177 | | Param | Type | 1178 | | --- | --- | 1179 | | attempt | number | 1180 | | retries | number | 1181 | 1182 | 1183 | 1184 | ### CPromise~resolveOptionsObject : Object 1185 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1186 | **Properties** 1187 | 1188 | | Name | Type | Default | 1189 | | --- | --- | --- | 1190 | | [resolveSignatures] | Boolean | true | 1191 | | [atomic] | AtomicType | true | 1192 | | [args] | \* | | 1193 | 1194 | 1195 | 1196 | ### CPromise~PromisifyFinalizeFn : function 1197 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1198 | 1199 | | Param | Type | 1200 | | --- | --- | 1201 | | result | \* | 1202 | | scope | CPromise | 1203 | 1204 | 1205 | 1206 | ### CPromise~CPPromisifyDecoratorFn ⇒ function 1207 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1208 | 1209 | | Param | Type | Description | 1210 | | --- | --- | --- | 1211 | | originalFn | function | function to decorate | 1212 | | options | PromisifyOptions | | 1213 | 1214 | 1215 | 1216 | ### CPromise~PromisifyOptions : Object 1217 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1218 | **Properties** 1219 | 1220 | | Name | Type | Default | Description | 1221 | | --- | --- | --- | --- | 1222 | | [multiArgs] | Boolean | | aggregate all passed arguments to an array | 1223 | | [finalize] | PromisifyFinalizeFn | | aggregate all passed arguments to an array | 1224 | | [fnType] | "plain" \| "generator" \| "async" | | | 1225 | | [scopeArg] | boolean | | pass the CPromise scope as the first argument to the generator function | 1226 | | [decorator] | function | | CPPromisifyDecoratorFn | 1227 | | [alignArgs] | boolean | | align passed arguments to function definition for callback-styled function | 1228 | | [once] | boolean | true | don't promisify already promisified function | 1229 | | [types] | array.<('plain'\|'async'\|'generator')> | | function types to promisify | 1230 | 1231 | 1232 | 1233 | ### CPromise~AbortControllerId : string \| symbol 1234 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1235 | 1236 | 1237 | ### CPromise~ProgressDecoratorHandler : function 1238 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1239 | 1240 | | Param | Type | 1241 | | --- | --- | 1242 | | progress | number | 1243 | | scope | CPromise | 1244 | | data | \* | 1245 | | context | object | 1246 | 1247 | 1248 | 1249 | ### CPromise~ReactComponentDecoratorOptions : object 1250 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1251 | **Properties** 1252 | 1253 | | Name | Type | Default | 1254 | | --- | --- | --- | 1255 | | [subscribeAll] | boolean | false | 1256 | | [bindListeners] | boolean | true | 1257 | | [bindMethods] | boolean | true | 1258 | 1259 | 1260 | 1261 | ### CPromise~CPDecoratorDoneHandler : function 1262 | **Kind**: inner typedef of [CPromise](#module_CPromise) 1263 | 1264 | | Param | Type | 1265 | | --- | --- | 1266 | | value | \* | 1267 | | isRejected | boolean | 1268 | | scope | CPromise | 1269 | | context | object | 1270 | 1271 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Changelog 2 | 3 | All notable changes to this project will be documented in this file. Dates are displayed in UTC. 4 | 5 | Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). 6 | 7 | #### [v0.13.12](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.11...v0.13.12) 8 | 9 | - Fixed progress capturing support for `CPromise.run` method; [`d5fead1`](https://github.com/DigitalBrainJS/c-promise/commit/d5fead1b977028df82c31b109ed68f4ad3176740) 10 | 11 | #### [v0.13.11](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.10...v0.13.11) 12 | 13 | > 31 January 2022 14 | 15 | - Added support for signal filter parameter in `onSignal` method; [`104654a`](https://github.com/DigitalBrainJS/c-promise/commit/104654a8154d4156dbaa689f762a91b3bb5b0a2b) 16 | 17 | #### [v0.13.10](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.9...v0.13.10) 18 | 19 | > 31 January 2022 20 | 21 | - Fixed atomic support in generators context by the `CPromise.run` method; [`2366f27`](https://github.com/DigitalBrainJS/c-promise/commit/2366f2741cd5920ff3634b03f7af02d630549c0d) 22 | 23 | #### [v0.13.9](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.8...v0.13.9) 24 | 25 | > 29 January 2022 26 | 27 | - Improved progress throttling logic; [`185dd8a`](https://github.com/DigitalBrainJS/c-promise/commit/185dd8a0ec5454ce8b999885518ef55ee7870d61) 28 | - Fixed README.md; [`1d741fe`](https://github.com/DigitalBrainJS/c-promise/commit/1d741fe895f15fda1d72f867df2169389863ff26) 29 | 30 | #### [v0.13.8](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.7...v0.13.8) 31 | 32 | > 17 October 2021 33 | 34 | - Refactored README.md; [`e939c51`](https://github.com/DigitalBrainJS/c-promise/commit/e939c51f5bcae191700e8e457341e966ca9d7457) 35 | - Added Wiki link; [`38802f7`](https://github.com/DigitalBrainJS/c-promise/commit/38802f735287c9ea0e045ac0dac4843d2e1f5999) 36 | 37 | #### [v0.13.7](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.6...v0.13.7) 38 | 39 | > 24 May 2021 40 | 41 | - Fixed `retry` method to catch sync errors; [`14ddfb8`](https://github.com/DigitalBrainJS/c-promise/commit/14ddfb8e3978a80f75129158785f1699041307e4) 42 | 43 | #### [v0.13.6](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.5...v0.13.6) 44 | 45 | > 24 May 2021 46 | 47 | - Fixed `retry` method; [`1f678be`](https://github.com/DigitalBrainJS/c-promise/commit/1f678be9057c02cf7f4ddc7539222ff6d1d153c7) 48 | 49 | #### [v0.13.5](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.4...v0.13.5) 50 | 51 | > 21 May 2021 52 | 53 | - Added support for propagation pause and resume events; [`acc8995`](https://github.com/DigitalBrainJS/c-promise/commit/acc899575e475fc72e78a1c1cc745c7c12e75ad0) 54 | 55 | #### [v0.13.4](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.3...v0.13.4) 56 | 57 | > 20 May 2021 58 | 59 | - Fixed version injection by the build flow; [`7d03432`](https://github.com/DigitalBrainJS/c-promise/commit/7d03432e0087e11eb1e524595aaf8673b3071825) 60 | - Fixed a bug due to which wrong values passed to the `done` event listeners on promise fulfill; [`82964ed`](https://github.com/DigitalBrainJS/c-promise/commit/82964ed0a325edcee59eedeef75cb0af728a5281) 61 | 62 | #### [v0.13.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.2...v0.13.3) 63 | 64 | > 13 May 2021 65 | 66 | - Fixed a bug with determining the presence of a native AbortController; [`a5a1394`](https://github.com/DigitalBrainJS/c-promise/commit/a5a1394dcd1d961f3e2a0378bbd5865ee8fde097) 67 | 68 | #### [v0.13.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.1...v0.13.2) 69 | 70 | > 12 May 2021 71 | 72 | - Fixed getFnType logic [`#2`](https://github.com/DigitalBrainJS/c-promise/pull/2) 73 | - Refactored to avoid using constructor names; [`190747a`](https://github.com/DigitalBrainJS/c-promise/commit/190747a8e010f7c222639ca87913d9357f61cdda) 74 | 75 | #### [v0.13.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.13.0...v0.13.1) 76 | 77 | > 8 May 2021 78 | 79 | - Added the ability for the all, race, allSettled methods to take into account the atomic type of the nested chain; [`4008a2e`](https://github.com/DigitalBrainJS/c-promise/commit/4008a2eb6e80ac707e2ad89ee4acf6ffa5d6bcba) 80 | 81 | #### [v0.13.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.12.5...v0.13.0) 82 | 83 | > 7 May 2021 84 | 85 | - Removed `CPromise.from` method; [`b79f8c5`](https://github.com/DigitalBrainJS/c-promise/commit/b79f8c54e62729409ab4e590c11189e1711cf1a4) 86 | 87 | #### [v0.12.5](https://github.com/DigitalBrainJS/c-promise/compare/v0.12.4...v0.12.5) 88 | 89 | > 5 May 2021 90 | 91 | - Fixed a bug with capturing the progress of a generator with nested chains; [`7d1b6ae`](https://github.com/DigitalBrainJS/c-promise/commit/7d1b6ae88b1d9070e14d85c16ad80da9c6c2d0b3) 92 | 93 | #### [v0.12.4](https://github.com/DigitalBrainJS/c-promise/compare/v0.12.3...v0.12.4) 94 | 95 | > 2 May 2021 96 | 97 | - Fixed @ReactComponent bug; [`2d75308`](https://github.com/DigitalBrainJS/c-promise/commit/2d753082a168d54ea7e98bbfb745f9cf4769c09b) 98 | 99 | #### [v0.12.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.12.2...v0.12.3) 100 | 101 | > 1 May 2021 102 | 103 | - Added the ability to omit the empty `componentWillUnmount` method while still being able to automatically cancel async routines on unmount; [`d5685d3`](https://github.com/DigitalBrainJS/c-promise/commit/d5685d3b594f6855295981136a79de1d798e5f20) 104 | 105 | #### [v0.12.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.12.1...v0.12.2) 106 | 107 | > 29 April 2021 108 | 109 | - Added `bindMethods` and `bindListeners` options for the @ReactComponent decorator; [`4e86048`](https://github.com/DigitalBrainJS/c-promise/commit/4e860487cdf69423f9daac43daf2bce85f6b5db5) 110 | 111 | #### [v0.12.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.12.0...v0.12.1) 112 | 113 | > 27 April 2021 114 | 115 | - Fixed `finally` method; [`4239923`](https://github.com/DigitalBrainJS/c-promise/commit/423992300c5134e131f9d6bc667a9fa723f47767) 116 | - Added @rollup/plugin-json to the build flow; [`d9d5dcc`](https://github.com/DigitalBrainJS/c-promise/commit/d9d5dcc1eb7827a03e39f2ee741fc75c32872d82) 117 | 118 | #### [v0.12.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.11...v0.12.0) 119 | 120 | > 26 April 2021 121 | 122 | - Reworked decorators system; [`f50ff58`](https://github.com/DigitalBrainJS/c-promise/commit/f50ff586de72e67f4e92a64307581306f051e0bc) 123 | - Added playground/build to .gitignore; [`c8f3058`](https://github.com/DigitalBrainJS/c-promise/commit/c8f3058b433051b626d276ae406ffec62bfdcf55) 124 | - Updated README; [`98724eb`](https://github.com/DigitalBrainJS/c-promise/commit/98724eb48cdb5a55f4fdce52bdf842773bac0790) 125 | - Updated README; [`9c04744`](https://github.com/DigitalBrainJS/c-promise/commit/9c04744f51e5dcdd50dffedb09e3bd90afc8693f) 126 | - Added `@ReactComponent` decorator test; [`71282be`](https://github.com/DigitalBrainJS/c-promise/commit/71282be8bb0b56b45ff479593d55c81aea12e136) 127 | - Updated package.json; [`2ab6b92`](https://github.com/DigitalBrainJS/c-promise/commit/2ab6b92301e6ef9c144fde67e0e2393f3e09b7d6) 128 | 129 | #### [v0.11.11](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.10...v0.11.11) 130 | 131 | > 21 April 2021 132 | 133 | - Fixed bug with context for `onDone` shortcut method; [`95d1c01`](https://github.com/DigitalBrainJS/c-promise/commit/95d1c016f168087f64e7468929deca32ffccd90e) 134 | 135 | #### [v0.11.10](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.9...v0.11.10) 136 | 137 | > 21 April 2021 138 | 139 | - Fixed `promisify` bug with scope passing to the original function; [`5d6e32c`](https://github.com/DigitalBrainJS/c-promise/commit/5d6e32c385ee8f410fc292bc2f576256fd496e48) 140 | 141 | #### [v0.11.9](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.8...v0.11.9) 142 | 143 | > 20 April 2021 144 | 145 | - Improved `toString()` logic; [`7876164`](https://github.com/DigitalBrainJS/c-promise/commit/7876164afc29843b83f9d226383590ed5c942228) 146 | 147 | #### [v0.11.8](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.7...v0.11.8) 148 | 149 | > 19 April 2021 150 | 151 | - Fixed cancellation of the `allSettled` method; [`6862b8f`](https://github.com/DigitalBrainJS/c-promise/commit/6862b8f16d15e0eef31e1d44993333f374f40261) 152 | 153 | #### [v0.11.7](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.6...v0.11.7) 154 | 155 | > 18 April 2021 156 | 157 | - Added `decorator` option for the `.promisify` method; [`63e7895`](https://github.com/DigitalBrainJS/c-promise/commit/63e7895e4747560ed348f36975875806b7d1ce84) 158 | 159 | #### [v0.11.6](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.5...v0.11.6) 160 | 161 | > 17 April 2021 162 | 163 | - Fix: improved `.delay` timer disposing logic; [`4de5852`](https://github.com/DigitalBrainJS/c-promise/commit/4de5852e4f3f7871362bddf5ade3690e31d18c30) 164 | 165 | #### [v0.11.5](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.4...v0.11.5) 166 | 167 | > 17 April 2021 168 | 169 | - Added the ability for `.delay` to notify intermediate progress values; [`eb32ab5`](https://github.com/DigitalBrainJS/c-promise/commit/eb32ab5021e7dcaa085d5196cc6068309c9fb67b) 170 | 171 | #### [v0.11.4](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.3...v0.11.4) 172 | 173 | > 16 April 2021 174 | 175 | - Fixed bug with progress capturing of nested chains; [`15c0c0c`](https://github.com/DigitalBrainJS/c-promise/commit/15c0c0c22be5f484d613de2c8d5b94a5f0493fd5) 176 | - Fixed npm docs script; [`f5ac87d`](https://github.com/DigitalBrainJS/c-promise/commit/f5ac87d1482e29e7c36965d3f783c3b076aec763) 177 | - Added comparison table; [`6fabc2d`](https://github.com/DigitalBrainJS/c-promise/commit/6fabc2dd5abda65f50115b759fef6cf4a6efdf6f) 178 | - Fixed README.md section with retry usage examples; [`6640360`](https://github.com/DigitalBrainJS/c-promise/commit/6640360fdbb256d26addab8e54ad2720a8ff26b5) 179 | - Fixed JSDoc types for the `.toString()`; [`e9a48d3`](https://github.com/DigitalBrainJS/c-promise/commit/e9a48d3e6750184ba44b2fbeb2d255a4fa4116e6) 180 | 181 | #### [v0.11.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.2...v0.11.3) 182 | 183 | > 13 April 2021 184 | 185 | - Fixed a potential bug with late internal event listeners appending; [`6e3e0b2`](https://github.com/DigitalBrainJS/c-promise/commit/6e3e0b263ad08b5821f96d8443b8c9f481f25ad2) 186 | - Fixed build badge link; [`1bbe995`](https://github.com/DigitalBrainJS/c-promise/commit/1bbe9950c7f74b95fe873fb43870d11a70dc0114) 187 | 188 | #### [v0.11.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.1...v0.11.2) 189 | 190 | > 13 April 2021 191 | 192 | - Fixed a bug with `_objectToCPromise` that happens if the `thing` has nullable value; [`7daa396`](https://github.com/DigitalBrainJS/c-promise/commit/7daa3966fa01ce4d46799f1f7ca3c0a98aa2fcc9) 193 | 194 | #### [v0.11.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.11.0...v0.11.1) 195 | 196 | > 13 April 2021 197 | 198 | - Updated auto-changelog config; [`82aeccd`](https://github.com/DigitalBrainJS/c-promise/commit/82aeccd461fa4102ef2122e37117244d177f5af7) 199 | - Fixed a bug with generator progress capturing, if onProgress listener was attached before the first yield; [`8bb1161`](https://github.com/DigitalBrainJS/c-promise/commit/8bb1161cf9a7555e9c8fb1bb72c863165f9c2172) 200 | 201 | #### [v0.11.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.8...v0.11.0) 202 | 203 | > 13 April 2021 204 | 205 | - Removed `scope` argument that was previously passed to generators as the last argument; [`e78b2d6`](https://github.com/DigitalBrainJS/c-promise/commit/e78b2d6dba32e1ac3e85007d405c8a492874f0b9) 206 | 207 | #### [v0.10.8](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.7...v0.10.8) 208 | 209 | > 13 December 2020 210 | 211 | - exported `promisify` method; [`8c9746b`](https://github.com/DigitalBrainJS/c-promise/commit/8c9746b2ba686dd7836aa6f5401297058cb3c7e2) 212 | - Added tests for `CanceledError.rethrow` method; [`e3d9f6f`](https://github.com/DigitalBrainJS/c-promise/commit/e3d9f6f8493a237e5007bb2ecae8d0856bc24a00) 213 | - spellcheck; [`895d0d6`](https://github.com/DigitalBrainJS/c-promise/commit/895d0d6bee1ea60acac56c00be64c92e10bde483) 214 | - spellcheck; [`aa32a7b`](https://github.com/DigitalBrainJS/c-promise/commit/aa32a7b2b72b7d79f3257cb43a894ce34315ec1c) 215 | 216 | #### [v0.10.7](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.6...v0.10.7) 217 | 218 | > 12 December 2020 219 | 220 | - Added promisify method; [`b07585d`](https://github.com/DigitalBrainJS/c-promise/commit/b07585d0cc14dd00767c81dd02e04b3015752287) 221 | - Updated README; [`96ed673`](https://github.com/DigitalBrainJS/c-promise/commit/96ed67344eb89ba17c002d04d578880499b0b0e0) 222 | 223 | #### [v0.10.6](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.5...v0.10.6) 224 | 225 | > 10 December 2020 226 | 227 | - added `weight`, `innerWeight` and `label` options for the `async` decorator; [`25a1d8d`](https://github.com/DigitalBrainJS/c-promise/commit/25a1d8d0d52c3ec9c160cb471e9f0f685ce358be) 228 | 229 | #### [v0.10.5](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.4...v0.10.5) 230 | 231 | > 8 December 2020 232 | 233 | - Updated README; [`e9f1041`](https://github.com/DigitalBrainJS/c-promise/commit/e9f10415a4859805a3c6b1951ca8f923ea2b2170) 234 | 235 | #### [v0.10.4](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.3...v0.10.4) 236 | 237 | > 7 December 2020 238 | 239 | - Added @progress decorator; [`b982644`](https://github.com/DigitalBrainJS/c-promise/commit/b982644bdefb428b53c976117661890a8b097fb5) 240 | - Updated README; [`ce89805`](https://github.com/DigitalBrainJS/c-promise/commit/ce89805191c43a95451c8484c96408c15cb380d9) 241 | - Updated README; [`9b5261e`](https://github.com/DigitalBrainJS/c-promise/commit/9b5261e57549555f5582cb3f4b7cebe5ca6dbdd9) 242 | 243 | #### [v0.10.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.2...v0.10.3) 244 | 245 | > 7 December 2020 246 | 247 | - Fixed a bug with cleaning internal timer; [`b1b91a1`](https://github.com/DigitalBrainJS/c-promise/commit/b1b91a1650d30d1a4ee73ba2a6ad53caa57bba4c) 248 | - Updated CHANGELOG.md; [`459fdbf`](https://github.com/DigitalBrainJS/c-promise/commit/459fdbf0cfef848d109e35212dd68bcd32ab6018) 249 | 250 | #### [v0.10.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.1...v0.10.2) 251 | 252 | > 7 December 2020 253 | 254 | - Added @canceled decorator; [`7b53ca3`](https://github.com/DigitalBrainJS/c-promise/commit/7b53ca36faf9436dfdaadb978a2a4b6976ce300b) 255 | 256 | #### [v0.10.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.10.0...v0.10.1) 257 | 258 | > 7 December 2020 259 | 260 | - Refactored AbortController; [`6255fb1`](https://github.com/DigitalBrainJS/c-promise/commit/6255fb18eed4d59d380ec71754bf56d519963555) 261 | - Improved JSDoc annotation; [`5d4aafe`](https://github.com/DigitalBrainJS/c-promise/commit/5d4aafee14fac3e399c220b05b4ba0636bce7c50) 262 | - Updated CHANGELOG.md; [`37c4829`](https://github.com/DigitalBrainJS/c-promise/commit/37c4829be34e4c384eba12a62c96230d83e1259a) 263 | 264 | #### [v0.10.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.9.1...v0.10.0) 265 | 266 | > 6 December 2020 267 | 268 | - Added decorators support; [`ed3be00`](https://github.com/DigitalBrainJS/c-promise/commit/ed3be00e4009e8367a960d6912ce8d8029771608) 269 | - Updated readme; [`5eb8611`](https://github.com/DigitalBrainJS/c-promise/commit/5eb861155d0f1445bdaff4512d14b744f7de996c) 270 | - Fixed async decorator anchor in the README; [`ab1e49c`](https://github.com/DigitalBrainJS/c-promise/commit/ab1e49c375080251755a583944043843849cd7c6) 271 | 272 | #### [v0.9.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.9.0...v0.9.1) 273 | 274 | > 30 November 2020 275 | 276 | - Added generator support for the `then` method; [`ee41529`](https://github.com/DigitalBrainJS/c-promise/commit/ee415295d127915f17833fbb18b9929adf8068e2) 277 | - Refactored test for `CPromise.all`; [`4f28354`](https://github.com/DigitalBrainJS/c-promise/commit/4f28354ace5b0d502a45fa1eed8310c98b4cddbd) 278 | - Updated CHANGELOG.md; [`cfbe7cb`](https://github.com/DigitalBrainJS/c-promise/commit/cfbe7cb2ec0f10843d13886ea9ad8652f81b5b35) 279 | 280 | #### [v0.9.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.8.2...v0.9.0) 281 | 282 | > 29 November 2020 283 | 284 | - Improved cancellation logic to work properly with multi leaves chains; [`cef6c54`](https://github.com/DigitalBrainJS/c-promise/commit/cef6c544ffb0d3f81bd9113c1b5704eed621e32a) 285 | - Updated CHANGELOG.md; [`7b2ab2c`](https://github.com/DigitalBrainJS/c-promise/commit/7b2ab2c78edaafba562fb55665f860aac535f361) 286 | 287 | #### [v0.8.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.8.1...v0.8.2) 288 | 289 | > 28 November 2020 290 | 291 | - Improved README.md; [`f417c13`](https://github.com/DigitalBrainJS/c-promise/commit/f417c13d618b3d9b412166a1d491b912408e588d) 292 | 293 | #### [v0.8.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.8.0...v0.8.1) 294 | 295 | > 28 November 2020 296 | 297 | - Made the promise executor optional; [`50e6649`](https://github.com/DigitalBrainJS/c-promise/commit/50e66497077c8305315efd75b52637a4489d3b14) 298 | 299 | #### [v0.8.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.6...v0.8.0) 300 | 301 | > 27 November 2020 302 | 303 | - Added `canceled` method to catch CanceledError rejection; [`b5f1336`](https://github.com/DigitalBrainJS/c-promise/commit/b5f1336a42af1373c34eee35098122adb42c900e) 304 | - Added `canceled` method to catch CanceledError rejection; [`3d87a7f`](https://github.com/DigitalBrainJS/c-promise/commit/3d87a7f31c1a95fafae2e39c1f64047e2151458f) 305 | 306 | #### [v0.7.6](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.5...v0.7.6) 307 | 308 | > 26 November 2020 309 | 310 | - refactored allSettled; [`a7a6829`](https://github.com/DigitalBrainJS/c-promise/commit/a7a68299b748d2cf7e59b9670184635e7f8cf532) 311 | 312 | #### [v0.7.5](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.4...v0.7.5) 313 | 314 | > 26 November 2020 315 | 316 | - Fixed allSettled bug with options resolving; [`3fc84c3`](https://github.com/DigitalBrainJS/c-promise/commit/3fc84c3b7079d7b138f41bc43ca22ee11101ad7d) 317 | 318 | #### [v0.7.4](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.3...v0.7.4) 319 | 320 | > 26 November 2020 321 | 322 | - Improved isCanceled state for then method; [`004f032`](https://github.com/DigitalBrainJS/c-promise/commit/004f032a32b95a2b090466762da3da4d9ec03f07) 323 | 324 | #### [v0.7.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.2...v0.7.3) 325 | 326 | > 25 November 2020 327 | 328 | - Fixed React example anchor; [`7716058`](https://github.com/DigitalBrainJS/c-promise/commit/77160586d0c50c3d9d1d9dfd0869dbba41c81188) 329 | 330 | #### [v0.7.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.1...v0.7.2) 331 | 332 | > 25 November 2020 333 | 334 | - Added React example; [`d82c8a7`](https://github.com/DigitalBrainJS/c-promise/commit/d82c8a7a7ad2a21e58ca6c86eeac47df2d42c8fd) 335 | 336 | #### [v0.7.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.7.0...v0.7.1) 337 | 338 | > 25 November 2020 339 | 340 | - Added listen method; [`4af990f`](https://github.com/DigitalBrainJS/c-promise/commit/4af990fb76b54b75d63beb474a7a3de41d11397d) 341 | 342 | #### [v0.7.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.6.1...v0.7.0) 343 | 344 | > 24 November 2020 345 | 346 | - Improved signals API; [`bdddb5e`](https://github.com/DigitalBrainJS/c-promise/commit/bdddb5e55df4c6b0eb225cfe2456c04925abeb38) 347 | - Updated CHANGELOG.md; [`c2b24f1`](https://github.com/DigitalBrainJS/c-promise/commit/c2b24f1d3da72fe91305bf3fd4f00ec964cdd29f) 348 | 349 | #### [v0.6.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.6.0...v0.6.1) 350 | 351 | > 22 November 2020 352 | 353 | - Updated README.md; [`feabfe0`](https://github.com/DigitalBrainJS/c-promise/commit/feabfe0ee3d845670c6e54472508caf8a724211b) 354 | 355 | #### [v0.6.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.5.3...v0.6.0) 356 | 357 | > 22 November 2020 358 | 359 | - Added signals support; [`ee002ea`](https://github.com/DigitalBrainJS/c-promise/commit/ee002ea1cd8aafeb03d5cdd39516ff538aca2112) 360 | - Added signals support; [`25e2b0a`](https://github.com/DigitalBrainJS/c-promise/commit/25e2b0adae7c3ba92e2bf043badd76f2c73e2d24) 361 | - Fixed jsdoc docs; [`b49520a`](https://github.com/DigitalBrainJS/c-promise/commit/b49520a6012321b346f06dbddd12e05a6d97e8eb) 362 | 363 | #### [v0.5.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.5.2...v0.5.3) 364 | 365 | > 17 October 2020 366 | 367 | - Updated CHANGELOG.md; [`2736ac4`](https://github.com/DigitalBrainJS/c-promise/commit/2736ac4f801db9b092142ac13adea05a5b097246) 368 | - Updated CHANGELOG.md; [`990f481`](https://github.com/DigitalBrainJS/c-promise/commit/990f481bef5f4af1b50d0dfc73c68163efb3f7ee) 369 | 370 | #### [v0.5.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.5.1...v0.5.2) 371 | 372 | > 16 October 2020 373 | 374 | - Fixed docs; [`e75ec21`](https://github.com/DigitalBrainJS/c-promise/commit/e75ec21986ce5ae7ffb8aba1245585bcd7a321f4) 375 | - Fixed bug with promise cancellation for `all` method [`c3ab73f`](https://github.com/DigitalBrainJS/c-promise/commit/c3ab73f41787e19a142893aa3dcd476545f1cc6b) 376 | 377 | #### [v0.5.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.5.0...v0.5.1) 378 | 379 | > 14 October 2020 380 | 381 | - Fixed bug with promise cancellation for `all` method [`011ff3f`](https://github.com/DigitalBrainJS/c-promise/commit/011ff3f2967d0f6d702ce23688705b0c59285431) 382 | 383 | #### [v0.5.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.4.2...v0.5.0) 384 | 385 | > 13 October 2020 386 | 387 | - Added concurrency, mapper, signatures options for `all` method; [`bcb4e63`](https://github.com/DigitalBrainJS/c-promise/commit/bcb4e63408c9caed093f433301e9ea2e89547e15) 388 | 389 | #### [v0.4.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.4.1...v0.4.2) 390 | 391 | > 25 September 2020 392 | 393 | - Updated README.md; [`f96a103`](https://github.com/DigitalBrainJS/c-promise/commit/f96a1034752c25694d14fbaafb00f4e7d8c98024) 394 | 395 | #### [v0.4.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.4.0...v0.4.1) 396 | 397 | > 24 September 2020 398 | 399 | - Updated README.md; [`55cf393`](https://github.com/DigitalBrainJS/c-promise/commit/55cf39375c467ed6fe887fb7ba94edd9e4fe46cf) 400 | 401 | #### [v0.4.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.3.2...v0.4.0) 402 | 403 | > 20 September 2020 404 | 405 | - Added outer abort signal support; [`ecf8905`](https://github.com/DigitalBrainJS/c-promise/commit/ecf890509362043a790a963484291e8614f40881) 406 | - Updated JSDoc signatures; [`cb2055c`](https://github.com/DigitalBrainJS/c-promise/commit/cb2055cec1494a62849669d806599da7f197c256) 407 | - Added CHANGELOG.md; [`86067d3`](https://github.com/DigitalBrainJS/c-promise/commit/86067d3306101fc857a662a8d8b979293e9f3a08) 408 | - Updated JSDoc signatures; [`bccee42`](https://github.com/DigitalBrainJS/c-promise/commit/bccee424084c9bdfd30074ee6065057c5a82d284) 409 | 410 | #### [v0.3.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.3.1...v0.3.2) 411 | 412 | > 17 September 2020 413 | 414 | - Introduced AsyncGeneratorScope class; [`2d1f427`](https://github.com/DigitalBrainJS/c-promise/commit/2d1f427f0a8ade2049f3db879cf611316d311104) 415 | 416 | #### [v0.3.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.3.0...v0.3.1) 417 | 418 | > 15 September 2020 419 | 420 | - Fixed CDN links; [`36e2c45`](https://github.com/DigitalBrainJS/c-promise/commit/36e2c454a39d8f48ce778f1f73428d5ee1efa51a) 421 | 422 | #### [v0.3.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.2.1...v0.3.0) 423 | 424 | > 15 September 2020 425 | 426 | - Updated README.hbs.md; [`503fc03`](https://github.com/DigitalBrainJS/c-promise/commit/503fc0358d6345d353bb83848c674fedcfe85404) 427 | - Removed plugin-replace; [`5be940a`](https://github.com/DigitalBrainJS/c-promise/commit/5be940a9e9eea87105054de56addab7e68e72eeb) 428 | 429 | #### [v0.2.1](https://github.com/DigitalBrainJS/c-promise/compare/v0.2.0...v0.2.1) 430 | 431 | > 14 September 2020 432 | 433 | - Fixed all and race methods to support iterable input; [`6829b41`](https://github.com/DigitalBrainJS/c-promise/commit/6829b41a2216211bfb4602c618159166c6d8d86e) 434 | 435 | #### [v0.2.0](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.9...v0.2.0) 436 | 437 | > 13 September 2020 438 | 439 | - Updated README.hbs.md; [`1a80ce5`](https://github.com/DigitalBrainJS/c-promise/commit/1a80ce57766dc42d3846c3285914a2fb6e7b943b) 440 | - Fixed pre-commit script; [`b6439f9`](https://github.com/DigitalBrainJS/c-promise/commit/b6439f957159274094ce91754d7e8c7f1374ca56) 441 | - Added debounce option for scope.progress; [`295d433`](https://github.com/DigitalBrainJS/c-promise/commit/295d43303a9f723eca0b3aacf5372703897fd892) 442 | - Updated README.hbs.md; [`cdaf10a`](https://github.com/DigitalBrainJS/c-promise/commit/cdaf10a42c12781f41738622dbded2efb7ace1a9) 443 | - Added package size badges; [`f52b1fe`](https://github.com/DigitalBrainJS/c-promise/commit/f52b1fe700861ee497fd0632a28f16a6b17afeef) 444 | - Fixed pre-commit script; [`f0ddee7`](https://github.com/DigitalBrainJS/c-promise/commit/f0ddee719521331e3d65e84f82e06b2dbe54ca43) 445 | - Updated package size badges; [`8eec56c`](https://github.com/DigitalBrainJS/c-promise/commit/8eec56c1ac4a7d6eaecc4aee9de20b5549bb66f1) 446 | 447 | #### [v0.1.9](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.8...v0.1.9) 448 | 449 | > 11 September 2020 450 | 451 | - Updated README.hbs.md; [`747d620`](https://github.com/DigitalBrainJS/c-promise/commit/747d620c153cca0cdea10f7e5c04f51e43fa2a7a) 452 | 453 | #### [v0.1.8](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.7...v0.1.8) 454 | 455 | > 11 September 2020 456 | 457 | - Added CPromise.from method; [`e384f6d`](https://github.com/DigitalBrainJS/c-promise/commit/e384f6d20ecb29c168ca21ce91dd1b0e2f539445) 458 | - Added timeout test; [`b41a296`](https://github.com/DigitalBrainJS/c-promise/commit/b41a29607d9cce0c82ccaf0b30afe6511a4433da) 459 | 460 | #### [v0.1.7](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.6...v0.1.7) 461 | 462 | > 11 September 2020 463 | 464 | - Fixed timeout cancellation; [`3238d79`](https://github.com/DigitalBrainJS/c-promise/commit/3238d79b89d19a768d630025a6c2298411bab044) 465 | 466 | #### [v0.1.6](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.5...v0.1.6) 467 | 468 | > 10 September 2020 469 | 470 | - Updated README.md; [`2fa1bc2`](https://github.com/DigitalBrainJS/c-promise/commit/2fa1bc2b63eb065207112d86131c3fd22d3c980a) 471 | 472 | #### [v0.1.5](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.4...v0.1.5) 473 | 474 | > 10 September 2020 475 | 476 | - Fixed build script; [`47f2b17`](https://github.com/DigitalBrainJS/c-promise/commit/47f2b172bea4ec8fda8e51cb07aeb59079ea45d1) 477 | 478 | #### [v0.1.4](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.3...v0.1.4) 479 | 480 | > 10 September 2020 481 | 482 | - Fixed prepublishOnly script; [`d1156b9`](https://github.com/DigitalBrainJS/c-promise/commit/d1156b9ea65e67f8cab9c06c4486efbc195cb91f) 483 | 484 | #### [v0.1.3](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.2...v0.1.3) 485 | 486 | > 10 September 2020 487 | 488 | - Updated .npmignore; [`3ca5cd0`](https://github.com/DigitalBrainJS/c-promise/commit/3ca5cd0cfc9e7ad56d7f1896fa9fd56439b7f69d) 489 | - Updated README.md; [`2f948dd`](https://github.com/DigitalBrainJS/c-promise/commit/2f948dde6e736fc8ecbd13a36524e55dd4f8a202) 490 | 491 | #### [v0.1.2](https://github.com/DigitalBrainJS/c-promise/compare/v0.1.1...v0.1.2) 492 | 493 | > 10 September 2020 494 | 495 | - Updated README.md; Fixed coveralls; [`323858e`](https://github.com/DigitalBrainJS/c-promise/commit/323858e6e0640ef666a6cded4c671ec051bdc82f) 496 | - Updated README.md; [`e35d155`](https://github.com/DigitalBrainJS/c-promise/commit/e35d1554e38f272af4ac94aefeb9606da7f8c64f) 497 | - Updated README.md; [`12318cd`](https://github.com/DigitalBrainJS/c-promise/commit/12318cdf39e2d9fe4bcd3c5f27138fe1fa970506) 498 | - Updated README.md; [`a5c1436`](https://github.com/DigitalBrainJS/c-promise/commit/a5c1436136a96e8b67f845de96101f686afc7c12) 499 | 500 | #### v0.1.1 501 | 502 | > 10 September 2020 503 | 504 | - Initial commit [`f932cb8`](https://github.com/DigitalBrainJS/c-promise/commit/f932cb8584ebd458d7961aba53f8f33c797bb650) 505 | - Updated README.md [`a3d3f94`](https://github.com/DigitalBrainJS/c-promise/commit/a3d3f94ad8e166d6081942bca7555d00a3f6d8e9) 506 | - Updated README.md [`7b15cb8`](https://github.com/DigitalBrainJS/c-promise/commit/7b15cb8e419624ea322c7b46bb4c6b9a05eb284a) 507 | - Updated README.md [`f13d677`](https://github.com/DigitalBrainJS/c-promise/commit/f13d6773c7eab9004b83a46d01be80a9843c3c72) 508 | - Updated README.md [`8bef0a5`](https://github.com/DigitalBrainJS/c-promise/commit/8bef0a5169fa2859d0102a98c4d77b8ede575e6c) 509 | - Updated README.md [`f4e0d36`](https://github.com/DigitalBrainJS/c-promise/commit/f4e0d36d0dbae76411ed044059759d69c89a4271) 510 | - Updated README.md [`db4b88b`](https://github.com/DigitalBrainJS/c-promise/commit/db4b88b1b9661e47f850f490fe023542e07a5e48) 511 | - Updated README.md [`06d90b2`](https://github.com/DigitalBrainJS/c-promise/commit/06d90b2cf8771b976c40288f5bed05aca360a40b) 512 | - Updated README.md [`4df153e`](https://github.com/DigitalBrainJS/c-promise/commit/4df153e676d0dbf42e982db7f0703b8d09f3a096) 513 | - Updated README.md [`ebaae6b`](https://github.com/DigitalBrainJS/c-promise/commit/ebaae6bd3452c167531932e9346107ccca75517a) 514 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2020 Mozgovoy Dmitriy and Contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the 'Software'), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is furnished 11 | to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 20 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/DigitalBrainJS/c-promise.svg?branch=master)](https://travis-ci.com/DigitalBrainJS/c-promise) 2 | [![Coverage Status](https://coveralls.io/repos/github/DigitalBrainJS/c-promise/badge.svg?branch=master)](https://coveralls.io/github/DigitalBrainJS/c-promise?branch=master) 3 | ![npm](https://img.shields.io/npm/dm/c-promise2) 4 | ![npm bundle size](https://img.shields.io/bundlephobia/minzip/c-promise2) 5 | ![David](https://img.shields.io/david/DigitalBrainJS/c-promise) 6 | [![Stars](https://badgen.net/github/stars/DigitalBrainJS/c-promise)](https://github.com/DigitalBrainJS/c-promise/stargazers) 7 | 8 | ## CPromise2 9 | 10 | This lib provides `CPromise` class - an advanced version of the built-in Promise that supports: 11 | - deep cancellation **through rejection**. All static methods like `all`/`race`/`allSettled` support cancellation. 12 | - `all` & `allSettled` method support concurrency limitation and generators as promise producers 13 | - advanced progress capturing 14 | - flat async code writing using `generators/yield` as an alternative of `async/await` 15 | - event data flows (upstream & downstream) 16 | - `pause` & `resume` ability 17 | - timeouts 18 | - retries 19 | - `AbortController` support (providing & subscribing for multiple signals sources) 20 | - atomic subchains - such chains will be completed if the execution has been started, even if its upper chain received a Cancel signal. 21 | 22 | ## Installation :hammer: 23 | 24 | npm: 25 | ```bash 26 | $ npm install c-promise2 27 | ``` 28 | yarn: 29 | ```bash 30 | $ yarn add c-promise2 31 | ``` 32 | CDN: 33 | - [production UMD version](https://unpkg.com/c-promise2) 34 | (or [minified](https://unpkg.com/c-promise2/dist/c-promise.umd.min.js) ~11KB) - provides the CPromise class 35 | as the default export, other exports values declared as static properties 36 | 37 | - [production CommonJS version](https://unpkg.com/c-promise2/dist/c-promise.cjs.js) 38 | 39 | - [production ESM version](https://unpkg.com/c-promise2/dist/c-promise.mjs) 40 | 41 | ### Quick start 42 | 43 | - Import CPromise class: 44 | 45 | ````js 46 | import { CPromise } from "c-promise2"; 47 | ```` 48 | 49 | - Use `CPromise` constructor instead of `Promise`. To terminate internal async tasks (timers, request, streams etc.) gracefully subscribe your cleanup handler with `onCancel(handler)`: 50 | 51 | ````js 52 | const doTask = (ms)=>{ 53 | return CPromise((resolve, reject, {onCancel})=>{ 54 | const timer= setTimeout(resolve, ms, "myValue"); 55 | onCancel(()=> clearTimeout(timer)); 56 | }); 57 | } 58 | 59 | const promise = doTask(1000).then(console.log); 60 | ```` 61 | 62 | Or/and turn generators to async functions with `CPromise.promisify` to write cancellable async code in flat style: 63 | ````js 64 | const doTask = CPromise.promisify(function*(ms){ 65 | yield CPromise.delay(ms); 66 | return "myValue"; 67 | }); 68 | 69 | const promise = doTask(1000).then(console.log); 70 | ```` 71 | - Call `promise.cancel([reason])` to cancel pending promise chain by rejecting the deepest 72 | pending promise in the chain with a special `CanceledError` reason: 73 | ````js 74 | promise.cancel("My bad"); 75 | ```` 76 | 77 | ### Basic example 78 | 79 | #### Building plain CPromise chains using `then` 80 | [Codesandbox Live Demo](https://codesandbox.io/s/c-promise2-readme-basic1-7d8u0) 81 | ````javascript 82 | import { CPromise } from "c-promise2"; 83 | 84 | const promise= new CPromise((resolve, reject, {onCancel, onPause, onResume})=>{ 85 | onCancel(()=>{ 86 | //optionally some code here to abort your long-term task (abort request, stop timers etc.) 87 | }); 88 | }).then( 89 | value => console.log(`Done: ${value}`), 90 | (err, scope) => { 91 | console.warn(`Failed: ${err}`); // Failed: CanceledError: canceled 92 | console.log('chain isCanceled:', promise.isCanceled); // true 93 | console.log('promise isCanceled:', scope.isCanceled); // true 94 | } 95 | ); 96 | 97 | console.log('isPromise:', promise instanceof Promise); // true 98 | 99 | setTimeout(()=> promise.cancel(), 1000); 100 | ```` 101 | 102 | Log: 103 | ```` 104 | isPromise: true 105 | Failed: CanceledError: canceled 106 | chain isCanceled: true 107 | promise isCanceled: true 108 | ```` 109 | 110 | #### Writing flat code using generators 111 | 112 | [Codesandbox Live Demo](https://codesandbox.io/s/cpromise-readme-flat-code1-forked-cg4ch?file=/src/index.js) 113 | 114 | ````javascript 115 | import { CPromise } from "c-promise2"; 116 | 117 | const sayHello = CPromise.promisify(function* (v) { 118 | for (let i = 0; i < 3; i++) { 119 | console.log(`Hello [${i}]`); 120 | yield CPromise.delay(1000); 121 | } 122 | return v + 1; 123 | }); 124 | 125 | const p = sayHello(5) 126 | .then(function* (v) { 127 | console.log(`Then`); 128 | yield CPromise.delay(1000); 129 | return v + 1; 130 | }) 131 | .then((v) => console.log(`Done: ${v}`)); 132 | 133 | // setTimeout(() => p.cancel(), 1000); stop trying 134 | ```` 135 | 136 | #### Abortable fetch with timeout 137 | 138 | This is how an abortable fetch ([live example](https://jsfiddle.net/DigitalBrain/c6njyrt9/10/)) with a timeout might look like 139 | ````javascript 140 | function fetchWithTimeout(url, {timeout, ...fetchOptions}= {}) { 141 | return new CPromise((resolve, reject, {signal}) => { 142 | fetch(url, {...fetchOptions, signal}).then(resolve, reject) 143 | }, {timeout, nativeController: true}) 144 | } 145 | 146 | const promise= fetchWithTimeout('http://localhost/', {timeout: 5000}) 147 | .then(response => response.json()) 148 | .then(data => console.log(`Done: `, data), err => console.log(`Error: `, err)) 149 | 150 | setTimeout(()=> promise.cancel(), 1000); 151 | 152 | // you able to call cancel() at any time to cancel the entire chain at any stage 153 | // the related network request will also be aborted 154 | ```` 155 | 156 | #### Note 157 | 158 | You can use the [cp-fetch](https://www.npmjs.com/package/cp-fetch) which provides a ready to use 159 | CPromise wrapper for cross-platform fetch API, or [cp-axios](https://www.npmjs.com/package/cp-axios) wrapper for `axios` with powers of CPromise. 160 | 161 | ## `.then` method behavior notes 162 | 163 | The behavior of the method is slightly different from native Promise. 164 | In the case when you cancel the chain after it has been resolved within one eventloop tick, 165 | `onRejected` will be called with a `CanceledError` instance, instead of `onFulfilled`. 166 | This prevents the execution of unwanted code in the next eventloop tick if 167 | the user canceled the promise immediately after the promise was resolved, 168 | during the same eventloop tick. 169 | 170 | ## Ecosystem 171 | #### React 172 | * [use-async-effect2](https://www.npmjs.com/package/use-async-effect2) - feature-rich React async hooks that built on top of the cancellable promises ([CPromise](https://www.npmjs.com/package/c-promise2)) 173 | 174 | #### Data fetching 175 | * [cp-axios](https://www.npmjs.com/package/cp-axios) - axios cancellable wrapper that supports CPromise context. Can be directly used in [use-async-effect2](https://www.npmjs.com/package/use-async-effect2) or general CPromise context 176 | * [cp-fetch](https://www.npmjs.com/package/cp-fetch) - cross-platform fetch wrapper that can be used in cooperation with [use-async-effect2](https://www.npmjs.com/package/use-async-effect2) or general [CPromise](https://www.npmjs.com/package/c-promise2) chains 177 | 178 | #### Backend 179 | * [cp-koa](https://www.npmjs.com/package/cp-koa) - a wrapper for [`koa`](https://www.npmjs.com/package/koa) that adds cancellable middlewares/routes to the framework 180 | 181 | ## Learn more 182 | 183 | See [CPromise wiki](https://github.com/DigitalBrainJS/c-promise/wiki) 184 | 185 | ## API Reference 186 | 187 | JSDoc autogenerated [API Reference](https://github.com/DigitalBrainJS/c-promise/blob/master/API.md) 188 | 189 | ## License 190 | 191 | The MIT License Copyright (c) 2020 Dmitriy Mozgovoy robotshara@gmail.com 192 | 193 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 194 | 195 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 196 | 197 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 198 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 199 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 200 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 201 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 202 | -------------------------------------------------------------------------------- /examples/signals.js: -------------------------------------------------------------------------------- 1 | const {CPromise}= require('../lib/c-promise'); 2 | 3 | const chain= new CPromise((resolve, reject, scope)=>{ 4 | scope.on('signal', (type, data) => { 5 | if (type === 'inc') { // ignore other signal types 6 | console.log(`Signal ${type} handled`); 7 | resolve(data.x + 1); 8 | return true; // we accepted this signal, we should return `true` to stop the propagation 9 | } 10 | }); 11 | }).then( 12 | (value)=> console.log(`Done: ${value}`), 13 | (err)=> console.log(`Failed: ${err}`) 14 | ) 15 | 16 | setTimeout(() => { 17 | // returns true 18 | console.log(`Inc signal result: ${chain.emitSignal('inc', {x: 2})}`); 19 | // returns false because there are no handlers to catch this signal type 20 | console.log(`Custom signal result: ${chain.emitSignal('custom')}`); 21 | }); 22 | 23 | -------------------------------------------------------------------------------- /examples/suspend.js: -------------------------------------------------------------------------------- 1 | const {CPromise}= require('../lib/c-promise'); 2 | 3 | function cancelableDelay(ms, value){ 4 | return new CPromise(function(resolve, reject, {onCancel, onPause, onResume}){ 5 | let timestamp= Date.now(); 6 | let timeLeft; 7 | let timer= setTimeout(resolve, ms, value); 8 | onPause(()=>{ 9 | console.log(`Pause`); 10 | clearTimeout(timer); 11 | timer=0; 12 | timeLeft= ms - (Date.now()- timestamp); 13 | timestamp= Date.now(); 14 | }); 15 | 16 | onResume(()=>{ 17 | console.log(`Resume`); 18 | timer= setTimeout(resolve, timeLeft, value); 19 | }); 20 | 21 | onCancel(()=>{ 22 | console.log(`Cancel`); 23 | timer && clearTimeout(timer); 24 | }) 25 | }); 26 | } 27 | 28 | const chain= cancelableDelay(1000, 123) 29 | .then( 30 | value=> console.log(`Done:`, value), 31 | err=> console.warn(`Fail: ${err}`) 32 | ); 33 | 34 | setTimeout(()=>{ 35 | chain.pause(); 36 | 37 | setTimeout(()=>{ 38 | chain.resume(); 39 | }, 5000); 40 | }, 100); 41 | 42 | -------------------------------------------------------------------------------- /jsdoc2md/API.hbs.md: -------------------------------------------------------------------------------- 1 | ## API Reference 2 | 3 | {{#module name="CPromise"}} 4 | {{>body}} 5 | {{>separator~}} 6 | {{>members~}} 7 | {{/module}} 8 | -------------------------------------------------------------------------------- /lib/abort-controller.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module AbortController 3 | */ 4 | 5 | const {globalObject}= require('./utils'); 6 | 7 | const _signal = Symbol('signal'); 8 | const _aborted = Symbol('aborted'); 9 | const _events = Symbol('events'); 10 | const _abort = Symbol('abort'); 11 | const _repeatedly = Symbol('repeatedly'); 12 | 13 | const nativeAbortController= (()=>{ 14 | try { 15 | const {AbortController}= globalObject; 16 | if (typeof AbortController === 'function') { 17 | const controller = new AbortController(); 18 | if (controller.toString() === '[object AbortController]' && 19 | controller.constructor.toString().indexOf('[native code]') !== -1) { 20 | return AbortController; 21 | } 22 | } 23 | } catch (e) { 24 | } 25 | })() 26 | 27 | /** 28 | * AbortSignalPolyfill class 29 | * @alias CPromise.AbortSignalPolyfill 30 | */ 31 | 32 | const AbortSignal= class AbortSignal { 33 | /** 34 | * Constructs a new AbortSignal instance 35 | */ 36 | 37 | constructor(repeatedly) { 38 | this[_events] = {}; 39 | this[_aborted] = false; 40 | this[_repeatedly]= repeatedly; 41 | } 42 | 43 | /** 44 | * Check whether controller is aborted 45 | * @returns {Boolean} 46 | */ 47 | 48 | get aborted() { 49 | return this[_aborted]; 50 | } 51 | 52 | /** 53 | * adds a new listener to the controller 54 | * @param {String|Symbol} event 55 | * @param {Function} listener 56 | */ 57 | 58 | addEventListener(event, listener) { 59 | const events = this[_events]; 60 | if (!events) return; 61 | const listeners = events[event]; 62 | if (!listeners) { 63 | events[event] = listener; 64 | return; 65 | } 66 | typeof listeners === 'function' ? (events[event] = [listeners, listener]) : listeners.push(listener); 67 | } 68 | 69 | /** 70 | * removes the listener 71 | * @param {String|Symbol} event 72 | * @param {Function} listener 73 | */ 74 | 75 | removeEventListener(event, listener) { 76 | const events = this[_events]; 77 | if (!events) return; 78 | const listeners = events[event]; 79 | if (!listeners) { 80 | return; 81 | } 82 | if (typeof listeners === 'function') { 83 | events[event] = null; 84 | return; 85 | } 86 | 87 | const index = listeners.indexOf(listener); 88 | 89 | if (index !== -1) { 90 | listeners.length > 1 ? listeners.splice(index, 1) : (events[event] = null); 91 | } 92 | } 93 | 94 | /** 95 | * dispatch the event 96 | * @param {String|Symbol} type 97 | * @param {String} [reason] 98 | */ 99 | 100 | dispatchEvent(type, reason) { 101 | if (!this[_events]) return; 102 | let listener; 103 | const event = { 104 | type, 105 | target: this, 106 | bubbles: false, 107 | cancelable: false 108 | }; 109 | typeof (listener = this['on' + type]) === 'function' && listener.call(this, event); 110 | const listeners = this[_events][type]; 111 | if (!listeners) return; 112 | if (typeof listeners === 'function') { 113 | listeners.call(this, type, event, reason); 114 | return; 115 | } 116 | const {length} = listeners; 117 | for (let i = 0; i < length; i++) { 118 | listeners[i].call(this, type, event, reason); 119 | } 120 | } 121 | 122 | [_abort](reason) { 123 | if(this[_repeatedly]){ 124 | this.dispatchEvent('abort', reason); 125 | return; 126 | } 127 | if (this[_aborted]) return; 128 | this[_aborted] = true; 129 | this.dispatchEvent('abort', reason); 130 | this[_events] = null; 131 | } 132 | 133 | clear(){ 134 | this[_events]= {}; 135 | } 136 | 137 | get [Symbol.toStringTag]() { 138 | return 'AbortSignal' 139 | } 140 | 141 | get repeatedly(){ 142 | return this[_repeatedly] 143 | } 144 | 145 | toString() { 146 | return '[object AbortSignal]' 147 | } 148 | } 149 | 150 | /** 151 | * AbortController class 152 | */ 153 | 154 | const AbortController= class AbortController { 155 | /** 156 | * Constructs new AbortController instance 157 | */ 158 | constructor(repeatedly) { 159 | this[_signal] = null; 160 | this[_repeatedly]= !!repeatedly; 161 | } 162 | 163 | /** 164 | * returns the signal of the controller 165 | * @returns {AbortSignal} 166 | */ 167 | 168 | get signal() { 169 | return this[_signal] || (this[_signal] = new AbortSignal(this[_repeatedly])); 170 | } 171 | 172 | set signal(v) { 173 | throw Error('signal is read-only property'); 174 | } 175 | 176 | /** 177 | * Abort the controller 178 | * @param {String} [reason] 179 | */ 180 | 181 | abort(reason) { 182 | this.signal[_abort](reason); 183 | } 184 | 185 | get [Symbol.toStringTag]() { 186 | return 'AbortController' 187 | } 188 | 189 | toString() { 190 | return '[object AbortController]' 191 | } 192 | } 193 | 194 | const isAbortSignal = (thing) => { 195 | return thing && 196 | typeof thing === 'object' && 197 | typeof thing.aborted === 'boolean' && 198 | typeof thing.addEventListener === 'function' && 199 | typeof thing.removeEventListener === 'function'; 200 | } 201 | 202 | const isAbortController = (thing) => { 203 | return thing && typeof thing === 'object' && typeof thing.abort === 'function' && isAbortSignal(thing.signal); 204 | }; 205 | 206 | module.exports = { 207 | AbortController: nativeAbortController || AbortController, 208 | AbortControllerEx: AbortController, 209 | AbortSignalEx: AbortSignal, 210 | isAbortSignal, 211 | isAbortController 212 | }; 213 | -------------------------------------------------------------------------------- /lib/canceled-error.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module CanceledError 3 | */ 4 | 5 | const {version, versionNumber, _version, _versionNumber, warnVersionInteraction}= require('./env'); 6 | const {validateOptions, validators} = require('./validator'); 7 | const {getTag} = require('./utils'); 8 | const {string, number, boolean}= validators; 9 | 10 | const {hasOwnProperty}= Object.prototype; 11 | 12 | const _scope = Symbol('scope'); 13 | const _message = Symbol('message'); 14 | const _errors = Symbol('errors'); 15 | const _code = Symbol('code'); 16 | const _priority = Symbol('priority'); 17 | const _forced = Symbol('forced'); 18 | 19 | const _isCanceledError= Symbol.for('CPromise:CanceledError'); 20 | 21 | const {toStringTag} = Symbol; 22 | 23 | const isLikeCanceledError = (thing) => thing[_isCanceledError] || 24 | getTag(thing.constructor) === 'CanceledError' || 25 | thing.constructor.name === 'CanceledError' && 26 | hasOwnProperty.call(thing, 'scope'); 27 | /** 28 | * CanceledError 29 | */ 30 | 31 | const CanceledError= class CanceledError extends Error { 32 | /** 33 | * Constructs a new error instance 34 | * @param {String} [message] 35 | * @param {String} [code] 36 | * @param {Number} [priority= 0] 37 | * @param {Boolean} [forced= false] 38 | */ 39 | constructor(message, code = E_REASON_CANCELED, priority= 0, forced= false) { 40 | super(); 41 | this.name = getTag(this.constructor); 42 | this[_message] = message || 'canceled'; 43 | this[_code] = code || E_REASON_CANCELED; 44 | this[_scope] = null; 45 | 46 | if (priority !== undefined && typeof priority !== "number") { 47 | throw TypeError('priority must be a number'); 48 | } 49 | 50 | this[_forced] = !!forced; 51 | this[_priority] = priority; 52 | } 53 | 54 | /** 55 | * get promise scope where the error was raised 56 | * @returns {CPromise|null} 57 | */ 58 | 59 | get scope() { 60 | return this[_scope]; 61 | } 62 | 63 | get message(){ 64 | return this[_message]; 65 | } 66 | 67 | /** 68 | * get error code 69 | * @returns {string} 70 | */ 71 | 72 | get code() { 73 | return this[_code] || ''; 74 | } 75 | 76 | /** 77 | * Get error priority 78 | * @returns {Number} 79 | */ 80 | 81 | get priority(){ 82 | return this[_priority]; 83 | } 84 | 85 | /** 86 | * Get forced flag of the error 87 | * @returns {*} 88 | */ 89 | get forced(){ 90 | return this[_forced]; 91 | } 92 | 93 | set scope(scope) { 94 | if (!this[_scope]) { 95 | this[_scope] = scope; 96 | return; 97 | } 98 | throw Error('Scope has been already set'); 99 | } 100 | 101 | /** 102 | * converts thing to a CanceledError instance 103 | * @param {String|Error} thing 104 | * @returns {CanceledError} 105 | */ 106 | 107 | static from(thing) { 108 | const type = typeof thing; 109 | 110 | if (type === 'string' || thing == null) { 111 | const registered = this[_errors][thing]; 112 | 113 | if (registered) { 114 | this[_code] = thing; 115 | this[_priority] = registered[_priority]; 116 | this[_forced] = registered[_forced]; 117 | return new CanceledError(registered.message, thing, registered.priority, registered.forced); 118 | } 119 | 120 | return new CanceledError(thing); 121 | } else if (type === 'object') { 122 | if (thing instanceof Error) { 123 | if (thing instanceof CanceledError) { 124 | return thing; 125 | } 126 | 127 | return new CanceledError(thing.message); 128 | } 129 | } 130 | 131 | throw TypeError(`unable convert ${thing} to a CanceledError`); 132 | } 133 | 134 | /** 135 | * Check whether object is an instance of CanceledError 136 | * @param thing 137 | * @param {...string} [codes] codes to match 138 | * @returns {boolean} 139 | */ 140 | 141 | static isCanceledError(thing, ...codes) { 142 | return thing && (thing instanceof this || isLikeCanceledError(thing) && warnVersionInteraction(thing)) && 143 | (!codes.length || codes.some(code => code === thing.code)); 144 | } 145 | 146 | static registerErrors(errors) { 147 | return Object.entries(errors).reduce((acc, [name, entry]) => { 148 | Object.defineProperty(this, name, { 149 | value: name 150 | }); 151 | 152 | if(typeof entry==='object'){ 153 | validateOptions(entry, { 154 | message: string, 155 | priority: number, 156 | forced: boolean 157 | }) 158 | }else{ 159 | entry = { 160 | message: typeof entry === 'symbol' ? String(entry).slice(7, -1) : entry 161 | } 162 | } 163 | 164 | this[_errors][name] = entry 165 | acc[name] = name; 166 | return acc; 167 | }, {}); 168 | } 169 | 170 | /** 171 | * Rethrow the canceled error with optional codes matching 172 | * @param err an error to assert 173 | * @param {...String} [codes] 174 | * @throws CanceledError 175 | */ 176 | 177 | static rethrow(err, ...codes) { 178 | if (!(this.isCanceledError(err))) { 179 | return; 180 | } 181 | const {length} = codes; 182 | if (!length) { 183 | throw err; 184 | } else { 185 | const {code} = err; 186 | for (let i = 0; i < length; i++) { 187 | let targetCode = codes[i]; 188 | if (typeof targetCode !== 'string') { 189 | throw TypeError( 190 | `CanceledError code passed as the ${i + 1}th argument must be a string, got ${targetCode}` 191 | ) 192 | } 193 | if (targetCode === code) { 194 | throw err; 195 | } 196 | } 197 | } 198 | } 199 | 200 | static get [toStringTag](){ 201 | return 'CanceledError'; 202 | } 203 | } 204 | 205 | Object.defineProperties(CanceledError.prototype, { 206 | [_version]: {value : version}, 207 | [_versionNumber]: {value : versionNumber} 208 | }) 209 | 210 | Object.defineProperties(CanceledError.prototype, { 211 | [_isCanceledError]: {value : true} 212 | }) 213 | 214 | CanceledError[_errors] = {}; 215 | 216 | const {E_REASON_CANCELED} = CanceledError.registerErrors({ 217 | E_REASON_CANCELED: 'canceled', 218 | E_REASON_TIMEOUT: 'timeout', 219 | E_REASON_DISPOSED: 'disposed', 220 | E_REASON_UNMOUNTED: { 221 | message: 'component unmounted', 222 | priority: Infinity, 223 | forced: true 224 | } 225 | }); 226 | 227 | module.exports = { 228 | CanceledError 229 | }; 230 | -------------------------------------------------------------------------------- /lib/decorators.js: -------------------------------------------------------------------------------- 1 | const {validateArguments}= require('./validator'); 2 | const {isContextDefined, isPropertyDescriptor, hasOwnProperty}= require('./utils'); 3 | 4 | const isModernDescriptor= (thing)=> thing && typeof thing === 'object' && thing[Symbol.toStringTag] === 'Descriptor'; 5 | 6 | const isAnyPropertyDecoratorDescriptor = function(arg0, arg1, arg2){ 7 | if (arguments.length === 1) { 8 | return isModernDescriptor(arg0); 9 | } 10 | 11 | if (arguments.length === 3) { 12 | return isPropertyDescriptor(arg2); 13 | } 14 | } 15 | 16 | function reducePropertyDecorator(decorator) { 17 | return function (arg0, arg1, arg2) { 18 | if (arguments.length === 1) { 19 | return decorator(arg0); 20 | } else { 21 | const { descriptor, finisher } = decorator.call(null, { 22 | key: arg1, 23 | kind: "method", 24 | placement: typeof arg0 === "function" ? "static" : "prototype", 25 | descriptor: arg2 26 | }); 27 | finisher && finisher(arg0.constructor); 28 | return descriptor; 29 | } 30 | }; 31 | } 32 | 33 | const assertDecoratorArgs= (decoratorName, args, validators)=>{ 34 | validators && validateArguments(args, validators, { 35 | context: `@${decoratorName} decorator` 36 | }) 37 | } 38 | 39 | const fail = (decoratorName, message) =>{ 40 | throw Error(`@${decoratorName}: ${message}`); 41 | } 42 | 43 | const propertyDecorator= (builder, argsTypes, hook)=>{ 44 | return function(name, context) { 45 | 46 | function decorator(...params) { 47 | 48 | assertDecoratorArgs(name, params, argsTypes); 49 | 50 | return reducePropertyDecorator((descriptor) => { 51 | if (descriptor.kind !== 'field' && descriptor.kind !== 'method') { 52 | throw Error(`${name} decorator can be used for method or field`); 53 | } 54 | return builder(descriptor, params, {context, fail: fail.bind(null, name), name}); 55 | }); 56 | } 57 | 58 | return function () { 59 | const hookResult = hook && isContextDefined(this) ? hook.apply(this, arguments) : undefined; 60 | 61 | if (hookResult !== undefined) { 62 | return hookResult; 63 | } 64 | 65 | if (isAnyPropertyDecoratorDescriptor.apply(null, arguments)) { 66 | return decorator().apply(null, arguments); 67 | } 68 | return decorator.apply(null, arguments); 69 | } 70 | } 71 | } 72 | 73 | function reduceClassDecorator(decorator) { 74 | return function (arg0) { 75 | if (arguments.length === 1){ 76 | if(typeof arg0==='function'){ 77 | const { descriptor, finisher }= decorator({ 78 | kind: "class" 79 | }); 80 | finisher && finisher(arg0); 81 | return descriptor; 82 | }else if(isModernDescriptor(arg0)){ 83 | return decorator(arg0); 84 | } 85 | } 86 | 87 | throw new Error('cannot recognize decorator calling context'); 88 | }; 89 | } 90 | 91 | function isClassDecoratorContext(arg0) { 92 | return arguments.length === 1 && typeof arg0 === 'function' || isModernDescriptor(arg0); 93 | } 94 | 95 | const classDecorator = (builder, argsTypes, hook)=>{ 96 | return function(name, context){ 97 | const decorator= function(...params){ 98 | 99 | assertDecoratorArgs(name, params, argsTypes); 100 | 101 | return reduceClassDecorator((descriptor) => { 102 | if (descriptor.kind !== 'class') { 103 | fail(name, 'can only be used for classes'); 104 | } 105 | return builder(descriptor, params, {context, fail: fail.bind(null, name), name}); 106 | }); 107 | } 108 | return function(arg0){ 109 | const hookResult = hook && isContextDefined(this) ? hook.apply(this, arguments) : undefined; 110 | 111 | if (hookResult !== undefined) { 112 | return hookResult; 113 | } 114 | 115 | if(isClassDecoratorContext.apply(null, arguments)){ 116 | return decorator().apply(null, arguments); 117 | } 118 | return decorator.apply(null, arguments); 119 | } 120 | } 121 | } 122 | 123 | const bindDecorators = (context, decorators)=>{ 124 | const descriptors= {}; 125 | Object.entries(decorators).forEach(([name, decorator])=>{ 126 | const symbol = Symbol(`${name}Bound`); 127 | 128 | descriptors[name] = { 129 | get: function () { 130 | return hasOwnProperty.call(this, symbol) ? this[symbol] : (this[symbol] = decorator(name, this)) 131 | }, 132 | configurable: true, 133 | enumerable: true 134 | } 135 | }) 136 | 137 | Object.defineProperties(context, descriptors); 138 | } 139 | 140 | module.exports= { 141 | propertyDecorator, 142 | classDecorator, 143 | bindDecorators 144 | } 145 | -------------------------------------------------------------------------------- /lib/env.js: -------------------------------------------------------------------------------- 1 | const version = require('../package.json').version; 2 | 3 | const _version= Symbol.for('CPromise:version'); 4 | const _versionNumber= Symbol.for('CPromise:version:number'); 5 | 6 | const versionNumber= version 7 | .split('.') 8 | .reverse() 9 | .reduce((computed, part, i)=> computed + part * Math.pow(1000, i), 0); 10 | 11 | let warned= false; 12 | 13 | const warnVersionInteraction = (thing) => { 14 | if (!warned) { 15 | warned = true; 16 | const meta = thing.constructor[_version]; 17 | const versions = `v${version} <=> ${meta ? 'v' + meta.version : ''}`; 18 | console.warn( 19 | `Interaction of multiple versions of CPromise detected (${versions}). 20 | Please update your dependencies to the latest version and avoid using multiple package versions at the same time` 21 | ); 22 | } 23 | return thing; 24 | } 25 | 26 | module.exports= { 27 | version, 28 | _version, 29 | versionNumber, 30 | _versionNumber, 31 | warnVersionInteraction 32 | } 33 | -------------------------------------------------------------------------------- /lib/umd-wrapper.js: -------------------------------------------------------------------------------- 1 | const {CPromise}= require('./c-promise'); 2 | 3 | module.exports= CPromise; 4 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | const {hasOwnProperty}= Object.prototype; 2 | 3 | const isThenable = obj => !!obj && (typeof obj === 'function' || typeof obj === 'object') && typeof obj.then === 'function'; 4 | 5 | const _setImmediate = typeof setImmediate === 'function' ? setImmediate : function setImmediateShim(cb) { 6 | setTimeout(cb, 0) 7 | } 8 | 9 | const {toStringTag} = Symbol; 10 | 11 | const getTag = (thing) => thing && thing[toStringTag] || ''; 12 | 13 | function isGeneratorFunction(thing) { 14 | return typeof thing === 'function' && thing.constructor && getTag(thing) === 'GeneratorFunction'; 15 | } 16 | 17 | const getFnType = (thing) => typeof thing === 'function' && (({ 18 | GeneratorFunction: 'generator', 19 | AsyncFunction: 'async', 20 | })[getTag(thing)] || 'plain'); 21 | 22 | function isGenerator(thing) { 23 | return thing && typeof thing === 'object' && typeof thing.next === 'function'; 24 | } 25 | 26 | function EmptyObject() { 27 | } 28 | 29 | EmptyObject.prototype = null; 30 | 31 | const toGenerator= function(thing, args, context= null){ 32 | if(isGeneratorFunction(thing)){ 33 | return thing.apply(context, args); 34 | } 35 | return thing && (isGenerator(thing)? thing : (thing[Symbol.iterator] && thing[Symbol.iterator]())) || null; 36 | } 37 | 38 | const toArray= (thing, mapper)=>{ 39 | 40 | if (thing) { 41 | if (Array.isArray(thing)) { 42 | return mapper ? thing.map(mapper) : thing; 43 | } 44 | 45 | if ((thing= toGenerator(thing))) { 46 | const arr = []; 47 | let item; 48 | while ((item = thing.next()) && item.done === false) { 49 | arr.push(mapper ? mapper(item.value) : item.value); 50 | } 51 | return arr; 52 | } 53 | } 54 | 55 | return null; 56 | } 57 | 58 | const isPropertyDescriptor = ((allowed)=>{ 59 | return (thing)=>{ 60 | if(thing!== null && typeof thing==='object'){ 61 | const props= Object.getOwnPropertyNames(thing); 62 | return props.length && props.every((prop)=> allowed[prop]); 63 | } 64 | return false; 65 | } 66 | })({ 67 | value: true, 68 | set: true, 69 | get: true, 70 | writable: true, 71 | enumerable: true, 72 | configurable: true 73 | }); 74 | 75 | const lazyBindMethods = (obj, props) => { 76 | props.forEach(prop => { 77 | const symbol = Symbol(`${prop}Bound`); 78 | const {value: fn} = Object.getOwnPropertyDescriptor(obj, prop); 79 | 80 | Object.defineProperty(obj, prop, { 81 | get: function () { 82 | return hasOwnProperty.call(this, symbol)? this[symbol] : (this[symbol] = fn.bind(this)); 83 | }, 84 | 85 | set: function (v) { 86 | throw Error(`Can not rewrite method ${prop} with ${v}`); 87 | }, 88 | 89 | enumerable: true, 90 | configurable: true 91 | }) 92 | }) 93 | } 94 | 95 | const globalObject= (()=>{ 96 | if (typeof globalThis !== 'undefined') { return globalThis; } 97 | if (typeof self !== 'undefined') { return self; } 98 | if (typeof window !== 'undefined') { return window; } 99 | if (typeof global !== 'undefined') { return global; } 100 | })(); 101 | 102 | const isContextDefined = (context) => context != null && context !== globalObject; 103 | 104 | const throttle = (fn, ms) => { 105 | let timer = null; 106 | let lastArgs = null; 107 | let last = null; 108 | 109 | const cancel = ()=>{ 110 | if (!timer) return; 111 | clearTimeout(timer); 112 | timer = null; 113 | lastArgs = null; 114 | } 115 | 116 | const wrapper = function (args, force) { 117 | const now = Date.now(); 118 | const left = ms - (now - last); 119 | 120 | if (force || left <= 0) { 121 | fn.apply(this, args); 122 | last = now; 123 | cancel(); 124 | return true; 125 | } 126 | 127 | lastArgs = args; 128 | 129 | timer = setTimeout(()=>{ 130 | fn.apply(this, args); 131 | last = now; 132 | lastArgs = null; 133 | }, left); 134 | 135 | return false; 136 | }; 137 | 138 | wrapper.cancel = cancel; 139 | 140 | return wrapper; 141 | } 142 | 143 | module.exports={ 144 | isPropertyDescriptor, 145 | isThenable, 146 | setImmediate: _setImmediate, 147 | isGenerator, 148 | isGeneratorFunction, 149 | EmptyObject, 150 | toGenerator, 151 | toArray, 152 | getFnType, 153 | lazyBindMethods, 154 | globalObject, 155 | isContextDefined, 156 | hasOwnProperty, 157 | getTag, 158 | throttle 159 | }; 160 | -------------------------------------------------------------------------------- /lib/validator.js: -------------------------------------------------------------------------------- 1 | const {isAbortSignal, isAbortController} = require('./abort-controller') 2 | 3 | const typify = (validator, type, kind) => { 4 | validator.type = type; 5 | kind && (validator.kind = kind); 6 | return validator; 7 | } 8 | 9 | const findValidatorName = (validator) => { 10 | const fnName = Object.keys(validators).find(key => validators[key] === validator) || validator.name; 11 | return fnName ? fnName.replace(/[A-Z]/g, char => '.' + char.toLowerCase()) : ''; 12 | } 13 | 14 | const renderValidator = (validator) => validator && (validator.type || findValidatorName(validator)) || ''; 15 | 16 | const compose = (...validators) => { 17 | return typify((thing) => { 18 | const {length} = validators; 19 | for (let i = 0; i < length; i++) { 20 | const result = validators[i](thing); 21 | if (result !== true) { 22 | return result; 23 | } 24 | } 25 | return true; 26 | }, validators.map(renderValidator).join('.')); 27 | } 28 | 29 | const renderValue = (value) => typeof value == 'string' ? `'${value}'` : String(value); 30 | 31 | const validators = { 32 | null: (thing) => thing === null, 33 | nullable: typify((thing) => thing == null, 'undefined|null'), 34 | numberFinitePositive: (thing) => typeof thing === 'number' && Number.isFinite(thing) && thing >= 0, 35 | functionPlain: (thing) => typeof thing === 'function' && thing.constructor === Function, 36 | array: (thing) => Array.isArray(thing), 37 | union: (...validators) => { 38 | const len = validators.length; 39 | const shouldBe = validators.map(renderValidator).join('|'); 40 | 41 | return typify((thing) => { 42 | for (let i = 0; i < len; i++) { 43 | if (validators[i](thing) === true) { 44 | return true; 45 | } 46 | } 47 | }, shouldBe) 48 | }, 49 | abortController: typify(isAbortSignal, 'AbortController'), 50 | abortSignal: typify(isAbortController, 'AbortSignal'), 51 | values: (...values) => typify((thing) => values.indexOf(thing) !== -1, values.map(renderValue).join('|')) 52 | }; 53 | 54 | ['object', 'boolean', 'number', 'function', 'string', 'symbol', 'undefined'].forEach((type, i) => { 55 | validators[type] = typify(thing => typeof thing === type || `a${i < 1 ? 'n' : ''} ${type}`, type); 56 | }) 57 | 58 | const {array, object, union} = validators; 59 | 60 | validators.object = (schema, required, allowUnknown = false) => { 61 | const rendered = schema && Object.entries(schema).map(([prop, validator]) => { 62 | return `${prop}: ${renderValidator(validator)}`; 63 | }).join(', '); 64 | 65 | return schema ? compose(object, typify((thing) => { 66 | validateOptions(thing, schema, required, allowUnknown); 67 | return true; 68 | }, schema ? `<${rendered}>` : '')) : object; 69 | } 70 | 71 | 72 | validators.array = (...validators) => { 73 | const validator = validators.length > 1 ? union(...validators) : validators[0]; 74 | 75 | return compose(array, typify((thing) => thing.every(validator), `<${renderValidator(validator)||'*'}>`)) 76 | } 77 | 78 | validators.rest = (...validators) => { 79 | const validator = validators.length > 1 ? union(...validators) : validators[0]; 80 | 81 | return typify(validator, `...${renderValidator(validator)||'*'}`, 'rest') 82 | } 83 | 84 | 85 | function validateOptions(options, schema, required = null, allowUnknown = false) { 86 | if (typeof options !== 'object') { 87 | throw TypeError('options must be an object'); 88 | } 89 | 90 | required && required.forEach(option => { 91 | if (options[option] === undefined) { 92 | throw Error(`Option ${option} is required`); 93 | } 94 | }) 95 | 96 | const keys = Object.getOwnPropertyNames(options); 97 | let i = keys.length; 98 | while (i-- > 0) { 99 | const option = keys[i]; 100 | const validator = schema[option]; 101 | if (validator) { 102 | const value = options[option]; 103 | const result = value === undefined || validator(value); 104 | if (result !== true) { 105 | throw TypeError( 106 | `option '${option}' must be ${typeof result === 'string' ? result : renderValidator(validator)}` 107 | ) 108 | } 109 | continue; 110 | } 111 | if (!allowUnknown) { 112 | throw Error(`Unknown option '${option}'`); 113 | } 114 | } 115 | 116 | return options; 117 | } 118 | 119 | function validateArguments(args, validators, {context = "function"} = {}) { 120 | const {length} = validators; 121 | const argsCount = args.length; 122 | let restDetected; 123 | let validator; 124 | for (let i = 0; i < length; i++) { 125 | if (!restDetected) { 126 | validator = validators[i]; 127 | if (validator.kind === 'rest') { 128 | restDetected = true; 129 | } 130 | } 131 | 132 | let result = validator(args[i]); 133 | if (result !== true) { 134 | const renderedType = result || renderValidator(validator); 135 | throw TypeError(`${context} ${i >= argsCount ? "requires" : "expects"} ${renderedType} as ${i + 1}th argument`); 136 | } 137 | } 138 | } 139 | 140 | module.exports = { 141 | validators, 142 | validateOptions, 143 | validateArguments 144 | } 145 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "c-promise2", 3 | "version": "0.13.12", 4 | "description": "Cancelable promise with progress capturing, pause, timeouts, signals, data flows and decorators support", 5 | "author": { 6 | "name": "Dmitriy Mozgovoy", 7 | "email": "robotshara@gmail.com", 8 | "url": "http://github.com/DigitalBrainJS/" 9 | }, 10 | "main": "./dist/c-promise.cjs.js", 11 | "module": "./dist/c-promise.mjs", 12 | "es2015": "./dist/c-promise.mjs", 13 | "browser": "./dist/c-promise.umd.js", 14 | "unpkg": { 15 | "default": "./dist/c-promise.umd.js", 16 | "module": "./dist/c-promise.js", 17 | "production": "./dist/c-promise.umd.min.js" 18 | }, 19 | "scripts": { 20 | "test": "npm run test:build && mocha ./test/runner.js --exit --timeout=3000", 21 | "test:watch": "nodemon --watch lib/ --watch ./test/tests/CPromise.js --watch ./test/src --exec \"npm test\"", 22 | "test:coverage": "nyc --check-coverage npm run test", 23 | "test:decorators:build": "rollup ./test/src/decorators.js --file ./test/tests/decorators.js --format cjs --config ./test/rollup.config.js", 24 | "test:decorators:legacy:build": "rollup ./test/src/decorators.js --file ./test/tests/decorators.legacy.js --format cjs --config ./test/rollup.config.js --environment decorators:legacy", 25 | "test:build": "npm run test:decorators:build && npm run test:decorators:legacy:build", 26 | "coverage:report": "nyc report --reporter=html --reporter=text", 27 | "coveralls": "nyc report --reporter=text-lcov | coveralls", 28 | "prepublishOnly": "npm run build && npm run test:coverage && npm run docs", 29 | "postversion": "git push && git push --tags", 30 | "changelog": "auto-changelog -p", 31 | "version": "npm run changelog && git add CHANGELOG.md", 32 | "build": "rollup -c", 33 | "build:watch": "nodemon --watch lib/ --exec \"npm run build\"", 34 | "dev": "cross-env NODE_ENV=development \"npm run test:watch\"", 35 | "playground": "npm run playground:build && concurrently --kill-others \"npm run playground:server\" \"npm run playground:watch\"", 36 | "playground:run": "node playground/build/index.js || true", 37 | "playground:build": "rollup ./playground/src/index.js --file ./playground/build/index.js --format cjs --config ./playground/rollup.config.js", 38 | "playground:watch": "nodemon --delay 1000ms -e js,jsx,css --watch ./playground/src/ --watch lib/ --exec \"npm run playground:build\"", 39 | "playground:server": "browser-sync start --server --index ./playground/index.html --files \"./playground/build/*.*\"", 40 | "playground:generator": "node playground/generator.js || true", 41 | "playground:generator:watch": "nodemon --watch ./playground --watch lib/ --exec \"npm run playground:generator\"", 42 | "playground:decorators": "rollup ./playground/src/decorators.js --file ./playground/build/decorators.js --format cjs --config ./playground/rollup.config.js && node playground/build/decorators.js || true", 43 | "playground:decorators:legacy": "rollup ./playground/src/decorators.js --file ./playground/build/decorators.js --format cjs --config ./playground/rollup.config.js --environment decorators:legacy && node playground/build/decorators.js || true", 44 | "playground:decorators:watch": "nodemon --watch ./playground/src/ --watch lib/ --exec \"npm run playground:decorators\"", 45 | "docs": "jsdoc2md -t ./jsdoc2md/API.hbs.md ./lib/c-promise.js > ./API.md", 46 | "docs:namepaths": "jsdoc2md ./lib/c-promise.js --namepaths" 47 | }, 48 | "husky": { 49 | "hooks": { 50 | "pre-commit": "npm run docs && git add ." 51 | } 52 | }, 53 | "auto-changelog": { 54 | "output": "CHANGELOG.md", 55 | "unreleased": true, 56 | "commitLimit": false 57 | }, 58 | "repository": "https://github.com/DigitalBrainJS/c-promise.git", 59 | "bugs": { 60 | "url": "https://github.com/DigitalBrainJS/c-promise/issues" 61 | }, 62 | "nyc": { 63 | "lines": 60, 64 | "functions": 60, 65 | "branches": 60, 66 | "statements": 60, 67 | "watermarks": { 68 | "lines": [ 69 | 80, 70 | 95 71 | ], 72 | "functions": [ 73 | 80, 74 | 95 75 | ], 76 | "branches": [ 77 | 80, 78 | 95 79 | ], 80 | "statements": [ 81 | 80, 82 | 95 83 | ] 84 | }, 85 | "reporter": [ 86 | "lcov", 87 | "text-summary" 88 | ] 89 | }, 90 | "keywords": [ 91 | "promise", 92 | "cancelable", 93 | "cancellable", 94 | "p-cancelable", 95 | "timeout", 96 | "progress", 97 | "cancel", 98 | "abortable", 99 | "abort", 100 | "AbortController", 101 | "AbortSignal", 102 | "async", 103 | "signal", 104 | "await", 105 | "promises", 106 | "generator", 107 | "co", 108 | "yield", 109 | "reject", 110 | "race", 111 | "decorator", 112 | "delay", 113 | "break", 114 | "suspending", 115 | "wait", 116 | "bluebird", 117 | "deferred", 118 | "react", 119 | "setState", 120 | "cancellation", 121 | "aborting", 122 | "close", 123 | "closable", 124 | "pause", 125 | "task" 126 | ], 127 | "license": "MIT", 128 | "devDependencies": { 129 | "@babel/plugin-proposal-class-properties": "^7.13.0", 130 | "@babel/plugin-proposal-decorators": "^7.13.15", 131 | "@babel/preset-react": "^7.13.13", 132 | "@rollup/plugin-babel": "^5.3.0", 133 | "@rollup/plugin-commonjs": "^15.1.0", 134 | "@rollup/plugin-json": "^4.1.0", 135 | "@rollup/plugin-multi-entry": "^4.0.0", 136 | "@rollup/plugin-node-resolve": "^9.0.0", 137 | "@rollup/plugin-replace": "^2.4.2", 138 | "auto-changelog": "^2.2.1", 139 | "bootstrap": "^4.6.0", 140 | "concurrently": "^6.0.2", 141 | "coveralls": "^3.1.0", 142 | "cp-axios": "^0.2.0", 143 | "cross-env": "^7.0.3", 144 | "husky": "^4.3.8", 145 | "install": "^0.13.0", 146 | "jsdoc-to-markdown": "^6.0.1", 147 | "mocha": "^8.3.2", 148 | "nodemon": "^2.0.7", 149 | "npm": "^6.14.13", 150 | "nyc": "^15.1.0", 151 | "postcss": "^8.2.12", 152 | "react": "^17.0.2", 153 | "react-bootstrap": "^1.5.2", 154 | "react-dom": "^17.0.2", 155 | "react-is": "^17.0.2", 156 | "rollup": "^2.45.2", 157 | "rollup-plugin-postcss": "^4.0.0", 158 | "rollup-plugin-terser": "^7.0.2" 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /playground/basic.js: -------------------------------------------------------------------------------- 1 | const {CPromise}= require('../lib/c-promise'); 2 | 3 | const timestamp= Date.now(); 4 | 5 | function log(message, ...values){ 6 | console.log(`[${Date.now()- timestamp}ms] ${message}`, ...values); 7 | } 8 | 9 | const delay= (ms, value)=>{ 10 | return new CPromise((resolve, reject, {onCancel}) => { 11 | const timer = setTimeout(resolve, ms, value); 12 | 13 | onCancel(() => { 14 | log(`clearTimeout`); 15 | clearTimeout(timer); 16 | }) 17 | }) 18 | } 19 | 20 | const chain = delay(1000, 1).label('first chain') 21 | .then((value)=> delay(1000, value + 1)).label('second chain') 22 | .then((value)=> delay(1000, value + 1)).label('third chain') 23 | .then((value)=> delay(1000, value + 1).label('inner chain')).label('fourth chain') 24 | .then((value)=> delay(1000, value + 1)).label('fifth chain') 25 | .progress((value, scope)=> log(`Pending progress ${value} (${scope.label()})`)); 26 | 27 | const echoChainState= ()=>console.log(`Is pending: ${chain.isPending}\nIs canceled: ${chain.isCanceled}`); 28 | 29 | echoChainState(); 30 | 31 | chain 32 | .then((value) => { 33 | log(`Done with value '${value}'`); // [1006ms] CanceledError: canceled 34 | }).label('final') 35 | .catch((err)=>{ 36 | log(`cancelled with error : ${err} on '${err.scope.label()}'`); // [1006ms] CanceledError: canceled 37 | }, CPromise.CanceledError) 38 | .catch(err=>{ 39 | log(`Some other error occurred: ${err}`); 40 | }) 41 | .finally(() => { 42 | echoChainState(); 43 | }); 44 | 45 | 46 | setTimeout(()=> chain.cancel(), 3500); // Close the chain after 1000ms 47 | -------------------------------------------------------------------------------- /playground/fetch-timeout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fetch timeout 6 | 7 | 8 | 9 |

Make request to endpoint with 10s+ latency

10 |

Open your console to see the log

11 |

Note that the related request does abort when you undo the promise chain (see the network tab in developer tools)

12 | 13 | 14 | 15 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /playground/fetch-timeout2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fetch timeout 6 | 7 | 8 | 9 |

Make request to endpoint with 10s+ latency

10 |

Open your console to see the log

11 |

Note that the related request does abort when you undo the promise chain (see the network tab in developer tools)

12 | 13 | 14 | 15 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /playground/fetch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fetch cancellation 6 | 7 | 8 | 46 | 47 | 48 |
49 |
50 |
51 |
52 | 53 | 54 |
55 |
56 | 57 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /playground/generator.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Generator 6 | 7 | 8 | 9 |

See your console

10 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /playground/generator.js: -------------------------------------------------------------------------------- 1 | const {CPromise}= require('../lib/c-promise'); 2 | 3 | const timestamp= Date.now(); 4 | 5 | function log(message, ...values){ 6 | console.log(`[${Date.now()- timestamp}ms] ${message}`, ...values); 7 | } 8 | 9 | function fetchWithTimeout(url, options= {}) { 10 | const {timeout, ...fetchOptions}= options; 11 | return new CPromise((resolve, reject, {signal}) => { 12 | fetch(url, {...fetchOptions, signal}).then(resolve, reject) 13 | }, timeout) 14 | } 15 | 16 | const chain= CPromise.from(function*(url){ 17 | const response= yield fetchWithTimeout(url); // fetch movie info 18 | const json= response.json(); 19 | console.log(`Json: `, json); 20 | let i= 10; 21 | while (--i > 0) { 22 | yield i * 100; //wait (i * 100)ms 23 | console.log(`Iteration ${i}`); 24 | } 25 | return json.name; 26 | }, ["https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s"]) 27 | .progress(value=> log(`Progress: ${value}`)) 28 | .then(value=> log(`Done: ${value}`), err=> log(`Error: ${err}`)); 29 | 30 | //chain.cancel();// 31 | -------------------------------------------------------------------------------- /playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Playground 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /playground/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import babel from '@rollup/plugin-babel'; 4 | import replace from '@rollup/plugin-replace'; 5 | import postcss from 'rollup-plugin-postcss'; 6 | import json from '@rollup/plugin-json'; 7 | 8 | const legacy = process.env.decorators === 'legacy'; 9 | 10 | export default [ 11 | { 12 | plugins: [ 13 | babel({ 14 | babelHelpers: 'bundled' , 15 | plugins: [ 16 | legacy ? ["@babel/plugin-proposal-decorators", {legacy}] : 17 | ["@babel/plugin-proposal-decorators", {legacy, decoratorsBeforeExport: true}], 18 | ["@babel/plugin-proposal-class-properties", { "loose" : true }] 19 | ], 20 | presets: [ 21 | "@babel/preset-react" 22 | ] 23 | }), 24 | json(), 25 | postcss({ 26 | plugins: [] 27 | }), 28 | replace({ 29 | 'process.env.NODE_ENV': JSON.stringify( 'production' ) 30 | }), 31 | resolve({ 32 | browser: true 33 | }), 34 | commonjs({ 35 | extensions: ['.js', '.jsx'] 36 | }) 37 | ] 38 | }, 39 | ]; 40 | -------------------------------------------------------------------------------- /playground/simple.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalBrainJS/c-promise/f316574870313bdc27a55391c7df44dd3495dfa0/playground/simple.js -------------------------------------------------------------------------------- /playground/src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import TestComponent from "./TestComponent"; 3 | import "bootstrap/dist/css/bootstrap.min.css"; 4 | import "./styles.css"; 5 | 6 | export default class App extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { url: props.url, _url: props.url, timeout: 5000 }; 10 | this.onClick = this.onClick.bind(this); 11 | this.onFetchClick = this.onFetchClick.bind(this); 12 | this.handleChange = this.handleChange.bind(this); 13 | this.handleTimeoutChange = this.handleTimeoutChange.bind(this); 14 | } 15 | 16 | onFetchClick() { 17 | this.setState(({ _url }) => ({ url: _url })); 18 | } 19 | 20 | onClick() { 21 | this.setState({ timestamp: Date.now() }); 22 | } 23 | 24 | handleChange(event) { 25 | console.log(event.target.value); 26 | this.setState({ _url: event.target.value }); 27 | } 28 | 29 | handleTimeoutChange(event) { 30 | this.setState({ timeout: event.target.value * 1 }); 31 | } 32 | 33 | render() { 34 | return ( 35 |
36 | 42 | 49 |
50 | 59 | 62 |
63 | 67 | 70 |
71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /playground/src/TestComponent.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | CPromise, 4 | CanceledError, 5 | ReactComponent, 6 | E_REASON_UNMOUNTED, 7 | listen, 8 | async, 9 | cancel 10 | } from "c-promise2"; 11 | import { Spinner } from "react-bootstrap"; 12 | import cpAxios from "cp-axios"; 13 | 14 | @ReactComponent 15 | class TestComponent extends Component { 16 | state = { 17 | text: "idle", 18 | loading: false, 19 | loader: false 20 | }; 21 | 22 | *componentDidMount() { 23 | console.log("mounted"); 24 | yield this.fetch(); 25 | } 26 | 27 | * fetch() { 28 | this.setState({text: "fetching...", loading: true}); 29 | yield CPromise.race([ 30 | this.showLoader(1000, 'loader'), 31 | CPromise.run(function* () { 32 | try { 33 | const response = yield cpAxios(this.props.url).timeout( 34 | this.props.timeout 35 | ); 36 | this.setState({ 37 | text: JSON.stringify(response.data, null, 2), 38 | loading: false 39 | }); 40 | } catch (err) { 41 | CanceledError.rethrow(err, E_REASON_UNMOUNTED); 42 | this.setState({text: err.toString(), loading: false}); 43 | } 44 | }, {context: this}) 45 | ]) 46 | } 47 | 48 | @async({ scopeArg: true }) 49 | *showLoader(scope, delay= 1000, stateVar) { 50 | yield CPromise.delay(delay); 51 | this.setState({ [stateVar]: true }); 52 | scope.onDone(()=>{ 53 | this.setState({ [stateVar]: false }); 54 | }) 55 | scope.pause(); 56 | } 57 | 58 | render() { 59 | return ( 60 |
61 |
CPromise decorators demo:
62 |
63 | {this.state.loader ? ( 64 | 65 | ) : null} 66 | {this.state.text} 67 |
68 | 71 | 77 |
78 | ); 79 | } 80 | } 81 | 82 | console.log("componentDidMount", TestComponent.prototype.componentDidMount); 83 | 84 | export default TestComponent; 85 | -------------------------------------------------------------------------------- /playground/src/decorators.js: -------------------------------------------------------------------------------- 1 | /*const { 2 | CPromise, 3 | async, 4 | listen, 5 | cancel, 6 | canceled, 7 | timeout, 8 | progress, 9 | innerWeight, 10 | E_REASON_UNMOUNTED, 11 | ReactComponent 12 | }= require('../../lib/c-promise');*/ 13 | 14 | const {CPromise, ReactComponent, async, listen, cancel, E_REASON_CANCELED, timeout, atomic}= require('../../lib/c-promise'); 15 | /* 16 | arguments = Arguments(1) [Descriptor, Accessor, Function] 17 | 0 = Descriptor {kind: "class", 18 | elements: Array(0), 19 | Symbol(Symbol.toStringTag): "Descriptor"} 20 | kind = "class" 21 | elements = Array(0) [] 22 | Symbol(Symbol.toStringTag) = "Descriptor" 23 | __proto__ = Object 24 | length = 1 25 | */ 26 | 27 | 28 | 29 | /*@ReactComponent(123,456) 30 | //@decorator 31 | class TestDecorator{ 32 | constructor() { 33 | this.x= 1; 34 | } 35 | }*/ 36 | 37 | //@ReactComponent 38 | class Test { 39 | constructor() { 40 | this.value= 3000; 41 | } 42 | /* @progress((value)=> console.log(`Progress: ${value}`)) 43 | @innerWeight(2)*/ 44 | @timeout(function(){ 45 | console.log(this); 46 | return this.value; 47 | }) 48 | @listen('test') 49 | @atomic("detached") 50 | *asyncTask(delay=2000) { 51 | debugger; 52 | const result1= yield CPromise.delay(delay, 123); 53 | const result2= yield new CPromise((resolve, reject, {onCancel})=>{ 54 | const timer= setTimeout(resolve, 2000); 55 | onCancel((reason)=>{ 56 | console.log(`Cancel inner promise due ${reason}`); 57 | clearTimeout(timer); 58 | }) 59 | }) 60 | return result1 + 1; 61 | } 62 | // @async 63 | /* @cancel('test') 64 | 65 | asyncTask(delay, cb){ 66 | console.log('delay' , delay); 67 | setTimeout(cb, 3000, new Error('oops')); 68 | }*/ 69 | 70 | @cancel(null, 'test2') 71 | async cancel(){ 72 | return CPromise.delay(1000, 123); 73 | } 74 | } 75 | 76 | const test= new Test(); 77 | 78 | test.asyncTask() 79 | .then( 80 | value => console.log(`Done: ${value}`), 81 | err => console.warn(`Fail: ${err}`) 82 | ); 83 | 84 | setTimeout(()=>{ 85 | //cancel.call(test, 'oops', 'test'); 86 | }, 1100); 87 | 88 | 89 | -------------------------------------------------------------------------------- /playground/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | 5 | ReactDOM.render( 6 | , 7 | document.getElementById('root') 8 | ); 9 | -------------------------------------------------------------------------------- /playground/src/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 10px; 3 | } 4 | 5 | .App { 6 | font-family: sans-serif; 7 | text-align: center; 8 | } 9 | 10 | .field-url { 11 | width: 450px; 12 | height: 40px; 13 | font-size: 12px; 14 | } 15 | 16 | .btn-change { 17 | margin: 5px; 18 | } 19 | 20 | .component { 21 | margin: 20px 0px; 22 | border: 1px solid black; 23 | width: 600px; 24 | padding: 5px; 25 | } 26 | 27 | .component * { 28 | margin: 5px; 29 | } 30 | 31 | form { 32 | display: inline; 33 | } 34 | 35 | .component > .caption { 36 | background-color: #535353; 37 | color: white; 38 | margin: 0px; 39 | padding: 5px 10px; 40 | } 41 | 42 | #timeout-input { 43 | width: 200px; 44 | } 45 | -------------------------------------------------------------------------------- /public/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalBrainJS/c-promise/f316574870313bdc27a55391c7df44dd3495dfa0/public/demo.gif -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import { terser } from "rollup-plugin-terser"; 4 | import json from '@rollup/plugin-json'; 5 | import replace from '@rollup/plugin-replace'; 6 | 7 | const lib = require("./package.json"); 8 | const outputFileName= 'c-promise'; 9 | const name= "CPromise"; 10 | 11 | const input = './lib/c-promise.js'; 12 | 13 | const year= new Date().getFullYear(); 14 | const banner= `// ${lib.name} v${lib.version}\n// Copyright (c) ${year===2020? "2020" : "2020-"+ year} ${lib.author.name} <${lib.author.email}>`; 15 | 16 | const replaceEnvVars= replace({ 17 | "require('../package.json').version": `'${lib.version}'`, 18 | }) 19 | 20 | export default [ 21 | { 22 | input, 23 | output: { 24 | file: `dist/${outputFileName}.cjs.js`, 25 | format: 'cjs', 26 | name, 27 | exports: "named", 28 | banner 29 | }, 30 | plugins: [ 31 | replaceEnvVars, 32 | json(), 33 | resolve(), 34 | commonjs() 35 | ] 36 | }, 37 | { 38 | input: './lib/umd-wrapper.js', 39 | output: { 40 | file: `dist/${outputFileName}.umd.js`, 41 | format: 'umd', 42 | name, 43 | exports: "default", 44 | banner 45 | }, 46 | plugins: [ 47 | replaceEnvVars, 48 | json(), 49 | resolve(), 50 | commonjs() 51 | ] 52 | }, 53 | { 54 | input: './lib/umd-wrapper.js', 55 | output: { 56 | file: `dist/${outputFileName}.umd.min.js`, 57 | format: 'umd', 58 | name, 59 | exports: "default", 60 | banner 61 | }, 62 | plugins: [ 63 | replaceEnvVars, 64 | json(), 65 | resolve(), 66 | commonjs(), 67 | terser() 68 | ] 69 | }, 70 | { 71 | input, 72 | output: { 73 | file: `dist/${outputFileName}.mjs`, 74 | format: 'esm', 75 | preferConst: true, 76 | exports: "named", 77 | banner 78 | }, 79 | plugins: [ 80 | replaceEnvVars, 81 | json(), 82 | resolve(), 83 | commonjs() 84 | ] 85 | } 86 | ]; 87 | -------------------------------------------------------------------------------- /test/bin/headtest.js: -------------------------------------------------------------------------------- 1 | const CPromise = require('../../lib/c-promise'); 2 | 3 | const count= 50000; 4 | const heapCycles = []; 5 | const pushStat = () => heapCycles.push((process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2) * 1); 6 | 7 | const generatorFn = function* () { 8 | this.captureProgress(count) 9 | let i = count; 10 | while (--i > 0) { 11 | yield 0; 12 | } 13 | } 14 | 15 | global.gc(); 16 | 17 | function analyze() { 18 | const calcAvg = (start, end) => { 19 | const data = heapCycles.slice(start, end); 20 | return data.reduce((prev, value) => prev + value, 0) / data.length; 21 | } 22 | const {length} = heapCycles; 23 | const avg = calcAvg(length * 0.2); 24 | const trend= calcAvg(-length * 0.2) - avg; 25 | let trendValue= (trend*100).toFixed(2); 26 | if (trendValue) { 27 | trendValue = '+' + trendValue; 28 | } 29 | 30 | console.log( 31 | `Chains created & disposed: ${count}\n` + 32 | `Average memory consummation: ${avg.toFixed(1)} MB\n` + 33 | `Min: ${Math.min.apply(null, heapCycles)} MB\n` + 34 | `Max: ${Math.max.apply(null, heapCycles)} MB\n` 35 | ); 36 | if (trend > 0.01) { 37 | console.warn(`Memory leakage detected: ${trendValue}%`); 38 | process.exit(1); 39 | } 40 | 41 | console.log(`Memory leakage not detected: ${trendValue}% ${trend}`); 42 | } 43 | 44 | console.log(`Resolve generator with ${count} chains`); 45 | const chain = CPromise.from(generatorFn).throttleProgress(1500).progress(value => { 46 | global.gc(); 47 | pushStat(); 48 | console.warn(`Progress [${value}] [${heapCycles}]`); 49 | }).then(() => { 50 | analyze(); 51 | }); 52 | -------------------------------------------------------------------------------- /test/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import babel from '@rollup/plugin-babel'; 4 | 5 | const legacy = process.env.decorators === 'legacy'; 6 | 7 | export default [ 8 | { 9 | plugins: [ 10 | babel({ 11 | babelHelpers: 'bundled' , 12 | plugins: [ 13 | legacy ? ["@babel/plugin-proposal-decorators", {legacy}] : 14 | ["@babel/plugin-proposal-decorators", {legacy, decoratorsBeforeExport: true}], 15 | ["@babel/plugin-proposal-class-properties", { "loose" : true }] 16 | ] 17 | }), 18 | resolve(), 19 | commonjs() 20 | ] 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /test/runner.js: -------------------------------------------------------------------------------- 1 | const fs= require('fs'); 2 | const path= require('path'); 3 | const assert= require('assert'); 4 | 5 | function importTests(root, filter) { 6 | const tests = {}; 7 | 8 | function scanDir(currentDir, testEntry) { 9 | fs.readdirSync(currentDir).forEach(function (entry) { 10 | entry = path.resolve(currentDir, entry); 11 | const stats = fs.statSync(entry); 12 | const {name, ext} = path.parse(entry); 13 | const relativePath = path.relative(root, entry); 14 | if (filter && !filter(relativePath)) return; 15 | if (stats.isDirectory()) { 16 | return scanDir(entry, testEntry[name] || (testEntry[name] = {})); 17 | } 18 | ext === '.js' && (testEntry[name] = require(entry)); 19 | }); 20 | } 21 | 22 | scanDir(root, tests); 23 | 24 | function _import(tests) { 25 | Object.keys(tests).forEach(function (testName) { 26 | const test = tests[testName]; 27 | const type = typeof test; 28 | if (type === 'object') { 29 | if (!type) { 30 | return; 31 | } 32 | describe(testName, function () { 33 | _import(test); 34 | }) 35 | } else if (type === 'function') { 36 | const executor = function (done) { 37 | if (!done) { 38 | return test.call(this); 39 | } 40 | let count, expectedCount; 41 | 42 | function wrap(fn) { 43 | return function () { 44 | count++; 45 | return fn.apply(null, arguments); 46 | } 47 | } 48 | 49 | const testObj = Object.assign(function () { 50 | return done.apply(this, arguments); 51 | }, { 52 | done: function (err) { 53 | if (expectedCount !== undefined && count < expectedCount) { 54 | throw Error(`${count}/${expectedCount} assertions was done`); 55 | } 56 | err ? done(err) : done(); 57 | }, 58 | expect: function (count) { 59 | expectedCount = count; 60 | } 61 | }); 62 | Object.keys(assert).forEach(function(prop){ 63 | const value= assert[prop]; 64 | typeof value==='function' && (testObj[prop]= wrap(value)); 65 | }); 66 | return test.call(this, testObj); 67 | }; 68 | it(testName, test.length ? executor : function () { 69 | return executor.call(this, null); 70 | }) 71 | } else { 72 | throw TypeError('expected an object or function'); 73 | } 74 | }) 75 | } 76 | _import(tests); 77 | } 78 | 79 | importTests('./test/tests'); 80 | 81 | -------------------------------------------------------------------------------- /test/src/decorators.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { 3 | CPromise, 4 | async, 5 | listen, 6 | cancel, 7 | timeout, 8 | innerWeight, 9 | label, 10 | canceled, 11 | atomic, 12 | done, 13 | progress, 14 | CanceledError, 15 | ReactComponent, 16 | E_REASON_TIMEOUT 17 | } = require('../../lib/c-promise'); 18 | 19 | 20 | const delay = (ms, value, options) => new CPromise(resolve => setTimeout(() => resolve(value), ms), options); 21 | 22 | const makePromise = (ms, value, handler) => { 23 | return new CPromise((resolve, reject, scope) => { 24 | const timer = setTimeout(resolve, ms, value); 25 | scope.onCancel(() => { 26 | clearTimeout(timer); 27 | handler && handler(scope); 28 | }) 29 | }); 30 | }; 31 | 32 | const measureTime = () => { 33 | let timestamp = Date.now(); 34 | return () => Date.now() - timestamp; 35 | } 36 | 37 | module.exports = { 38 | "should support async decorator": async function () { 39 | const klass = class { 40 | @async 41 | * generator(x) { 42 | const y = yield delay(1000, 123); 43 | return x + y; 44 | } 45 | } 46 | 47 | const obj = new klass(); 48 | 49 | const thenable = obj.generator(1); 50 | 51 | assert.ok(thenable instanceof CPromise); 52 | 53 | return thenable.then(result => { 54 | assert.equal(result, 124); 55 | }) 56 | }, 57 | 58 | "should support listen & cancel decorators": async function () { 59 | 60 | const time = measureTime(); 61 | 62 | const klass = class { 63 | @listen 64 | @async 65 | * generator(x) { 66 | const y = yield delay(1000, 123); 67 | return x + y; 68 | } 69 | 70 | @cancel 71 | emitCancel() { 72 | 73 | } 74 | } 75 | 76 | const obj = new klass(); 77 | 78 | const thenable = obj.generator(1); 79 | 80 | assert.ok(thenable instanceof CPromise); 81 | 82 | setTimeout(() => { 83 | obj.emitCancel() 84 | }, 500); 85 | 86 | return thenable.then(result => { 87 | assert.fail('was not canceled'); 88 | }, err => { 89 | assert.ok(err instanceof CanceledError, 'is not a CanceledError'); 90 | if (time() < 500) { 91 | assert.fail('early cancellation detected'); 92 | } 93 | }) 94 | }, 95 | 96 | "should support timeout, innerWeight, label decorators": async function () { 97 | const time = measureTime(); 98 | 99 | const klass = class { 100 | @label('test') 101 | @innerWeight(2) 102 | @timeout(500) 103 | @async 104 | * generator(x) { 105 | const y = yield delay(1000, 123); 106 | return x + y; 107 | } 108 | } 109 | 110 | const obj = new klass(); 111 | 112 | const thenable = obj.generator(1); 113 | 114 | assert.ok(thenable instanceof CPromise); 115 | 116 | return thenable.then(result => { 117 | assert.fail('was not canceled'); 118 | }, err => { 119 | assert.ok(err instanceof CanceledError, 'is not a CanceledError'); 120 | assert.equal(err.code, E_REASON_TIMEOUT); 121 | assert.equal(thenable.label(), 'test'); 122 | assert.equal(thenable.innerWeight(), 2); 123 | if (time() < 500) { 124 | assert.fail('early cancellation detected'); 125 | } 126 | }) 127 | }, 128 | 129 | "should support canceled decorator": async function () { 130 | let invoked = false; 131 | 132 | const klass = class { 133 | @canceled(function (err, scope, context) { 134 | assert.ok(this instanceof klass); 135 | assert.ok(context instanceof klass); 136 | assert.ok(err instanceof CanceledError); 137 | assert.ok(scope instanceof CPromise); 138 | invoked = true; 139 | }) 140 | @async 141 | * generator() { 142 | yield delay(1000, 123); 143 | } 144 | } 145 | 146 | const obj = new klass(); 147 | 148 | const thenable = obj.generator(); 149 | 150 | setTimeout(() => { 151 | thenable.cancel(); 152 | }, 100); 153 | 154 | return thenable.then(() => { 155 | assert.ok(invoked, 'was not canceled'); 156 | }, err => { 157 | if(err instanceof CanceledError) { 158 | assert.fail(`was not caught: ${err}`); 159 | }else{ 160 | throw err; 161 | } 162 | }) 163 | }, 164 | 165 | "should support progress decorator": async function () { 166 | let stage= 0; 167 | 168 | const klass = class { 169 | @progress(function (value, scope, data, context) { 170 | stage++; 171 | assert.ok(this instanceof klass); 172 | assert.ok(context instanceof klass); 173 | assert.ok(scope instanceof CPromise); 174 | assert.ok(typeof value === 'number'); 175 | assert.ok(typeof data === 'object'); 176 | assert.strictEqual(value, stage / 4); 177 | }) 178 | @innerWeight(4) 179 | @async 180 | *generator() { 181 | yield delay(300); 182 | yield delay(300); 183 | yield delay(300); 184 | yield delay(300); 185 | } 186 | } 187 | 188 | const obj = new klass(); 189 | 190 | const thenable = obj.generator(); 191 | 192 | return thenable.then(() => { 193 | assert.strictEqual(stage, 4); 194 | }) 195 | }, 196 | 197 | "should support atomic decorator": async function () { 198 | const klass = class { 199 | @async 200 | *fn1() { 201 | yield delay(100); 202 | yield delay(100); 203 | yield this.fn2(); 204 | yield delay(100); 205 | yield delay(100); 206 | } 207 | @label('atomic-fn') 208 | @atomic 209 | *fn2() { 210 | yield delay(100); 211 | yield delay(100); 212 | yield delay(100); 213 | yield delay(100); 214 | } 215 | } 216 | 217 | const obj = new klass(); 218 | 219 | const promise = obj.fn1(); 220 | 221 | setTimeout(()=>{ 222 | promise.cancel(); 223 | }, 300); 224 | 225 | return promise.canceled((err) => { 226 | assert.strictEqual(err.scope.label(), 'atomic-fn'); 227 | }) 228 | }, 229 | 230 | "should support done decorator": async function () { 231 | const err= new Error('test'); 232 | const klass = class { 233 | @done(function(value, isRejected, scope){ 234 | assert.strictEqual(value, err); 235 | assert.strictEqual(isRejected, true); 236 | assert.ok(this instanceof klass); 237 | assert.ok(scope instanceof CPromise); 238 | }) 239 | *fn1() { 240 | yield delay(100); 241 | throw err; 242 | } 243 | } 244 | 245 | const obj = new klass(); 246 | 247 | return obj.fn1(); 248 | }, 249 | 250 | "should support ReactComponent decorator": async function () { 251 | @ReactComponent 252 | class klass{ 253 | *componentDidMount(scope) { 254 | assert.ok(scope instanceof CPromise); 255 | yield delay(100); 256 | return 123; 257 | } 258 | 259 | *componentWillUnmount(scope){ 260 | assert.ok(scope instanceof CPromise); 261 | yield delay(100); 262 | return 456; 263 | } 264 | } 265 | 266 | const obj = new klass(); 267 | 268 | assert.strictEqual(await obj.componentDidMount(), 123); 269 | assert.strictEqual(await obj.componentWillUnmount(), 456); 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /test/tests/CPromise.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const {CPromise, E_REASON_CANCELED, E_REASON_UNMOUNTED} = require('../../lib/c-promise'); 3 | const {CanceledError} = CPromise; 4 | 5 | const delay = (ms, value, options) => new CPromise(resolve => setTimeout(() => resolve(value), ms), options); 6 | 7 | const makePromise = (ms, value, handler) => { 8 | return new CPromise((resolve, reject, scope) => { 9 | const timer = setTimeout(resolve, ms, value); 10 | scope.onCancel(() => { 11 | clearTimeout(timer); 12 | handler && handler(scope); 13 | }) 14 | }); 15 | }; 16 | 17 | const valuesList = (...expected) => { 18 | let i = 0; 19 | return (value) => { 20 | assert.deepStrictEqual(value, expected[i++]); 21 | } 22 | } 23 | 24 | module.exports = { 25 | constructor: { 26 | 'should create instance of CancelablePromise': function () { 27 | const promise = new CPromise((resolve, reject) => { 28 | }); 29 | assert(promise instanceof CPromise); 30 | assert(promise instanceof Promise); 31 | } 32 | }, 33 | 34 | 'should support cancellation by the external signal': async function () { 35 | const controller = new CPromise.AbortController(); 36 | 37 | const timestamp = Date.now(); 38 | const time = () => Date.now() - timestamp; 39 | 40 | setTimeout(() => controller.abort(), 55); 41 | 42 | return new CPromise((resolve, reject) => { 43 | setTimeout(resolve, 100); 44 | }, {signal: controller.signal}).then(() => { 45 | throw Error('not cancelled'); 46 | }, (err) => { 47 | if (!CPromise.isCanceledError(err)) { 48 | if (time() < 50) { 49 | throw Error('Early cancellation'); 50 | } 51 | throw err; 52 | } 53 | }) 54 | }, 55 | 56 | 'cancellation': { 57 | 'should reject the promise with CanceledError': async function () { 58 | const promise = new CPromise((resolve, reject) => { 59 | setTimeout(resolve, 1000, 123); 60 | }); 61 | 62 | const timestamp = Date.now(); 63 | const timeout = 100; 64 | 65 | setTimeout(() => { 66 | promise.cancel(); 67 | }, timeout); 68 | 69 | await promise.then(() => { 70 | assert.fail('promise has not been canceled'); 71 | }, (err) => { 72 | if (err instanceof CPromise.CanceledError) { 73 | if (Date.now() - timestamp + 5 < timeout) { 74 | assert.fail('early cancellation detected') 75 | } 76 | return; 77 | } 78 | throw err; 79 | }); 80 | }, 81 | 82 | 'should reject the promise chain with CanceledError': async function () { 83 | let currentChain = 1; 84 | 85 | const chain = delay(100) 86 | .then(() => { 87 | currentChain = 2 88 | return delay(100) 89 | }) 90 | .then(() => { 91 | currentChain = 3 92 | return delay(100) 93 | }) 94 | .then(() => { 95 | currentChain = 4 96 | return delay(100) 97 | }) 98 | .then(() => { 99 | currentChain = 5 100 | }) 101 | 102 | const timestamp = Date.now(); 103 | const timeout = 250; 104 | const targetChainIndex = 3; 105 | 106 | setTimeout(() => { 107 | chain.cancel(); 108 | }, timeout); 109 | 110 | await chain.then(() => { 111 | assert.fail('promise has not been canceled'); 112 | }, (err) => { 113 | if (err instanceof CPromise.CanceledError) { 114 | if (Date.now() - timestamp < timeout - 5) { 115 | assert.fail('early cancellation detected') 116 | } 117 | if (currentChain !== targetChainIndex) { 118 | assert.equal(currentChain, targetChainIndex, 'wrong chain raised the error') 119 | } 120 | return; 121 | } 122 | throw err; 123 | }); 124 | }, 125 | 126 | 'throwing the CanceledError inside the promise': { 127 | "should lead to chains cancellation": async function () { 128 | let canceled = false; 129 | let signaled = false; 130 | 131 | return CPromise.delay(10, 123).then((value, {signal, onCancel}) => { 132 | onCancel((reason) => { 133 | assert.equal(reason.message, 'test'); 134 | canceled = true; 135 | }); 136 | 137 | signal.addEventListener('abort', () => { 138 | signaled = true; 139 | }) 140 | 141 | return CPromise.delay(20).then(() => { 142 | throw new CPromise.CanceledError('test'); 143 | }); 144 | }).then(() => { 145 | assert.fail("has not been rejected"); 146 | }, (err) => { 147 | assert.equal(err.message, 'test'); 148 | assert.ok(canceled, "not cancelled"); 149 | assert.ok(signaled, "not signaled"); 150 | assert.ok(err instanceof CPromise.CanceledError); 151 | }) 152 | } 153 | }, 154 | 155 | 'should cancel only isolated leaves': async function () { 156 | let rootCanceled = false; 157 | let firstCanceled = false; 158 | let secondCanceled = false; 159 | 160 | const root = makePromise(1000, null, () => { 161 | rootCanceled = true; 162 | }); 163 | 164 | const firstLeaf = root.then(() => makePromise(1000)).then(() => { 165 | assert.fail('first promise leaf was not canceled'); 166 | }).canceled(() => { 167 | firstCanceled = true; 168 | }); 169 | 170 | const secondLeaf = root.then(() => makePromise(1000)).then(() => { 171 | assert.fail('second promise leaf was not canceled'); 172 | }).canceled(() => { 173 | secondCanceled = true; 174 | }); 175 | 176 | firstLeaf.cancel(); 177 | await firstLeaf; 178 | assert.equal(firstCanceled, true); 179 | assert.equal(secondCanceled, false); 180 | assert.equal(rootCanceled, false); 181 | secondLeaf.cancel(); 182 | await secondLeaf; 183 | assert.equal(firstCanceled, true); 184 | assert.equal(secondCanceled, true); 185 | assert.equal(rootCanceled, true); 186 | await root.canceled(); 187 | }, 188 | 189 | 'should cancel all leaves if the force option is set to true': async function () { 190 | let rootCanceled = false; 191 | let firstCanceled = false; 192 | let secondCanceled = false; 193 | 194 | const root = makePromise(1000, null, () => { 195 | rootCanceled = true; 196 | }); 197 | 198 | const firstLeaf = root.then(() => makePromise(1000)).then(() => { 199 | assert.fail('first promise leaf was not canceled'); 200 | }).canceled(() => { 201 | firstCanceled = true; 202 | }); 203 | 204 | const secondLeaf = root.then(() => makePromise(1000)).then(() => { 205 | assert.fail('second promise leaf was not canceled'); 206 | }).canceled(() => { 207 | secondCanceled = true; 208 | }); 209 | 210 | firstLeaf.cancel('', true); 211 | await firstLeaf; 212 | assert.equal(firstCanceled, true); 213 | assert.equal(secondCanceled, true); 214 | assert.equal(rootCanceled, true); 215 | }, 216 | 217 | 'should respect the priority of the CanceledError': async function(){ 218 | const chain= CPromise.delay(100).atomic().delay(100).delay(100); 219 | 220 | chain.cancel(E_REASON_CANCELED); 221 | 222 | await CPromise.delay(50); 223 | 224 | chain.cancel(E_REASON_UNMOUNTED); 225 | 226 | return chain.then(()=>{ 227 | assert.fail('was not cancelled'); 228 | }, (err)=>{ 229 | assert.strictEqual(err.code, E_REASON_UNMOUNTED); 230 | }) 231 | } 232 | }, 233 | 234 | 'progress capturing': { 235 | 'should return correct chain progress': async function () { 236 | const chain = delay(100) 237 | .then(() => { 238 | assertProgress(0.2) 239 | return delay(100) 240 | }) 241 | .then(() => { 242 | assertProgress(0.4) 243 | return delay(100) 244 | }) 245 | .then(() => { 246 | assertProgress(0.6) 247 | return delay(100) 248 | }) 249 | .then(() => { 250 | assertProgress(0.8) 251 | }); 252 | 253 | const assertProgress = (expected) => { 254 | assert.equal(chain.progress(), expected); 255 | } 256 | 257 | assertProgress(0); 258 | 259 | await chain.then(() => { 260 | assertProgress(1); 261 | }) 262 | } 263 | }, 264 | 265 | 'suspension': { 266 | 'should support pause and resume methods': async function () { 267 | let timestamp = Date.now(); 268 | let pauseEmitted, resumeEmitted; 269 | const passed = () => { 270 | return Date.now() - timestamp; 271 | } 272 | const chain = new CPromise((resolve, reject, {onPause, onResume}) => { 273 | setTimeout(resolve, 500); 274 | onPause(() => { 275 | pauseEmitted = true; 276 | }); 277 | 278 | onResume(() => { 279 | resumeEmitted = true; 280 | }); 281 | }).then(() => { 282 | assert.ok(passed() > 1200, `early completion (${passed()}ms)`); 283 | assert.ok(pauseEmitted, 'pause event has not been emitted'); 284 | assert.ok(resumeEmitted, 'resume event has not been emitted'); 285 | }); 286 | 287 | setTimeout(() => { 288 | chain.pause(); 289 | setTimeout(() => { 290 | chain.resume(); 291 | }, 1000); 292 | }, 300); 293 | 294 | return chain; 295 | }, 296 | 297 | 'should propagate pause&resume events': async function(){ 298 | let pauseEvents= [], resumeEvents= []; 299 | 300 | const chain = new CPromise((resolve, reject, {onPause, onResume}) => { 301 | setTimeout(resolve, 500); 302 | onPause(() => { 303 | pauseEvents[0] = true; 304 | }); 305 | 306 | onResume(() => { 307 | resumeEvents[0] = true; 308 | }); 309 | }).then((v, {onPause, onResume}) => { 310 | return delay(100); 311 | }); 312 | 313 | chain.onPause(() => { 314 | pauseEvents[1] = true; 315 | }); 316 | 317 | chain.onResume(() => { 318 | resumeEvents[1] = true; 319 | }); 320 | 321 | chain.pause(); 322 | 323 | assert.strictEqual(chain.isPaused, true); 324 | 325 | setTimeout(()=>chain.resume(), 100); 326 | 327 | return chain.then(()=>{ 328 | assert.ok(pauseEvents[0], 'pause event [0] has not been emitted'); 329 | assert.ok(resumeEvents[0], 'resume event [0] has not been emitted'); 330 | assert.ok(pauseEvents[1], 'pause event [1] has not been emitted'); 331 | assert.ok(resumeEvents[1], 'resume event [1] has not been emitted'); 332 | }); 333 | } 334 | }, 335 | 336 | 'timeout': { 337 | 'should cancel the chain with timeout reason': async function () { 338 | const p = new CPromise(function (resolve, reject) { 339 | setTimeout(resolve, 1000); 340 | }); 341 | 342 | return p 343 | .timeout(100) 344 | .then(() => { 345 | assert.fail('chain was not cancelled'); 346 | }, err => { 347 | assert.ok(err instanceof CanceledError); 348 | assert.equal(err.message, 'timeout'); 349 | }) 350 | } 351 | }, 352 | 353 | 'CPromise#then': { 354 | 'should support generators': async function () { 355 | return CPromise.resolve() 356 | .then(function* () { 357 | const result1 = yield makePromise(100, 123); 358 | const result2 = yield makePromise(100, 456); 359 | return result1 + result2; 360 | }) 361 | .then(function* (value) { 362 | assert.equal(value, 123 + 456); 363 | }) 364 | } 365 | }, 366 | 367 | 'CPromise#Symbol(toCPromise)': { 368 | 'should be invoked to convert the object to an CPromise instance': async function () { 369 | const toCPromise = Symbol.for('toCPromise'); 370 | let invoked = false; 371 | const obj = { 372 | [toCPromise]: function (CPromise) { 373 | invoked = true; 374 | return new CPromise((resolve) => resolve(123)); 375 | } 376 | }; 377 | 378 | const promise = CPromise.resolve(obj); 379 | 380 | assert.ok(invoked); 381 | assert.ok(promise instanceof CPromise); 382 | 383 | return promise.then(value => { 384 | assert.equal(value, 123); 385 | }) 386 | } 387 | }, 388 | 389 | 'CPromise#progress()': { 390 | 'should set the value of the promise progress': function (done) { 391 | this.timeout(5000); 392 | const p = new CPromise(function (resolve, reject) { 393 | let progress = 0; 394 | let i = 0; 395 | const timer = setInterval(() => { 396 | progress += 0.2; 397 | this.progress(progress, {description: 'test', value: i++}); 398 | if (progress >= 1) { 399 | clearInterval(timer); 400 | resolve('done'); 401 | } 402 | 403 | }, 300); 404 | }); 405 | 406 | const expect = [0.2, 0.4, 0.6, 0.8, 1]; 407 | let index = 0; 408 | 409 | p.on('progress', (actualProgress, scope, data) => { 410 | const expected = expect[index]; 411 | 412 | assert.strictEqual(actualProgress, expected); 413 | assert.deepStrictEqual(data, {description: 'test', value: index}); 414 | index++; 415 | }) 416 | 417 | p.then(result => { 418 | assert.equal(result, 'done'); 419 | done(); 420 | }).catch(done); 421 | } 422 | }, 423 | 424 | 'CPromise#canceled()': { 425 | 'should catch the CanceledError rejection': async function () { 426 | let onCanceledCalled = false; 427 | const chain = CPromise.delay(500) 428 | .canceled(() => { 429 | onCanceledCalled = true; 430 | }) 431 | .catch((err) => { 432 | assert.fail(`should not throw: ${err}`); 433 | }) 434 | 435 | setTimeout(() => chain.cancel(), 0); 436 | 437 | return chain.then((value) => { 438 | assert.equal(value, undefined); 439 | assert.equal(onCanceledCalled, true, 'onCanceledCalled was not called'); 440 | assert.equal(chain.isCanceled, true, `isCanceled is not true`) 441 | }); 442 | } 443 | }, 444 | 445 | 'CPromise.resolve': { 446 | 'should convert object to a CPromise instance': async function () { 447 | let isCanceled = false; 448 | const thenable = { 449 | then() { 450 | }, 451 | cancel() { 452 | isCanceled = true; 453 | } 454 | } 455 | 456 | const chain = CPromise.resolve(thenable).then(() => { 457 | assert.fail('not canceled'); 458 | }, (err) => { 459 | assert.ok(err instanceof CanceledError, 'not a CanceledError instance'); 460 | assert.ok(isCanceled, 'was not cancelled') 461 | }); 462 | 463 | chain.cancel(); 464 | 465 | return chain; 466 | }, 467 | 468 | 'generator': { 469 | 'should resolve generator to a CPromise': function () { 470 | assert.ok(CPromise.resolve(function* () { 471 | }) instanceof CPromise); 472 | }, 473 | 474 | 'should resolve internal chains': async function () { 475 | const timestamp = Date.now(); 476 | const time = () => Date.now() - timestamp; 477 | 478 | const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value)); 479 | 480 | return CPromise.resolve(function* () { 481 | const resolved1 = yield CPromise.delay(105, 123); 482 | assert.ok(time() >= 100); 483 | assert.equal(resolved1, 123); 484 | const resolved2 = yield delay(100, 456) 485 | assert.equal(resolved2, 456); 486 | }); 487 | }, 488 | 489 | 'should reject the promise if generator thrown an error': async function () { 490 | const timestamp = Date.now(); 491 | const time = () => Date.now() - timestamp; 492 | return CPromise.resolve(function* () { 493 | const timestamp = Date.now(); 494 | const time = () => Date.now() - timestamp; 495 | yield CPromise.delay(105); 496 | throw Error('test'); 497 | }).then(() => { 498 | assert.ok(time() >= 100, 'early throw detected'); 499 | assert.fail('the generator did not throw an error') 500 | }, (err) => { 501 | assert.equal(err.message, 'test'); 502 | }) 503 | }, 504 | 505 | 'should support cancellation': async function () { 506 | let thrown = false; 507 | let canceledInternals = false; 508 | 509 | const delay = (ms) => new CPromise((resolve, reject, {onCancel}) => { 510 | onCancel(() => { 511 | canceledInternals = true; 512 | }); 513 | setTimeout(resolve, ms); 514 | }); 515 | 516 | const chain = CPromise.resolve(function* () { 517 | yield CPromise.delay(100); 518 | try { 519 | yield delay(100); 520 | } catch (err) { 521 | thrown = true; 522 | assert.ok(err instanceof CanceledError, 'error is not an instanceof CanceledError'); 523 | } 524 | 525 | yield CPromise.delay(100); 526 | 527 | if (!thrown) { 528 | assert.fail('The canceled error was not thrown'); 529 | } 530 | }); 531 | 532 | setTimeout(() => { 533 | chain.cancel(); 534 | }, 150); 535 | 536 | return chain; 537 | }, 538 | 539 | 'progress capturing': { 540 | 'should support progress capturing': async function () { 541 | this.timeout(5000); 542 | const expected = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 1]; 543 | let index = 0; 544 | 545 | const fn= CPromise.promisify(function*(){ 546 | this.innerWeight(2); 547 | yield CPromise.delay(250); 548 | yield CPromise.delay(250); 549 | }) 550 | 551 | return CPromise.resolve(function* () { 552 | this.innerWeight(10); 553 | let i = 9; 554 | while (--i >= 0) { 555 | yield CPromise.delay(250); 556 | } 557 | yield fn(); 558 | }).progress(value => { 559 | assert.equal(value, expected[index], `${value}!=${expected[index]} progress tick value`); 560 | index++; 561 | }); 562 | } 563 | }, 564 | 565 | 'should proxy signals': async function () { 566 | const chain = CPromise.resolve(function* () { 567 | yield new CPromise((resolve, reject, scope) => { 568 | scope.on('signal', (type, data) => { 569 | assert.equal(type, 'test'); 570 | assert.equal(data, 123); 571 | resolve(); 572 | }) 573 | }); 574 | }); 575 | 576 | setTimeout(() => { 577 | chain.emitSignal('test', 123); 578 | }, 0); 579 | 580 | return chain; 581 | } 582 | } 583 | }, 584 | 585 | 'CPromise.all': { 586 | 'should be resolved with an array of inner chain values': async function () { 587 | const v1 = 123; 588 | const v2 = 456; 589 | const timestamp = Date.now(); 590 | const time = () => Date.now() - timestamp; 591 | return CPromise.all([ 592 | CPromise.delay(55, v1), 593 | CPromise.delay(105, v2) 594 | ]).then((values) => { 595 | assert.ok(time() >= 100); 596 | assert.deepStrictEqual(values, [v1, v2]); 597 | }) 598 | }, 599 | 600 | 'should cancel pending chains on reject': async function () { 601 | const message = 'test'; 602 | let canceledCounter = 0; 603 | return CPromise.all([ 604 | CPromise.reject(new Error(message)), 605 | makePromise(50, 123, () => canceledCounter++), 606 | makePromise(100, 456, () => canceledCounter++), 607 | ]).then(() => { 608 | assert.fail('does not throw'); 609 | }, err => { 610 | assert.equal(err.message, message); 611 | }).then(() => { 612 | assert.equal(canceledCounter, 2); 613 | }) 614 | }, 615 | 616 | 'should support concurrency': async function () { 617 | let pending = 0; 618 | 619 | return CPromise.all(function* () { 620 | for (let i = 0; i < 5; i++) { 621 | pending++; 622 | yield makePromise(500, i).then((v) => { 623 | pending--; 624 | assert.ok(pending < 2); 625 | return v; 626 | }); 627 | } 628 | }, {concurrency: 2}).then((values) => { 629 | assert.deepStrictEqual(values, [0, 1, 2, 3, 4]); 630 | }) 631 | }, 632 | 633 | 'should proxy signals': async function () { 634 | const chain = CPromise.all([ 635 | new CPromise((resolve, reject, scope) => { 636 | scope.on('signal', (type, data) => { 637 | assert.equal(type, 'test'); 638 | assert.equal(data, 123); 639 | resolve(); 640 | }) 641 | }), 642 | new CPromise((resolve, reject, scope) => { 643 | scope.on('signal', (type, data) => { 644 | assert.equal(type, 'test'); 645 | assert.equal(data, 123); 646 | resolve(); 647 | }) 648 | }) 649 | ]); 650 | 651 | setTimeout(() => { 652 | chain.emitSignal('test', 123); 653 | }, 0); 654 | 655 | return chain; 656 | } 657 | }, 658 | 659 | 'CPromise.race': { 660 | 'should return a promise that fulfills or rejects as soon as one of the promises settled': async function () { 661 | const v1 = 123; 662 | const v2 = 456; 663 | const timestamp = Date.now(); 664 | const time = () => Date.now() - timestamp; 665 | return CPromise.race([ 666 | CPromise.delay(55, v1), 667 | CPromise.delay(100, v2) 668 | ]).then((value) => { 669 | assert.ok(time() >= 50 && time() < 100); 670 | assert.equal(value, v1); 671 | }) 672 | }, 673 | 674 | 'should cancel other pending chains on settled': async function () { 675 | let canceledCounter = 0; 676 | return CPromise.race([ 677 | makePromise(50, 123, () => canceledCounter++), 678 | makePromise(100, 456, () => canceledCounter++), 679 | makePromise(150, 789, () => canceledCounter++), 680 | ]).then(() => { 681 | assert.equal(canceledCounter, 2); 682 | }); 683 | }, 684 | 685 | 'should proxy signals': async function () { 686 | return new Promise(resolve => { 687 | let counter = 0; 688 | const handle = () => { 689 | if (++counter === 2) { 690 | resolve(); 691 | } 692 | } 693 | 694 | const chain = CPromise.race([ 695 | new CPromise((resolve, reject, scope) => { 696 | scope.on('signal', (type, data) => { 697 | assert.equal(type, 'test'); 698 | assert.equal(data, 123); 699 | handle(); 700 | }) 701 | }), 702 | new CPromise((resolve, reject, scope) => { 703 | scope.on('signal', (type, data) => { 704 | assert.equal(type, 'test'); 705 | assert.equal(data, 123); 706 | handle(); 707 | }) 708 | }) 709 | ]); 710 | 711 | setTimeout(() => { 712 | chain.emitSignal('test', 123); 713 | }, 0); 714 | }); 715 | } 716 | }, 717 | 718 | 'CPromise.allSettled': { 719 | 'should aggregate promise results into array of status objects' : async function () { 720 | const err = new Error('test1'); 721 | return CPromise.allSettled([ 722 | delay(100, 123), 723 | CPromise.reject(err), 724 | CPromise.resolve(456) 725 | ]).then(results => { 726 | assert.deepStrictEqual(results, [ 727 | {status: 'fulfilled', value: 123}, 728 | {status: 'rejected', reason: err}, 729 | {status: 'fulfilled', value: 456} 730 | ]); 731 | }) 732 | }, 733 | 734 | 'should support cancellation': async()=>{ 735 | const chain= CPromise.allSettled([ 736 | delay(100, 123), 737 | delay(500).then(()=> { 738 | throw Error('break'); 739 | }) 740 | ]).then(results => { 741 | assert.fail('was not cancelled'); 742 | }, err=>{ 743 | if(!CPromise.isCanceledError(err)){ 744 | assert.fail(`rejected not with CancelledError`); 745 | } 746 | }) 747 | 748 | setTimeout(()=> chain.cancel(), 200); 749 | } 750 | }, 751 | 752 | 'events emitter': { 753 | 'CPromise.on': { 754 | 'should add new listener': function () { 755 | const ee = new CPromise(resolve => { 756 | }); 757 | assert.equal(ee.listenersCount('test'), 0); 758 | ee.on('test', function () { 759 | }); 760 | assert.equal(ee.listenersCount('test'), 1); 761 | ee.on('test', function () { 762 | }); 763 | assert.equal(ee.listenersCount('test'), 2); 764 | } 765 | }, 766 | 767 | 'CPromise.off': { 768 | 'should remove the listener': function () { 769 | const ee = new CPromise(resolve => { 770 | }); 771 | const listener1 = function () { 772 | }; 773 | const listener2 = function () { 774 | }; 775 | ee.on('test', listener1); 776 | assert.equal(ee.listenersCount('test'), 1); 777 | ee.on('test', listener2); 778 | assert.equal(ee.listenersCount('test'), 2); 779 | ee.off('test', listener1); 780 | assert.equal(ee.listenersCount('test'), 1); 781 | ee.off('test', listener2); 782 | assert.equal(ee.listenersCount('test'), 0); 783 | } 784 | }, 785 | 786 | 'CPromise.emit': { 787 | 'should emit the event listeners': function () { 788 | const ee = new CPromise(resolve => { 789 | }); 790 | let invoked1, invoked2; 791 | const listener1 = function (...data) { 792 | invoked1 = true; 793 | assert.deepStrictEqual(data, [1, 2, 3]); 794 | }; 795 | const listener2 = function (...data) { 796 | invoked2 = true; 797 | assert.deepStrictEqual(data, [1, 2, 3]); 798 | }; 799 | ee.on('test', listener1); 800 | ee.on('test', listener2); 801 | 802 | ee.emit('test', 1, 2, 3); 803 | 804 | assert.ok(invoked1); 805 | assert.ok(invoked2); 806 | } 807 | }, 808 | 809 | 'CPromise.hasListener': { 810 | 'should return true if the listener is already registered to the specific event': function () { 811 | const ee = new CPromise(resolve => { 812 | }); 813 | const listener1 = function (...data) { 814 | }; 815 | const listener2 = function (...data) { 816 | }; 817 | const listener3 = function (...data) { 818 | }; 819 | ee.on('test1', listener1); 820 | ee.on('test1', listener2); 821 | ee.on('test2', listener3); 822 | 823 | assert.strictEqual(ee.hasListener('test1', listener1), true); 824 | assert.strictEqual(ee.hasListener('test1', listener2), true); 825 | assert.strictEqual(ee.hasListener('test1', listener3), false); 826 | assert.strictEqual(ee.hasListener('test2', listener3), true); 827 | } 828 | }, 829 | }, 830 | 831 | 'CPromise.promisify': { 832 | 'callback styled function': { 833 | 'should handle resolving with a single argument': async function () { 834 | const fn = CPromise.promisify(function (arg0, cb) { 835 | assert.strictEqual(arg0, 123); 836 | setTimeout(cb, 100, undefined, 456); 837 | }); 838 | 839 | assert.ok(typeof fn === 'function'); 840 | 841 | const promise = fn(123); 842 | 843 | assert.ok(promise instanceof CPromise); 844 | 845 | return promise.then(value => { 846 | assert.strictEqual(value, 456); 847 | }) 848 | }, 849 | 850 | 'should handle resolving with multiple arguments': async function () { 851 | const fn = CPromise.promisify(function (arg0, cb) { 852 | assert.strictEqual(arg0, 123); 853 | setTimeout(cb, 100, undefined, 456, 789); 854 | }); 855 | 856 | assert.ok(typeof fn === 'function'); 857 | 858 | const promise = fn(123); 859 | 860 | assert.ok(promise instanceof CPromise); 861 | 862 | return promise.then(value => { 863 | assert.deepStrictEqual(value, [456, 789]); 864 | }) 865 | }, 866 | 867 | 'should handle a single argument as an array when multiArgs option activated': async function () { 868 | const fn = CPromise.promisify(function (arg0, cb) { 869 | assert.strictEqual(arg0, 123); 870 | setTimeout(cb, 100, undefined, 456); 871 | }, {multiArgs: true}); 872 | 873 | assert.ok(typeof fn === 'function'); 874 | 875 | const promise = fn(123); 876 | 877 | assert.ok(promise instanceof CPromise); 878 | 879 | return promise.then(value => { 880 | assert.deepStrictEqual(value, [456]); 881 | }) 882 | }, 883 | 884 | 'should handle rejection': async function () { 885 | const fn = CPromise.promisify(function (arg0, cb) { 886 | assert.strictEqual(arg0, 123); 887 | setTimeout(cb, 100, new Error('test')); 888 | }); 889 | 890 | const promise = fn(123); 891 | 892 | return promise.then(value => { 893 | assert.fail(`doesn't throw`); 894 | }).catch(err => { 895 | assert.ok(err.message, 'test'); 896 | }) 897 | } 898 | }, 899 | 900 | 'should support async function decoration': async function () { 901 | const fn = CPromise.promisify(async function (arg0) { 902 | return delay(100, 123); 903 | }); 904 | 905 | const promise = fn(123); 906 | 907 | assert.ok(promise instanceof CPromise); 908 | 909 | return promise.then(value => { 910 | assert.deepStrictEqual(value, 123); 911 | }) 912 | }, 913 | 914 | 'should support generator function decoration': async function () { 915 | const fn = CPromise.promisify(function* (arg0) { 916 | return yield delay(100, 123); 917 | }); 918 | 919 | const promise = fn(123); 920 | 921 | assert.ok(promise instanceof CPromise); 922 | 923 | return promise.then(value => { 924 | assert.deepStrictEqual(value, 123); 925 | }) 926 | }, 927 | 928 | 'should support decorator option': async function(){ 929 | CPromise.promisify(function* (arg0) { 930 | assert.strictEqual(arg0, 8); 931 | return yield CPromise.delay(100, 123); 932 | }, { 933 | decorator: (fn, options) => { 934 | return function(scope, arg0, arg1){ 935 | assert.ok(scope instanceof CPromise, 'scope is not a CPromise context'); 936 | assert.strictEqual(options.fnType, 'generator'); 937 | return fn(arg0 + arg1); 938 | }; 939 | }, 940 | scopeArg: true 941 | })(3, 5).then(v => { 942 | assert.strictEqual(v, 123); 943 | }); 944 | } 945 | }, 946 | 947 | 'CPromise#done': { 948 | 'should invoke the handler with settled values when the promise done with success or failure': async()=>{ 949 | let counter= 0; 950 | const handler= (value, isRejected, scope)=>{ 951 | assert.ok(typeof isRejected=='boolean'); 952 | assert.ok(scope instanceof CPromise); 953 | counter++; 954 | return value; 955 | } 956 | const err= new Error('test'); 957 | return CPromise.all([ 958 | CPromise.delay(100, 123).done(handler), 959 | CPromise.reject(err).delay(100).done(handler), 960 | ]).then((results)=>{ 961 | assert.strictEqual(counter, 2); 962 | assert.deepStrictEqual(results, [ 963 | 123, 964 | err 965 | ]) 966 | }) 967 | } 968 | }, 969 | 970 | 'CPromise#finally': { 971 | 'should invoke the handler that will be invoked when promise settled': async()=>{ 972 | let counter= 0; 973 | const handler= (value, isRejected, scope)=>{ 974 | assert.ok(typeof isRejected=='boolean'); 975 | assert.ok(scope instanceof CPromise); 976 | counter++; 977 | } 978 | const err= new Error('test'); 979 | return CPromise.allSettled([ 980 | CPromise.delay(100, 123).finally(handler), 981 | CPromise.reject(err).delay(100).finally(handler), 982 | ]).then((results)=>{ 983 | assert.strictEqual(counter, 2); 984 | assert.deepStrictEqual(results, [ 985 | {status: 'fulfilled', value: 123}, 986 | {status: 'rejected', reason : err}, 987 | ]) 988 | }) 989 | } 990 | }, 991 | 992 | 'CPromise#atomic': { 993 | 'atomic("detached")': { 994 | 'should protect promise chain from canceling from outside and keep it running in the background': async () => { 995 | let atomicChainCompleted = false; 996 | const chain = CPromise.delay(200, 1) 997 | .then(v => CPromise.delay(200, v + 2)) 998 | .then(result => { 999 | atomicChainCompleted = true; 1000 | assert.strictEqual(result, 3); 1001 | }, err => { 1002 | assert.fail(`Unexpected rejecting: ${err}`); 1003 | }) 1004 | .atomic('detached') 1005 | .then(v => CPromise.delay(1000)) 1006 | .then(v => { 1007 | assert.fail('Unexpected resolving'); 1008 | }, err => { 1009 | assert.ok(atomicChainCompleted === false); 1010 | assert.ok(err instanceof CanceledError); 1011 | }) 1012 | 1013 | setTimeout(() => { 1014 | chain.cancel(); 1015 | }, 50); 1016 | 1017 | return chain; 1018 | } 1019 | }, 1020 | 1021 | 'atomic("await")': { 1022 | 'must protect the promise chain from being canceled from the outside with waiting for it to complete': async () => { 1023 | let atomicChainCompleted = false; 1024 | const chain = CPromise.delay(200, 1) 1025 | .then(v => CPromise.delay(200, v + 2)) 1026 | .then(result => { 1027 | atomicChainCompleted = true; 1028 | assert.strictEqual(result, 3); 1029 | }, err => { 1030 | assert.fail(`Unexpected rejecting: ${err}`); 1031 | }) 1032 | .atomic('await') 1033 | .then(v => CPromise.delay(1000)) 1034 | .then(v => { 1035 | assert.fail('Unexpected resolving'); 1036 | }, err => { 1037 | assert.ok(atomicChainCompleted === true); 1038 | assert.ok(err instanceof CanceledError); 1039 | assert.strictEqual(err.message, 'test'); 1040 | }) 1041 | 1042 | setTimeout(() => { 1043 | chain.cancel('test'); 1044 | }, 50); 1045 | 1046 | return chain; 1047 | } 1048 | }, 1049 | }, 1050 | 1051 | 'CPromise.retry': { 1052 | 'should reject if all retries failed': async () => { 1053 | let counter = 0; 1054 | return CPromise.retry(function* () { 1055 | counter++; 1056 | yield CPromise.delay(100); 1057 | throw Error('test'); 1058 | }, {retries: 3, delay: 100}).then(() => { 1059 | assert.fail('was not rejected'); 1060 | }, err => { 1061 | assert.strictEqual(err.message, 'test'); 1062 | assert.strictEqual(counter, 3); 1063 | }) 1064 | }, 1065 | 'should resolve if some attempt was successful': async () => { 1066 | let counter = 0; 1067 | return CPromise.retry(function* () { 1068 | counter++; 1069 | yield CPromise.delay(100); 1070 | if (counter < 2) { 1071 | throw Error('test'); 1072 | } 1073 | 1074 | return 123; 1075 | }, {retries: 3, delay: 100}).then((value) => { 1076 | assert.strictEqual(counter, 2); 1077 | assert.strictEqual(value, 123); 1078 | }) 1079 | } 1080 | }, 1081 | 1082 | 'CPromise.delay': { 1083 | 'progress capturing' : async()=>{ 1084 | let index= 0; 1085 | const time= 1800; 1086 | const tick= 500; 1087 | const calc= (index)=> (index * tick) / time; 1088 | const arr= [calc(1), calc(2), calc(3), 1]; 1089 | 1090 | return new Promise((resolve, reject)=>{ 1091 | CPromise.run(function*(){ 1092 | yield CPromise.delay(time, undefined, {progressTick: tick}); 1093 | }).progress(v=> { 1094 | try{ 1095 | assert.ok(Math.abs(v- arr[index])<0.1, `${v}!=${arr[index]} (${arr})`); 1096 | index++; 1097 | }catch(err){ 1098 | reject(err); 1099 | } 1100 | }).then(()=>{ 1101 | assert.strictEqual(index, 4); 1102 | }).then(resolve, reject); 1103 | }); 1104 | } 1105 | }, 1106 | 1107 | 'CPromise.run': { 1108 | 'it should support progress capturing': async function () { 1109 | this.timeout(5000) 1110 | 1111 | const assertProgress = valuesList(0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1); 1112 | 1113 | const fn = CPromise.promisify(function* () { 1114 | this.innerWeight(0); 1115 | const max = 10; 1116 | 1117 | for (let i = 0; i < max; i++) { 1118 | this.progress(i / max); 1119 | yield CPromise.delay(300); 1120 | } 1121 | 1122 | }); 1123 | 1124 | return CPromise.run(function* () { 1125 | this.innerWeight(2); 1126 | yield CPromise.delay(500); 1127 | yield fn(); 1128 | }).progress(assertProgress); 1129 | } 1130 | }, 1131 | 1132 | 'signals': { 1133 | 'onSignal': async function () { 1134 | let signalEmitted = false; 1135 | const promise = CPromise.delay(1000).onSignal('test', (data) => { 1136 | assert.deepStrictEqual(data, {x: 1}); 1137 | signalEmitted = true; 1138 | }).then(() => { 1139 | assert.ok(signalEmitted, 'signal was not emitted'); 1140 | }); 1141 | 1142 | await CPromise.delay(500); 1143 | 1144 | promise.emitSignal('test', {x: 1}); 1145 | 1146 | return promise; 1147 | } 1148 | } 1149 | }; 1150 | -------------------------------------------------------------------------------- /test/tests/CanceledError.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const {CPromise, E_REASON_TIMEOUT, E_REASON_DISPOSED, E_REASON_CANCELED} = require('../../lib/c-promise'); 3 | const {CanceledError} = CPromise; 4 | 5 | module.exports = { 6 | 'CanceledError.throw': { 7 | 'should throw the error that is an instance of CanceledError': function () { 8 | const originalErr = new CanceledError(); 9 | try { 10 | CanceledError.rethrow(originalErr); 11 | } catch (err) { 12 | assert.strictEqual(err, originalErr); 13 | } 14 | }, 15 | 16 | 'should throw the error only if some error code matched': function () { 17 | const originalErr = new CanceledError(E_REASON_TIMEOUT); 18 | try { 19 | CanceledError.rethrow(originalErr, E_REASON_DISPOSED); 20 | } catch (err) { 21 | assert.fail('should not throw'); 22 | } 23 | 24 | try { 25 | CanceledError.rethrow(originalErr, E_REASON_TIMEOUT); 26 | } catch (err) { 27 | assert.strictEqual(err, originalErr); 28 | } 29 | } 30 | }, 31 | 32 | 'CanceledError.isCanceledError': { 33 | 'should return true if the object is CanceledError': ()=>{ 34 | assert.strictEqual(CanceledError.isCanceledError(new CanceledError()), true); 35 | }, 36 | 37 | 'should return true if error codes matched': ()=>{ 38 | assert.strictEqual( 39 | CanceledError.isCanceledError(CanceledError.from(E_REASON_DISPOSED), E_REASON_DISPOSED), 40 | true 41 | ); 42 | }, 43 | 44 | "should return true if error codes doesn't match": ()=>{ 45 | assert.strictEqual( 46 | CanceledError.isCanceledError(CanceledError.from(E_REASON_DISPOSED), E_REASON_CANCELED), 47 | false 48 | ); 49 | }, 50 | } 51 | } 52 | 53 | --------------------------------------------------------------------------------