Repository: petkaantonov/bluebird Branch: master Commit: c220cfe48086 Files: 369 Total size: 1.7 MB Directory structure: gitextract_5o3u1a2h/ ├── .editorconfig ├── .gitignore ├── .istanbul.yml ├── .jshintignore ├── .jshintrc ├── .travis.yml ├── API.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bench ├── benchmark/ │ ├── README.md │ ├── analysis/ │ │ ├── promises-bluebird-parallel.js │ │ └── promises-bluebird.js │ ├── doxbee-sequential/ │ │ ├── callbacks-baseline.js │ │ ├── callbacks-caolan-async-waterfall.js │ │ ├── callbacks-suguru03-neo-async-waterfall.js │ │ ├── generators-tj-co.js │ │ ├── observables-Reactive-Extensions-RxJS.js │ │ ├── observables-baconjs-bacon.js.js │ │ ├── observables-caolan-highland.js │ │ ├── observables-pozadi-kefir.js │ │ ├── promises-bluebird-generator.js │ │ ├── promises-bluebird.js │ │ ├── promises-calvinmetcalf-lie.js │ │ ├── promises-cujojs-when.js │ │ ├── promises-dfilatov-vow.js │ │ ├── promises-ecmascript6-native.js │ │ ├── promises-kriskowal-q.js │ │ ├── promises-lvivski-davy.js │ │ ├── promises-medikoo-deferred.js │ │ ├── promises-native-async-await.js │ │ ├── promises-obvious-kew.js │ │ ├── promises-then-promise.js │ │ ├── promises-tildeio-rsvp.js │ │ ├── streamline-callbacks.js │ │ ├── streamline-generators.js │ │ └── streamline._js │ ├── doxbee-sequential-errors/ │ │ ├── callbacks-baseline.js │ │ ├── callbacks-caolan-async-waterfall.js │ │ ├── callbacks-suguru03-neo-async-waterfall.js │ │ ├── promises-bluebird-generator.js │ │ ├── promises-bluebird.js │ │ ├── promises-calvinmetcalf-lie.js │ │ ├── promises-cujojs-when.js │ │ ├── promises-dfilatov-vow.js │ │ ├── promises-kriskowal-q.js │ │ ├── promises-lvivski-davy.js │ │ ├── promises-medikoo-deferred.js │ │ ├── promises-obvious-kew.js │ │ ├── promises-then-promise.js │ │ ├── promises-tildeio-rsvp.js │ │ ├── streamline-callbacks.js │ │ ├── streamline-generators.js │ │ └── streamline._js │ ├── lib/ │ │ ├── catcher.js │ │ ├── dummy.js │ │ ├── fakemaker.js │ │ ├── fakes-ctx.js │ │ ├── fakes.js │ │ ├── fakesC.js │ │ ├── fakesO.js │ │ ├── fakesObservable.js │ │ ├── fakesP-ctx.js │ │ ├── fakesP.js │ │ ├── fakesSJS-dst.js │ │ ├── fakesSJS-src.sjs │ │ ├── promiseSupport.js │ │ └── timers-ctx.js │ ├── madeup-parallel/ │ │ ├── callbacks-baseline.js │ │ ├── callbacks-caolan-async-parallel.js │ │ ├── callbacks-suguru03-neo-async-parallel.js │ │ ├── generators-tj-co.js │ │ ├── promises-bluebird-generator.js │ │ ├── promises-bluebird.js │ │ ├── promises-calvinmetcalf-lie.js │ │ ├── promises-cujojs-when.js │ │ ├── promises-dfilatov-vow.js │ │ ├── promises-ecmascript6-native.js │ │ ├── promises-lvivski-davy.js │ │ ├── promises-medikoo-deferred.js │ │ ├── promises-native-async-await.js │ │ ├── promises-obvious-kew.js │ │ ├── promises-then-promise.js │ │ ├── promises-tildeio-rsvp.js │ │ ├── streamline-callbacks.js │ │ ├── streamline-generators.js │ │ └── streamline._js │ ├── package.json │ └── performance.js ├── bower.json ├── build ├── changelog.md ├── deprecated_apis.md ├── docs/ │ ├── .gitignore │ ├── Gemfile │ ├── README.md │ ├── _config.yml │ ├── _layouts/ │ │ ├── api.html │ │ ├── default.html │ │ └── page.html │ ├── _plugins/ │ │ ├── mdate.rb │ │ └── plugin.rb │ ├── css/ │ │ ├── main.css │ │ └── mono-blue.css │ ├── docs/ │ │ ├── anti-patterns.md │ │ ├── api/ │ │ │ ├── aggregateerror.md │ │ │ ├── all.md │ │ │ ├── any.md │ │ │ ├── ascallback.md │ │ │ ├── bind.md │ │ │ ├── built-in-error-types.md │ │ │ ├── call.md │ │ │ ├── cancel.md │ │ │ ├── cancellation.md │ │ │ ├── cancellationerror.md │ │ │ ├── catch.md │ │ │ ├── catchreturn.md │ │ │ ├── catchthrow.md │ │ │ ├── collections.md │ │ │ ├── core.md │ │ │ ├── deferred-migration.md │ │ │ ├── delay.md │ │ │ ├── disposer.md │ │ │ ├── done.md │ │ │ ├── each.md │ │ │ ├── environment-variables.md │ │ │ ├── error-management-configuration.md │ │ │ ├── error.md │ │ │ ├── filter.md │ │ │ ├── finally.md │ │ │ ├── generators.md │ │ │ ├── get.md │ │ │ ├── iscancelled.md │ │ │ ├── isfulfilled.md │ │ │ ├── ispending.md │ │ │ ├── isrejected.md │ │ │ ├── map.md │ │ │ ├── mapseries.md │ │ │ ├── new-promise.md │ │ │ ├── operationalerror.md │ │ │ ├── progression-migration.md │ │ │ ├── promise.all.md │ │ │ ├── promise.any.md │ │ │ ├── promise.bind.md │ │ │ ├── promise.config.md │ │ │ ├── promise.coroutine.addyieldhandler.md │ │ │ ├── promise.coroutine.md │ │ │ ├── promise.delay.md │ │ │ ├── promise.each.md │ │ │ ├── promise.filter.md │ │ │ ├── promise.fromcallback.md │ │ │ ├── promise.getnewlibrarycopy.md │ │ │ ├── promise.join.md │ │ │ ├── promise.longstacktraces.md │ │ │ ├── promise.map.md │ │ │ ├── promise.mapseries.md │ │ │ ├── promise.method.md │ │ │ ├── promise.noconflict.md │ │ │ ├── promise.onpossiblyunhandledrejection.md │ │ │ ├── promise.onunhandledrejectionhandled.md │ │ │ ├── promise.promisify.md │ │ │ ├── promise.promisifyall.md │ │ │ ├── promise.props.md │ │ │ ├── promise.race.md │ │ │ ├── promise.reduce.md │ │ │ ├── promise.reject.md │ │ │ ├── promise.resolve.md │ │ │ ├── promise.setscheduler.md │ │ │ ├── promise.some.md │ │ │ ├── promise.try.md │ │ │ ├── promise.using.md │ │ │ ├── promiseinspection.md │ │ │ ├── promisification.md │ │ │ ├── props.md │ │ │ ├── race.md │ │ │ ├── reason.md │ │ │ ├── reduce.md │ │ │ ├── reflect.md │ │ │ ├── resource-management.md │ │ │ ├── return.md │ │ │ ├── some.md │ │ │ ├── spread.md │ │ │ ├── suppressunhandledrejections.md │ │ │ ├── synchronous-inspection.md │ │ │ ├── tap.md │ │ │ ├── tapcatch.md │ │ │ ├── then.md │ │ │ ├── throw.md │ │ │ ├── timeout.md │ │ │ ├── timeouterror.md │ │ │ ├── timers.md │ │ │ ├── utility.md │ │ │ └── value.md │ │ ├── api-reference.md │ │ ├── async-dialogs.md │ │ ├── beginners-guide.md │ │ ├── benchmarks.md │ │ ├── changelog.md │ │ ├── coming-from-other-languages.md │ │ ├── coming-from-other-libraries.md │ │ ├── contribute.md │ │ ├── deprecated-apis.md │ │ ├── deprecated_apis.md │ │ ├── download-api-reference.md │ │ ├── error-explanations.md │ │ ├── features.md │ │ ├── getting-started.md │ │ ├── install.md │ │ ├── new-in-bluebird-3.md │ │ ├── support.md │ │ ├── warning-explanations.md │ │ ├── what-about-generators.md │ │ ├── why-bluebird.md │ │ ├── why-performance.md │ │ ├── why-promises.md │ │ └── working-with-callbacks.md │ ├── helpers.rb │ ├── img/ │ │ └── README.txt │ └── index.html ├── issue_template.md ├── package.json ├── src/ │ ├── any.js │ ├── assert.js │ ├── async.js │ ├── bind.js │ ├── bluebird.js │ ├── call_get.js │ ├── cancel.js │ ├── catch_filter.js │ ├── constants.js │ ├── context.js │ ├── debuggability.js │ ├── direct_resolve.js │ ├── each.js │ ├── errors.js │ ├── es5.js │ ├── filter.js │ ├── finally.js │ ├── generators.js │ ├── join.js │ ├── map.js │ ├── method.js │ ├── nodeback.js │ ├── nodeify.js │ ├── promise.js │ ├── promise_array.js │ ├── promisify.js │ ├── props.js │ ├── queue.js │ ├── race.js │ ├── reduce.js │ ├── schedule.js │ ├── settle.js │ ├── some.js │ ├── synchronous_inspection.js │ ├── thenables.js │ ├── timers.js │ ├── using.js │ └── util.js ├── test/ │ ├── browser/ │ │ ├── es5-sham.js │ │ ├── es5-shim.js │ │ ├── have_getters.js │ │ ├── index.html │ │ ├── json.js │ │ ├── main.js │ │ ├── mocha.css │ │ ├── mocha.js │ │ ├── package.json │ │ ├── promise_debug.js │ │ ├── promise_instrumented.js │ │ └── worker.js │ └── mocha/ │ ├── 2.1.2.js │ ├── 2.1.3.js │ ├── 2.2.1.js │ ├── 2.2.2.js │ ├── 2.2.3.js │ ├── 2.2.4.js │ ├── 2.2.5.js │ ├── 2.2.6.js │ ├── 2.2.7.js │ ├── 2.3.1.js │ ├── 2.3.2.js │ ├── 2.3.3.js │ ├── 2.3.4.js │ ├── 3.2.1.js │ ├── 3.2.2.js │ ├── 3.2.3.js │ ├── 3.2.4.js │ ├── 3.2.5.js │ ├── 3.2.6.js │ ├── any.js │ ├── api_exceptions.js │ ├── async.js │ ├── async_hooks.js │ ├── bind.js │ ├── bluebird-multiple-instances.js │ ├── call.js │ ├── cancel.js │ ├── catch_filter.js │ ├── collections_thenables.js │ ├── constructor.js │ ├── cycles.js │ ├── direct_resolving.js │ ├── domain.js │ ├── done.js │ ├── each.js │ ├── error.js │ ├── filter.js │ ├── finally.js │ ├── following.js │ ├── generator.js │ ├── get.js │ ├── getNewLibraryCopy.js │ ├── github-2xx-76.js │ ├── github-3.6.4.js │ ├── github-3.7.3.js │ ├── github-4.1.7.js │ ├── github36.js │ ├── helpers/ │ │ ├── assert_long_trace.js │ │ ├── bluebird0_7_0.js │ │ ├── error.js │ │ ├── reasons.js │ │ ├── testThreeCases.js │ │ ├── thenables.js │ │ └── util.js │ ├── is.js │ ├── join.js │ ├── late_buffer_safety.js │ ├── long_stack_traces.js │ ├── map.js │ ├── method.js │ ├── monitoring.js │ ├── multiple-copies.js │ ├── no_conflict.js │ ├── nodeify.js │ ├── promise_array.js │ ├── promisify.js │ ├── props.js │ ├── race.js │ ├── reduce.js │ ├── reflect.js │ ├── regress.js │ ├── rejections.js │ ├── resolution.js │ ├── schedule.js │ ├── settle.js │ ├── some.js │ ├── spread.js │ ├── synchronous_inspection.js │ ├── tap.js │ ├── tapCatch.js │ ├── timers.js │ ├── try.js │ ├── unhandled_rejections.js │ └── using.js ├── tests └── tools/ ├── README.md ├── ast_passes.js ├── browser_test_generator.js ├── browser_test_runner.js ├── build.js ├── job-runner/ │ └── job-runner.js ├── jshint.js ├── jshintrc_generator.js ├── mocha_runner.js ├── saucelabs_runner.js ├── test.js └── utils.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # This file is for unifying the coding style for different editors and IDEs # editorconfig.org root = true [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [**.js] indent_style = space indent_size = 4 ================================================ FILE: .gitignore ================================================ docs/Gemfile.lock gh-pages node_modules gh-pages test/browser/bundle.js test/browser/worker_bundle.js js/* zalgo.js coverage/* .vscode .idea .jekyll-cache/* _site ================================================ FILE: .istanbul.yml ================================================ verbose: false instrumentation: root: . default-excludes: true excludes: [] embed-source: false variable: __coverage__ compact: true preserve-comments: false complete-copy: false save-baseline: false baseline-file: ./coverage/coverage-baseline.json include-all-sources: false reporting: print: summary reports: - lcov dir: ./coverage watermarks: statements: [50, 80] lines: [50, 80] functions: [50, 80] branches: [50, 80] report-config: clover: {file: clover.xml} cobertura: {file: cobertura-coverage.xml} json: {file: coverage-final.json} json-summary: {file: coverage-summary.json} lcovonly: {file: lcov.info} teamcity: {file: null} text: {file: null, maxCols: 0} text-summary: {file: null} hooks: hook-run-in-context: false post-require-hook: null handle-sigint: false check: global: statements: 0 lines: 0 branches: 0 functions: 0 excludes: [] each: statements: 0 lines: 0 branches: 0 functions: 0 excludes: [] ================================================ FILE: .jshintignore ================================================ src/constants.js ================================================ FILE: .jshintrc ================================================ { "bitwise": false, "camelcase": true, "curly": true, "eqeqeq": true, "es3": true, "forin": true, "immed": true, "latedef": false, "newcap": true, "noarg": true, "noempty": true, "nonew": true, "plusplus": false, "quotmark": "double", "undef": true, "unused": true, "strict": false, "maxparams": 6, "maxlen": 80, "asi": false, "boss": true, "eqnull": true, "evil": true, "expr": false, "funcscope": false, "globalstrict": false, "lastsemic": false, "laxcomma": false, "laxbreak": false, "loopfunc": true, "multistr": true, "proto": false, "scripturl": true, "shadow": true, "sub": true, "supernew": false, "validthis": true, "browser": true, "jquery": true, "devel": true, "-W014": true, "-W116": true, "-W106": true, "-W064": true, "-W097": true, "globals": { "Symbol": false, "Map": false, "JSON": false, "Error": true, "args": true, "chrome": true, "INLINE_SLICE": false, "INLINE_SLICE_LEFT_PADDED": false, "BIT_FIELD_CHECK": false, "BIT_FIELD_READ": false, "USE": false, "global": true, "setImmediate": true, "Promise": true, "WebKitMutationObserver": true, "TypeError": true, "RangeError": true, "__DEBUG__": false, "__BROWSER__": false, "process": true, "self": true, "console": false, "require": false, "module": false, "define": false, "LATE_QUEUE_CAPACITY": false, "NORMAL_QUEUE_CAPACITY": false, "ERROR_HANDLED_KEY": false, "OPERATIONAL_ERROR_KEY": false, "DEFAULT_STATE": false, "STACK_ATTACHED": false, "ERROR_HANDLED": false, "GENERATED_CLASS_COUNT": false, "USE_BOUND": false, "DONT_USE_BOUND": false, "PROPAGATE_CANCEL": false, "PROPAGATE_BIND": false, "PROPAGATE_ALL": false, "CALLBACK_FULFILL_OFFSET": false, "CALLBACK_REJECT_OFFSET": false, "CALLBACK_PROMISE_OFFSET": false, "CALLBACK_RECEIVER_OFFSET": false, "CALLBACK_SIZE": false, "ASYNC_GUARANTEE_SHIFT": false, "NO_STATE": false, "NO_ASYNC_GUARANTEE": false, "RETURNED_NON_UNDEFINED": false, "IS_ASYNC_GUARANTEED": false, "IS_FOLLOWING": false, "IS_FULFILLED": false, "IS_REJECTED": false, "WILL_BE_CANCELLED": false, "IS_FINAL": false, "IS_BOUND": false, "IS_REJECTION_UNHANDLED": false, "IS_REJECTION_IGNORED": false, "IS_UNHANDLED_REJECTION_NOTIFIED": false, "IS_DISPOSABLE": false, "IS_CANCELLED": false, "IS_CANCELLED_OR_WILL_BE_CANCELLED": false, "LENGTH_MASK": false, "LENGTH_CLEAR_MASK": false, "MAX_LENGTH": false, "IS_REJECTED_OR_CANCELLED": false, "IS_REJECTED_OR_FULFILLED": false, "IS_REJECTED_OR_FULFILLED_OR_CANCELLED": false, "IS_PENDING_AND_WAITING_NEG": false, "IS_FATE_SEALED": false, "AFTER_PROMISIFIED_SUFFIX": false, "UNHANDLED_REJECTION_EVENT": false, "REJECTION_HANDLED_EVENT": false, "RESOLVE_UNDEFINED": false, "RESOLVE_ARRAY": false, "RESOLVE_OBJECT": false, "RESOLVE_FOREVER_PENDING": false, "RESOLVE_CALL_METHOD": false, "RESOLVE_MAP": false, "QUEUE_MAX_CAPACITY": false, "QUEUE_MIN_CAPACITY": false, "FROM_PREVIOUS_EVENT": false, "NO_STACK_TRACE": false, "ADDITIONAL_STACK_TRACE": false, "UNHANDLED_REJECTION_HEADER": false, "FINALLY_TYPE": false, "TAP_TYPE": false, "THROW": false, "RETURN": false, "MAX_PARAM_COUNT": false, "PARAM_COUNTS_TO_TRY": false, "BLUEBIRD_ERRORS": false, "OBJECT_PROMISIFY_DEPRECATED": false, "SPAWN_DEPRECATED": false, "LATE_CANCELLATION_OBSERVER": false, "TIMEOUT_ERROR": false, "COLLECTION_ERROR": false, "OBJECT_ERROR": false, "FUNCTION_ERROR": false, "CONSTRUCT_ERROR_INVOCATION": false, "NOT_GENERATOR_ERROR": false, "LONG_STACK_TRACES_ERROR": false, "INSPECTION_VALUE_ERROR": false, "INSPECTION_REASON_ERROR": false, "PROMISIFY_TYPE_ERROR": false, "CIRCULAR_RESOLUTION_ERROR": false, "PROPS_TYPE_ERROR": false, "POSITIVE_INTEGER_ERROR": false, "YIELDED_NON_PROMISE_ERROR": false, "FROM_COROUTINE_CREATED_AT": false, "UNBOUND_RESOLVER_INVOCATION": false, "PROMISIFICATION_NORMAL_METHODS_ERROR": false, "SUFFIX_NOT_IDENTIFIER": false, "NO_ASYNC_SCHEDULER": false } } ================================================ FILE: .travis.yml ================================================ language: node_js sudo: false matrix: include: - node_js: "0.10" - node_js: "0.12" - node_js: "4" - node_js: "5" - node_js: "6" - node_js: "7" - node_js: "8" - node_js: "10" fast_finish: true git: depth: 5 env: - "NODE_FLAGS='--expose-gc' SCRIPT_FLAGS=''" before_script: - git submodule update --init --recursive script: "node $NODE_FLAGS tools/test.js $SCRIPT_FLAGS" branches: only: - master cache: directories: - "$HOME/.npm" ================================================ FILE: API.md ================================================ [http://bluebirdjs.com/docs/api-reference.html](http://bluebirdjs.com/docs/api-reference.html) ================================================ FILE: CONTRIBUTING.md ================================================ # Questions and issues Please see [The Support Page](http://bluebirdjs.com/docs/support.html) The [github issue tracker](https://github.com/petkaantonov/bluebird/issues) is **_only_** for bug reports and feature requests. # Contributing to the library Contributions are welcome and appreciated. See the [Contribution Page](http://bluebirdjs.com/docs/contribute.html) on bluebirdjs.com ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013-2018 Petka Antonov 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ Promises/A+ logo [![Build Status](https://travis-ci.org/petkaantonov/bluebird.svg?branch=master)](https://travis-ci.org/petkaantonov/bluebird) [![coverage-98%](https://img.shields.io/badge/coverage-98%25-brightgreen.svg?style=flat)](http://petkaantonov.github.io/bluebird/coverage/debug/index.html) **Got a question?** Join us on [stackoverflow](http://stackoverflow.com/questions/tagged/bluebird), the [mailing list](https://groups.google.com/forum/#!forum/bluebird-js) or chat on [IRC](https://webchat.freenode.net/?channels=#promises) # Introduction Bluebird is a fully featured promise library with focus on innovative features and performance See the [**bluebird website**](http://bluebirdjs.com/docs/getting-started.html) for further documentation, references and instructions. See the [**API reference**](http://bluebirdjs.com/docs/api-reference.html) here. For bluebird 2.x documentation and files, see the [2.x tree](https://github.com/petkaantonov/bluebird/tree/2.x). ## ⚠️Note⚠️ **Please use native promises instead if at all possible**. Native Promises have been stable in Node.js and browsers for around 10 years now and they have been fast for around 7. Any utility bluebird has like `.map` has native equivalents (like Node streams' `.map`). This is a good thing, the people working on Bluebird and promises have been able to help incorporate most of the useful things from Bluebird into JavaScript itself and platforms/engines. If there is a feature that keeps you using bluebird. Please let us know so we can try and upstream it :) Currently - it is only recommended to use Bluebird if you need to support _really old_ browsers or EoL Node.js or as an intermediate step to use warnings/monitoring to find bugs. # Questions and issues The [github issue tracker](https://github.com/petkaantonov/bluebird/issues) is **_only_** for bug reports and feature requests. Anything else, such as questions for help in using the library, should be posted in [StackOverflow](http://stackoverflow.com/questions/tagged/bluebird) under tags `promise` and `bluebird`. ## Thanks Thanks to BrowserStack for providing us with a free account which lets us support old browsers like IE8. # License The MIT License (MIT) Copyright (c) 2013-2021 Petka Antonov 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: bench ================================================ #!/usr/bin/env bash ./build --release --no-debug benchmark=$1 nodepath=${2:-node} shift 2; cwd=${PWD} export NODE_ENV=production trap 'cd "$cwd"' EXIT if [ "$benchmark" = "doxbee" ]; then cd "$cwd/benchmark" npm install echo "Doxbee sequential" $nodepath performance.js --n 10000 --t 1 ./doxbee-sequential/*.js --harmony "$@" exit 0 elif [ "$benchmark" = "doxbee-errors" ]; then cd "$cwd/benchmark" npm install echo "Doxbee sequential with 10% errors" $nodepath performance.js --n 10000 --t 1 --e 0.1 ./doxbee-sequential-errors/*.js --harmony "$@" exit 0 elif [ "$benchmark" = "parallel" ]; then cd "$cwd/benchmark" npm install echo "Madeup parallel" $nodepath performance.js --n 10000 --t 1 --p 25 ./madeup-parallel/*.js --harmony "$@" exit 0 elif [ "$benchmark" = "analysis" ]; then cd "$cwd/benchmark" npm install echo "analysis" $nodepath performance.js --n 10000 --t 1 ./analysis/*.js --harmony "$@" exit 0 else echo "Invalid benchmark name $benchmark" exit -1 fi ================================================ FILE: benchmark/README.md ================================================ **2018-07-16** Latest results, using latest versions of modules: ├── async@2.6.1 ├── davy@1.3.0 ├── deferred@0.7.9 ├── kew@0.7.0 ├── lie@3.3.0 ├── optimist@0.6.1 ├── promise@8.0.1 ├── q@1.5.1 ├── rsvp@4.8.3 ├── vow@0.4.17 ├── when@3.7.8 bench doxbee-sequential `ls ./doxbee-sequential/*.js | sed -e 's|\.js||' | xargs node ./performance.js --p 1 --t 1 --n 10000` file time(ms) memory(MB) callbacks-baseline 162 28.12 callbacks-suguru03-neo-async-waterfall 195 42.39 promises-bluebird-generator 199 40.23 callbacks-caolan-async-waterfall 225 46.36 promises-native-async-await 245 57.39 promises-bluebird 257 47.03 promises-lvivski-davy 313 87.59 promises-cujojs-when 318 64.34 promises-then-promise 323 64.49 generators-tj-co 334 58.03 promises-ecmascript6-native 335 65.40 promises-tildeio-rsvp 420 86.79 promises-calvinmetcalf-lie 514 138.58 promises-dfilatov-vow 629 135.28 promises-obvious-kew 693 190.43 streamline-generators 762 90.18 promises-medikoo-deferred 781 149.33 observables-pozadi-kefir 824 180.54 streamline-callbacks 1088 114.73 observables-Reactive-Extensions-RxJS 1208 243.74 observables-caolan-highland 3094 424.63 promises-kriskowal-q 3505 367.13 observables-baconjs-bacon.js 5224 660.07 Platform info: Darwin 17.7.0 x64 Node.JS 10.6.0 V8 6.7.288.46-node.13 Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz × 4 bench parallel (`--p 25`) results for 10000 parallel executions, 1 ms per I/O op `ls ./madeup-parallel/*.js | sed -e 's|\.js||' | xargs node ./performance.js --p 25 --t 1 --n 10000` results for 10000 parallel executions, 1 ms per I/O op file time(ms) memory(MB) callbacks-baseline 309 74.47 callbacks-suguru03-neo-async-parallel 374 84.18 promises-bluebird-generator 455 106.49 promises-bluebird 472 98.00 callbacks-caolan-async-parallel 510 119.34 promises-lvivski-davy 671 163.84 promises-cujojs-when 701 168.99 promises-native-async-await 1087 242.02 promises-tildeio-rsvp 1237 344.17 promises-calvinmetcalf-lie 1401 370.65 promises-ecmascript6-native 1509 242.91 promises-then-promise 1533 303.89 promises-medikoo-deferred 1923 334.75 promises-dfilatov-vow 2534 534.80 promises-obvious-kew 2623 306.68 Platform info: Darwin 17.7.0 x64 Node.JS 10.6.0 V8 6.7.288.46-node.13 Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz × 4 ================================================ FILE: benchmark/analysis/promises-bluebird-parallel.js ================================================ global.useBluebird = true; global.useQ = false; global.parallelQueries = 25; var Promise = require('../../js/release/bluebird.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } Promise.all(queries).then().then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); } ================================================ FILE: benchmark/analysis/promises-bluebird.js ================================================ global.useBluebird = true; global.useQ = false; var bluebird = require('../../js/release/bluebird.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; bluebird.join(blobIdP, fileP, function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/callbacks-baseline.js ================================================ require('../lib/fakes'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); function backoff(err) { tx.rollback(); return done(err); } blob.put(stream, function (err, blobId) { if (err) return done(err); self.byUuidOrPath(idOrPath).get(function (err, file) { if (err) return done(err); var previousId = file ? file.version : null; var version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); Version.insert(version).execWithin(tx, function (err) { if (err) return backoff(err); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }, function (err, q) { if (err) return backoff(err); q.execWithin(tx, function (err) { afterFileExists(err, newId); }); }) } else return afterFileExists(null, file.id); }); function afterFileExists(err, fileId) { if (err) return backoff(err); FileVersion.insert({fileId: fileId,versionId: version.id}) .execWithin(tx, function (err) { if (err) return backoff(err); File.whereUpdate({id: fileId}, { version: version.id }).execWithin(tx, function (err) { if (err) return backoff(err); tx.commit(done); }); }) } }); }); } ================================================ FILE: benchmark/doxbee-sequential/callbacks-caolan-async-waterfall.js ================================================ require('../lib/fakes'); var async = require('async'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobId, file, version, fileId; async.waterfall([ function writeBlob(callback) { blob.put(stream, callback); }, function afterBlobWritten(callback) { blobId = undefined // iBlobId; self.byUuidOrPath(idOrPath).get(callback); }, function afterFileFetched(callback) { file = undefined; //iFile; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, mergedId: null, mergeType: 'mine', comment: '', tag: tag }; version.id = Version.createHash(version); Version.insert(version).execWithin(tx, callback); }, function afterVersionInserted(callback) { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, type: 'file', name: fileName, version: version.id }, function (err, q) { if (err) return backoff(err); q.execWithin(tx, function (err) { callback(err, newId); }); }) } else return callback(null, file.id); }, function afterFileExists(iFileId, callback) { fileId = iFileId; FileVersion.insert({fileId: fileId, versionId: version.id}) .execWithin(tx, callback); }, function afterFileVersionInserted(callback) { File.whereUpdate({id: fileId}, { version: version.id }) .execWithin(tx, callback); }, function afterFileUpdated(callback) { tx.commit(callback); } ], function (err) { if (err) tx.rollback(); done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/callbacks-suguru03-neo-async-waterfall.js ================================================ require('../lib/fakes'); var async = require('neo-async'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobId, file, version, fileId; async.waterfall([ function writeBlob(callback) { blob.put(stream, callback); }, function afterBlobWritten(callback) { blobId = undefined // iBlobId; self.byUuidOrPath(idOrPath).get(callback); }, function afterFileFetched(callback) { file = undefined; //iFile; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, mergedId: null, mergeType: 'mine', comment: '', tag: tag }; version.id = Version.createHash(version); Version.insert(version).execWithin(tx, callback); }, function afterVersionInserted(callback) { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, type: 'file', name: fileName, version: version.id }, function (err, q) { if (err) return backoff(err); q.execWithin(tx, function (err) { callback(err, newId); }); }) } else return callback(null, file.id); }, function afterFileExists(iFileId, callback) { fileId = iFileId; FileVersion.insert({fileId: fileId, versionId: version.id}) .execWithin(tx, callback); }, function afterFileVersionInserted(callback) { File.whereUpdate({id: fileId}, { version: version.id }) .execWithin(tx, callback); }, function afterFileUpdated(callback) { tx.commit(callback); } ], function (err) { if (err) tx.rollback(); done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/generators-tj-co.js ================================================ global.useNative = true; try { if (Promise.race.toString() !== 'function race() { [native code] }') throw 0; } catch (e) { throw new Error("No ES6 promises available"); } var co = require("co"); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { co(function* () { try { var blob = blobManager.create(account); var tx = db.begin(); var blobId = yield blob.put(stream); var file = yield self.byUuidOrPath(idOrPath).get(); var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); yield Version.insert(version).execWithin(tx); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id } var query = yield self.createQuery(idOrPath, file); yield query.execWithin(tx); } yield FileVersion.insert({fileId: file.id, versionId: version.id}) .execWithin(tx); yield File.whereUpdate({id: file.id}, {version: version.id}) .execWithin(tx); tx.commit(); done(); } catch (err) { tx.rollback(); done(err); } }); }; ================================================ FILE: benchmark/doxbee-sequential/observables-Reactive-Extensions-RxJS.js ================================================ global.useRx = true; var Rx = require('rx'); require('../lib/fakesObservable'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; Rx.Observable.forkJoin(blobIdP, fileP).flatMap(function(v) { file = v[1]; var blobId = v[0]; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).flatMap(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).flatMap(function(q) { return q.execWithin(tx); }).map(function() { return newId; }); } else { return Rx.Observable.return(file.id); } }).flatMap(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).flatMap(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).subscribe(function() { }, function() { tx.rollback(); done(err); }, function() { tx.commit(); done(); }); } ================================================ FILE: benchmark/doxbee-sequential/observables-baconjs-bacon.js.js ================================================ global.useBacon = true; var Bacon = require('baconjs').Bacon; require('../lib/fakesObservable'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; var stream = Bacon.combineAsArray(blobIdP, fileP).flatMap(function(v) { file = v[1]; var blobId = v[0]; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).flatMap(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).flatMap(function(q) { return q.execWithin(tx); }).map(function() { return newId; }); } else { return Bacon.constant(file.id); } }).flatMap(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).flatMap(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }); stream.onError(function() { tx.rollback(); done(err); }); stream.onValue(function() { tx.commit(); done(); }); } ================================================ FILE: benchmark/doxbee-sequential/observables-caolan-highland.js ================================================ global.useHighland = true; var _ = require("highland"); require('../lib/fakesObservable'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; _([blobIdP, fileP]).merge().apply(function(blobId, file) { var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); Version.insert(version).execWithin(tx).flatMap(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).flatMap(function(q) { return q.execWithin(tx); }).map(function() { return newId; }); } else { return _([file.id]); } }).flatMap(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).flatMap(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).stopOnError(function(err) { tx.rollback(); done(err); }).apply(function(v) { tx.commit(); done(); }); }); } ================================================ FILE: benchmark/doxbee-sequential/observables-pozadi-kefir.js ================================================ global.useKefir = true; var Kefir = require('kefir').Kefir; require('../lib/fakesObservable'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; var stream = Kefir.zip([blobIdP, fileP]).flatMap(function(v) { file = v[1]; var blobId = v[0]; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).flatMap(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).flatMap(function(q) { return q.execWithin(tx); }).map(function() { return newId; }); } else { return Kefir.constant(file.id); } }).flatMap(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).flatMap(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }); stream.onError(function() { tx.rollback(); done(err); }); stream.onValue(function() { tx.commit(); done(); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-bluebird-generator.js ================================================ global.useBluebird = true; global.useQ = false; var bluebird = require('../../js/release/bluebird.js'); require('../lib/fakesP'); module.exports = bluebird.coroutine(function* upload(stream, idOrPath, tag, done) { try { var blob = blobManager.create(account); var tx = db.begin(); var blobId = yield blob.put(stream); var file = yield self.byUuidOrPath(idOrPath).get(); var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); yield Version.insert(version).execWithin(tx); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id } var query = yield self.createQuery(idOrPath, file); yield query.execWithin(tx); } yield FileVersion.insert({fileId: file.id, versionId: version.id}) .execWithin(tx); yield File.whereUpdate({id: file.id}, {version: version.id}) .execWithin(tx); tx.commit(); done(); } catch (err) { tx.rollback(); done(err); } }); ================================================ FILE: benchmark/doxbee-sequential/promises-bluebird.js ================================================ global.useBluebird = true; global.useQ = false; var bluebird = require('../../js/release/bluebird.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; bluebird.join(blobIdP, fileP, function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-calvinmetcalf-lie.js ================================================ global.useLie = true; var promise = require("lie"); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; promise.all([blobIdP, fileP]).then(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-cujojs-when.js ================================================ var when = require('when'), fn = require('when/function'), p = require('../lib/promiseSupport.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; when([blobIdP, fileP]).spread(function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-dfilatov-vow.js ================================================ var vow = require('vow'), p = require('../lib/promiseSupport.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; vow.all([blobIdP, fileP]).spread(function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-ecmascript6-native.js ================================================ global.useNative = true; try { if (Promise.race.toString() !== 'function race() { [native code] }') throw 0; } catch (e) { throw new Error("No ES6 promises available"); } require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; Promise.all([blobIdP, fileP]).then(function(result) { var blobId = result[0]; var fileV = result[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-kriskowal-q.js ================================================ global.useQ = true; var q = require('q'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; q.spread([blobIdP, fileP], function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-lvivski-davy.js ================================================ global.useDavy = true; var davy = require('davy'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; davy.all([blobIdP, fileP]).spread(function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-medikoo-deferred.js ================================================ global.useDeferred = true; var deferred = require('deferred'); require('../lib/fakesP'); function identity(v) { return v; } module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; //Couldn't find .all in docs, this seems closest deferred.map([blobIdP, fileP], identity)(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); })(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id })(function(q) { return q.execWithin(tx); })(function() { return newId; }); } else { return file.id; } })(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); })(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); })(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-native-async-await.js ================================================ global.useNative = true; try { if (Promise.race.toString() !== 'function race() { [native code] }') throw 0; } catch (e) { throw new Error("No ES6 promises available"); } require('../lib/fakesP'); module.exports = async function upload(stream, idOrPath, tag, done) { try { var blob = blobManager.create(account); var tx = db.begin(); var blobId = await blob.put(stream); var file = await self.byUuidOrPath(idOrPath).get(); var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); await Version.insert(version).execWithin(tx); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id } var query = await self.createQuery(idOrPath, file); await query.execWithin(tx); } await FileVersion.insert({fileId: file.id, versionId: version.id}) .execWithin(tx); await File.whereUpdate({id: file.id}, {version: version.id}) .execWithin(tx); tx.commit(); done(); } catch (err) { tx.rollback(); done(err); } }; ================================================ FILE: benchmark/doxbee-sequential/promises-obvious-kew.js ================================================ global.useKew = true; var q = require('kew'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; q.all([blobIdP, fileP]).then(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-then-promise.js ================================================ global.useThenPromise = true; var promise = require("promise"); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; promise.all([blobIdP, fileP]).then(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/promises-tildeio-rsvp.js ================================================ global.useRSVP = true; var rsvp = require('rsvp'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; rsvp.all([blobIdP, fileP]).then(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential/streamline-callbacks.js ================================================ 'use strict'; var regeneratorRuntime = typeof require === 'function' ? require('streamline-runtime/lib/callbacks/regenerator') : Streamline.require('streamline-runtime/lib/callbacks/regenerator'); var _streamline = typeof require === 'function' ? require('streamline-runtime/lib/callbacks/runtime') : Streamline.require('streamline-runtime/lib/callbacks/runtime'); var _filename = '/Users/bruno/dev/bluebird/benchmark/doxbee-sequential/streamline._js'; require('../lib/fakes'); module.exports = _streamline.async(regeneratorRuntime.mark(function _$$upload$$(stream, idOrPath, tag, _2) { var blob, tx, blobId, file, previousId, version, splitPath, fileName, query; return regeneratorRuntime.wrap(function _$$upload$$$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.prev = 0; blob = blobManager.create(account); tx = db.begin(); _context.next = 5; return _streamline.await(_filename, 7, blob, 'put', 1, null, false, [stream, true]); case 5: blobId = _context.sent; _context.next = 8; return _streamline.await(_filename, 8, self.byUuidOrPath(idOrPath), 'get', 0, null, false, [true]); case 8: file = _context.sent; previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId }; version.id = Version.createHash(version); _context.next = 14; return _streamline.await(_filename, 19, Version.insert(version), 'execWithin', 1, null, false, [tx, true]); case 14: if (file) { _context.next = 23; break; } splitPath = idOrPath.split('/'); fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id }; _context.next = 20; return _streamline.await(_filename, 29, self, 'createQuery', 2, null, false, [idOrPath, file, true]); case 20: query = _context.sent; _context.next = 23; return _streamline.await(_filename, 30, query, 'execWithin', 1, null, false, [tx, true]); case 23: _context.next = 25; return _streamline.await(_filename, 32, FileVersion.insert({ fileId: file.id, versionId: version.id }), 'execWithin', 1, null, false, [tx, true]); case 25: _context.next = 27; return _streamline.await(_filename, 34, File.whereUpdate({ id: file.id }, { version: version.id }), 'execWithin', 1, null, false, [tx, true]); case 27: tx.commit(); _context.next = 34; break; case 30: _context.prev = 30; _context.t0 = _context['catch'](0); tx.rollback(); throw _context.t0; case 34: case 'end': return _context.stop(); } } }, _$$upload$$, this, [[0, 30]]); }), 3, 4); ================================================ FILE: benchmark/doxbee-sequential/streamline-generators.js ================================================ 'use strict'; var _streamline = typeof require === 'function' ? require('streamline-runtime/lib/generators/runtime') : Streamline.require('streamline-runtime/lib/generators/runtime'); var _filename = '/Users/bruno/dev/bluebird/benchmark/doxbee-sequential/streamline._js'; require('../lib/fakes'); module.exports = _streamline.async(function* _$$upload$$(stream, idOrPath, tag, _2) { { try { var blob = blobManager.create(account); var tx = db.begin(); var blobId = yield _streamline.await(_filename, 7, blob, 'put', 1, null, false, [stream, true]); var file = yield _streamline.await(_filename, 8, self.byUuidOrPath(idOrPath), 'get', 0, null, false, [true]); var previousId = file ? file.version : null; var version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId }; version.id = Version.createHash(version); yield _streamline.await(_filename, 19, Version.insert(version), 'execWithin', 1, null, false, [tx, true]); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id }; var query = yield _streamline.await(_filename, 29, self, 'createQuery', 2, null, false, [idOrPath, file, true]); yield _streamline.await(_filename, 30, query, 'execWithin', 1, null, false, [tx, true]); } yield _streamline.await(_filename, 32, FileVersion.insert({ fileId: file.id, versionId: version.id }), 'execWithin', 1, null, false, [tx, true]); yield _streamline.await(_filename, 34, File.whereUpdate({ id: file.id }, { version: version.id }), 'execWithin', 1, null, false, [tx, true]); tx.commit(); } catch (err) { tx.rollback(); throw err; } } }, 3, 4); ================================================ FILE: benchmark/doxbee-sequential/streamline._js ================================================ require('../lib/fakes'); module.exports = function upload(stream, idOrPath, tag, _) { try { var blob = blobManager.create(account); var tx = db.begin(); var blobId = blob.put(stream, _); var file = self.byUuidOrPath(idOrPath).get(_); var previousId = file ? file.version : null; var version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); Version.insert(version).execWithin(tx, _); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id } var query = self.createQuery(idOrPath, file, _); query.execWithin(tx, _); } FileVersion.insert({fileId: file.id, versionId: version.id}) .execWithin(tx, _); File.whereUpdate({id: file.id}, {version: version.id}) .execWithin(tx, _); tx.commit(); } catch (err) { tx.rollback(); throw err; } } ================================================ FILE: benchmark/doxbee-sequential-errors/callbacks-baseline.js ================================================ require('../lib/fakes'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); function backoff(err) { tx.rollback(); return done(err); } var intentionalErrorShouldBeTriggered = function(){ return LIKELIHOOD_OF_REJECTION && Math.random() <= LIKELIHOOD_OF_REJECTION; } blob.put(stream, function (err, blobId) { if (err) return done(err); self.byUuidOrPath(idOrPath).get(function (err, file) { if (err) return done(err); var previousId = file ? file.version : null; var version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); Version.insert(version).execWithin(tx, function (err) { if (err) return backoff(err); if(intentionalErrorShouldBeTriggered()) return done(new Error("intentional failure")); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }, function (err, q) { if (err) return backoff(err); if(intentionalErrorShouldBeTriggered()) return done(new Error("intentional failure")); q.execWithin(tx, function (err) { afterFileExists(err, newId); }); }) } else return afterFileExists(null, file.id); }); function afterFileExists(err, fileId) { if (err) return backoff(err); if(intentionalErrorShouldBeTriggered()) return done(new Error("intentional failure")); FileVersion.insert({fileId: fileId,versionId: version.id}) .execWithin(tx, function (err) { if (err) return backoff(err); if(intentionalErrorShouldBeTriggered()) return done(new Error("intentional failure")); File.whereUpdate({id: fileId}, { version: version.id }).execWithin(tx, function (err) { if (err) return backoff(err); if(intentionalErrorShouldBeTriggered()) return done(new Error("intentional failure")); tx.commit(done); }); }) } }); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/callbacks-caolan-async-waterfall.js ================================================ require('../lib/fakes'); var async = require('async'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobId, file, version, fileId; async.waterfall([ function writeBlob(callback) { blob.put(stream, callback); }, function afterBlobWritten(callback) { blobId = undefined // iBlobId; self.byUuidOrPath(idOrPath).get(callback); }, function afterFileFetched(callback) { file = undefined; //iFile; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, mergedId: null, mergeType: 'mine', comment: '', tag: tag }; version.id = Version.createHash(version); Version.insert(version).execWithin(tx, callback); }, function afterVersionInserted(callback) { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, type: 'file', name: fileName, version: version.id }, function (err, q) { if (err) return backoff(err); q.execWithin(tx, function (err) { callback(err, newId); }); }) } else return callback(null, file.id); }, function afterFileExists(iFileId, callback) { fileId = iFileId; FileVersion.insert({fileId: fileId, versionId: version.id}) .execWithin(tx, callback); }, function afterFileVersionInserted(callback) { File.whereUpdate({id: fileId}, { version: version.id }) .execWithin(tx, callback); }, function afterFileUpdated(callback) { tx.commit(callback); } ], function (err) { if (err) tx.rollback(); done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/callbacks-suguru03-neo-async-waterfall.js ================================================ require('../lib/fakes'); var async = require('neo-async'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobId, file, version, fileId; async.waterfall([ function writeBlob(callback) { blob.put(stream, callback); }, function afterBlobWritten(callback) { blobId = undefined // iBlobId; self.byUuidOrPath(idOrPath).get(callback); }, function afterFileFetched(callback) { file = undefined; //iFile; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, mergedId: null, mergeType: 'mine', comment: '', tag: tag }; version.id = Version.createHash(version); Version.insert(version).execWithin(tx, callback); }, function afterVersionInserted(callback) { if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, type: 'file', name: fileName, version: version.id }, function (err, q) { if (err) return backoff(err); q.execWithin(tx, function (err) { callback(err, newId); }); }) } else return callback(null, file.id); }, function afterFileExists(iFileId, callback) { fileId = iFileId; FileVersion.insert({fileId: fileId, versionId: version.id}) .execWithin(tx, callback); }, function afterFileVersionInserted(callback) { File.whereUpdate({id: fileId}, { version: version.id }) .execWithin(tx, callback); }, function afterFileUpdated(callback) { tx.commit(callback); } ], function (err) { if (err) tx.rollback(); done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-bluebird-generator.js ================================================ global.useBluebird = true; global.useQ = false; var bluebird = require('../../js/release/bluebird.js'); require('../lib/fakesP'); module.exports = bluebird.coroutine(function* upload(stream, idOrPath, tag, done) { try { var blob = blobManager.create(account); var tx = db.begin(); var blobId = yield blob.put(stream); var file = yield self.byUuidOrPath(idOrPath).get(); var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); yield Version.insert(version).execWithin(tx); triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id } var query = yield self.createQuery(idOrPath, file); yield query.execWithin(tx); triggerIntentionalError(); } yield FileVersion.insert({fileId: file.id, versionId: version.id}) .execWithin(tx); triggerIntentionalError(); yield File.whereUpdate({id: file.id}, {version: version.id}) .execWithin(tx); triggerIntentionalError(); tx.commit(); done(); } catch (err) { tx.rollback(); done(err); } }); ================================================ FILE: benchmark/doxbee-sequential-errors/promises-bluebird.js ================================================ global.useBluebird = true; global.useQ = false; var bluebird = require('../../js/release/bluebird.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; bluebird.all([blobIdP, fileP]).spread(function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { triggerIntentionalError(); tx.commit(); return done(); }).then(null, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-calvinmetcalf-lie.js ================================================ global.useLie = true; var promise = require("lie"); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; promise.all([blobIdP, fileP]).then(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { triggerIntentionalError(); tx.commit(); return done(); }).then(null, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-cujojs-when.js ================================================ var when = require('when'), fn = require('when/function'), p = require('../lib/promiseSupport.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; when([blobIdP, fileP]).spread(function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { triggerIntentionalError(); tx.commit(); return done(); }).then(null, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-dfilatov-vow.js ================================================ var vow = require('vow'), p = require('../lib/promiseSupport.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; vow.all([blobIdP, fileP]).spread(function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { triggerIntentionalError(); tx.commit(); return done(); }).then(null, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-kriskowal-q.js ================================================ global.useQ = true; var q = require('q'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; q.spread([blobIdP, fileP], function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { triggerIntentionalError(); tx.commit(); return done(); }).then(null, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-lvivski-davy.js ================================================ global.useDavy = true; var davy = require('davy'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; davy.all([blobIdP, fileP]).spread(function(blobId, fileV) { file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQuery(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { triggerIntentionalError(); tx.commit(); return done(); }).then(null, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-medikoo-deferred.js ================================================ global.useDeferred = true; var deferred = require('deferred'); require('../lib/fakesP'); function identity(v) { return v; } module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; //Couldn't find .all in docs, this seems closest deferred.map([blobIdP, fileP], identity)(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); })(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id })(function(q) { return q.execWithin(tx); })(function() { return newId; }); } else { return file.id; } })(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); })(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); })(function() { triggerIntentionalError(); tx.commit(); return done(); }, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-obvious-kew.js ================================================ global.useKew = true; var q = require('kew'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; q.all([blobIdP, fileP]).then(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { triggerIntentionalError(); tx.commit(); return done(); }).then(null, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-then-promise.js ================================================ global.useThenPromise = true; var promise = require("promise"); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; promise.all([blobIdP, fileP]).then(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { triggerIntentionalError(); tx.commit(); return done(); }).then(null, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/promises-tildeio-rsvp.js ================================================ global.useRSVP = true; var rsvp = require('rsvp'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var blob = blobManager.create(account); var tx = db.begin(); var blobIdP = blob.put(stream); var fileP = self.byUuidOrPath(idOrPath).get(); var version, fileId, file; rsvp.all([blobIdP, fileP]).then(function(all) { var blobId = all[0], fileV = all[1]; file = fileV; var previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); return Version.insert(version).execWithin(tx); }).then(function() { triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; var newId = uuid.v1(); return self.createQueryCtxless(idOrPath, { id: newId, userAccountId: userAccount.id, name: fileName, version: version.id }).then(function(q) { return q.execWithin(tx); }).then(function() { return newId; }); } else { return file.id; } }).then(function(fileIdV) { triggerIntentionalError(); fileId = fileIdV; return FileVersion.insert({ fileId: fileId, versionId: version.id }).execWithin(tx); }).then(function() { triggerIntentionalError(); return File.whereUpdate({id: fileId}, {version: version.id}) .execWithin(tx); }).then(function() { triggerIntentionalError(); tx.commit(); return done(); }).then(null, function(err) { tx.rollback(); return done(err); }); } ================================================ FILE: benchmark/doxbee-sequential-errors/streamline-callbacks.js ================================================ 'use strict'; var regeneratorRuntime = typeof require === 'function' ? require('streamline-runtime/lib/callbacks/regenerator') : Streamline.require('streamline-runtime/lib/callbacks/regenerator'); var _streamline = typeof require === 'function' ? require('streamline-runtime/lib/callbacks/runtime') : Streamline.require('streamline-runtime/lib/callbacks/runtime'); var _filename = '/Users/bruno/dev/bluebird/benchmark/doxbee-sequential-errors/streamline._js'; require('../lib/fakes'); module.exports = _streamline.async(regeneratorRuntime.mark(function _$$upload$$(stream, idOrPath, tag, _2) { var blob, tx, blobId, file, previousId, version, splitPath, fileName, query; return regeneratorRuntime.wrap(function _$$upload$$$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.prev = 0; blob = blobManager.create(account); tx = db.begin(); _context.next = 5; return _streamline.await(_filename, 7, blob, 'put', 1, null, false, [stream, true]); case 5: blobId = _context.sent; _context.next = 8; return _streamline.await(_filename, 8, self.byUuidOrPath(idOrPath), 'get', 0, null, false, [true]); case 8: file = _context.sent; previousId = file ? file.version : null; version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId }; version.id = Version.createHash(version); _context.next = 14; return _streamline.await(_filename, 19, Version.insert(version), 'execWithin', 1, null, false, [tx, true]); case 14: triggerIntentionalError(); if (file) { _context.next = 25; break; } splitPath = idOrPath.split('/'); fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id }; _context.next = 21; return _streamline.await(_filename, 30, self, 'createQuery', 2, null, false, [idOrPath, file, true]); case 21: query = _context.sent; _context.next = 24; return _streamline.await(_filename, 31, query, 'execWithin', 1, null, false, [tx, true]); case 24: triggerIntentionalError(); case 25: _context.next = 27; return _streamline.await(_filename, 34, FileVersion.insert({ fileId: file.id, versionId: version.id }), 'execWithin', 1, null, false, [tx, true]); case 27: triggerIntentionalError(); _context.next = 30; return _streamline.await(_filename, 37, File.whereUpdate({ id: file.id }, { version: version.id }), 'execWithin', 1, null, false, [tx, true]); case 30: triggerIntentionalError(); tx.commit(); _context.next = 38; break; case 34: _context.prev = 34; _context.t0 = _context['catch'](0); tx.rollback(); throw _context.t0; case 38: case 'end': return _context.stop(); } } }, _$$upload$$, this, [[0, 34]]); }), 3, 4); ================================================ FILE: benchmark/doxbee-sequential-errors/streamline-generators.js ================================================ 'use strict'; var _streamline = typeof require === 'function' ? require('streamline-runtime/lib/generators/runtime') : Streamline.require('streamline-runtime/lib/generators/runtime'); var _filename = '/Users/bruno/dev/bluebird/benchmark/doxbee-sequential-errors/streamline._js'; require('../lib/fakes'); module.exports = _streamline.async(function* _$$upload$$(stream, idOrPath, tag, _2) { { try { var blob = blobManager.create(account); var tx = db.begin(); var blobId = yield _streamline.await(_filename, 7, blob, 'put', 1, null, false, [stream, true]); var file = yield _streamline.await(_filename, 8, self.byUuidOrPath(idOrPath), 'get', 0, null, false, [true]); var previousId = file ? file.version : null; var version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId }; version.id = Version.createHash(version); yield _streamline.await(_filename, 19, Version.insert(version), 'execWithin', 1, null, false, [tx, true]); triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id }; var query = yield _streamline.await(_filename, 30, self, 'createQuery', 2, null, false, [idOrPath, file, true]); yield _streamline.await(_filename, 31, query, 'execWithin', 1, null, false, [tx, true]); triggerIntentionalError(); } yield _streamline.await(_filename, 34, FileVersion.insert({ fileId: file.id, versionId: version.id }), 'execWithin', 1, null, false, [tx, true]); triggerIntentionalError(); yield _streamline.await(_filename, 37, File.whereUpdate({ id: file.id }, { version: version.id }), 'execWithin', 1, null, false, [tx, true]); triggerIntentionalError(); tx.commit(); } catch (err) { tx.rollback(); throw err; } } }, 3, 4); ================================================ FILE: benchmark/doxbee-sequential-errors/streamline._js ================================================ require('../lib/fakes'); module.exports = function upload(stream, idOrPath, tag, _) { try { var blob = blobManager.create(account); var tx = db.begin(); var blobId = blob.put(stream, _); var file = self.byUuidOrPath(idOrPath).get(_); var previousId = file ? file.version : null; var version = { userAccountId: userAccount.id, date: new Date(), blobId: blobId, creatorId: userAccount.id, previousId: previousId, }; version.id = Version.createHash(version); Version.insert(version).execWithin(tx, _); triggerIntentionalError(); if (!file) { var splitPath = idOrPath.split('/'); var fileName = splitPath[splitPath.length - 1]; file = { id: uuid.v1(), userAccountId: userAccount.id, name: fileName, version: version.id } var query = self.createQuery(idOrPath, file, _); query.execWithin(tx, _); triggerIntentionalError(); } FileVersion.insert({fileId: file.id, versionId: version.id}) .execWithin(tx, _); triggerIntentionalError(); File.whereUpdate({id: file.id}, {version: version.id}) .execWithin(tx, _); triggerIntentionalError(); tx.commit(); } catch (err) { tx.rollback(); throw err; } }; ================================================ FILE: benchmark/lib/catcher.js ================================================ exports.longStackSupport = global.longStackSupport; function invoke(ctx, cb, value, myhandler) { try { cb.call(ctx, value); // no error } catch (e) { if (myhandler) myhandler.call(ctx, e); else console.error(e); } } module.exports = function() { var self = {}; var notCaught = true, myhandler; self.try = function $try(cb) { if (exports.longStackSupport) { var ex = {}; Error.captureStackTrace(ex); } return function wrapper(err, value) { if (err) { if (notCaught) { notCaught = false; if (err.stack && ex) { var asyncStackRaw = ex.stack.substr(ex.stack.indexOf('\n')); err.stack += '\nFrom previous event:' + asyncStackRaw; } if (myhandler) myhandler(err); else console.error(err); } } else if (myhandler) invoke(this, cb, value, myhandler); else cb(value); } } self.catch = function $catch(handler) { myhandler = handler }; return self; }; ================================================ FILE: benchmark/lib/dummy.js ================================================ // A typical node callback function // with the callback at the Nth position exports.dummy = function dummy(n) { return function dummy_n() { var cb = arguments[n - 1] || function(){}; if (global.asyncTime) setTimeout(cb, global.asyncTime || 100); else process.nextTick(cb); } } // A throwing callback function exports.dummyt = function dummyt(n) { return function dummy_throwing_n() { var cb = arguments[n - 1]; if (global.testThrow) throw(new Error("Exception happened")); setTimeout(function throwTimeout() { if (global.testThrowAsync) { throw(new Error("Exception happened")); } else if (global.testError) { return cb(new Error("Error happened")); } else cb(); }, global.asyncTime || 100); } } ================================================ FILE: benchmark/lib/fakemaker.js ================================================ module.exports = function fakemaker(dummy, dummyt, wrap) { var dummy_2 = dummy(2), dummy_1 = dummy(1); var dummyt_2, dummyt_1; if (global.testError || global.testThrow || global.testThrowAsync) { dummyt_2 = dummyt(2); dummyt_1 = dummyt(1); } else { dummyt_2 = dummy_2; dummyt_1 = dummy_1; } // a queryish object with all // kinds of functions function queryish() { return { execWithin: dummy_2, exec: dummy_1, get: dummy_1, all: dummy_1, }; } // a queryish object with functions // that throw function queryisht() { return { execWithin: dummyt_2, exec: dummyt_1, get: dummyt_1, all: dummyt_1, }; } global.uuid = { v1: function v1() {} }; global.userAccount = { }; global.account = { }; global.blobManager = { create: function create() { return { put: dummy_2, } } }; var cqQueryish = queryish(); global.self = { byUuidOrPath: queryish, createQuery: wrap(function createQuery(x, y, cb, ctx) { cb.call(ctx, null, cqQueryish); }), createQueryCtxless: wrap(function createQuery(x, y, cb) { cb.call(this, null, cqQueryish); }) }; global.File = { insert: queryish, whereUpdate: queryish }; global.FileVersion = { insert: queryisht }; global.Version = { createHash: function createHash(v) { return 1; }, insert: queryish }; global.db = { begin: function begin() { return { commit: dummy_1, rollback: dummy_1, }; } }; }; ================================================ FILE: benchmark/lib/fakes-ctx.js ================================================ var timers = require('./timers-ctx'); var fakemaker = require('./fakemaker'); var f = {}; f.dummy = function dummy(n) { return function dummy_n() { var cb = arguments[n - 1], ctx = arguments[n]; //console.log(cb, ctx); timers.setTimeout(cb, ctx, global.asyncTime || 100); } } // A throwing callback function f.dummyt = function dummyt(n) { return function dummy_throwing_n() { var cb = arguments[n - 1], ctx = arguments[n]; if (global.testThrow) throw(new Error("Exception happened")); setTimeout(function throwTimeout() { if (global.testThrowAsync) { throw(new Error("Exception happened")); } else if (global.testError) { return cb.call(ctx, new Error("Error happened")); } else cb.call(ctx); }, global.asyncTime || 100); } } fakemaker(f.dummy, f.dummyt, function wrap_identity(f) { return f; }); ================================================ FILE: benchmark/lib/fakes.js ================================================ var fakemaker = require('./fakemaker'), f = require('./dummy'); fakemaker(f.dummy, f.dummyt, function wrap_identity(f) { return f; }); ================================================ FILE: benchmark/lib/fakesC.js ================================================ var co = require('co'); var f = require('./dummy'); var makefakes = require('./fakemaker'); // Continuable versions made with co.wrap function dummyC(n) { return co.wrap(f.dummy(n)); } function dummytC(n) { return co.wrap(f.dummyt(n)); } makefakes(dummyC, dummytC, co.wrap); ================================================ FILE: benchmark/lib/fakesO.js ================================================ var Rx = require('rx'); var f = require('./dummy'); var dummy1 = f.dummy(1), dummyt1 = f.dummyt(1); // Observable wrapper function dummyObsWrap(fn) { return function() { return Rx.Observable.create(function(observer) { fn(function(err, res) { if(err) return observer.onError(err); observer.onNext(res); observer.onCompleted(); }); }); } } function dummyO() { return dummyObsWrap(dummy(1)); } function dummytO() { return dummyObsWrap(dummyt(1)); } makefakes(dummyO, dummytO, dummyObsWrap); ================================================ FILE: benchmark/lib/fakesObservable.js ================================================ var lifter, fromNodeCallback; if (global.useRx) { lifter = require("rx").Observable.fromNodeCallback; } else if (global.useBacon) { fromNodeCallback = require("baconjs").fromNodeCallback; } else if (global.useKefir) { fromNodeCallback = require("kefir").Kefir.fromNodeCallback; lifter = function(nodeFn) { return function() { var args = [].slice.call(arguments); function thunk(callback) { args.push(callback); nodeFn.apply(null, args); args.pop(); } return fromNodeCallback(thunk); } }; } else if (global.useHighland) { lifter = require("highland").wrapCallback; } if (!lifter) { lifter = function(nodeFn) { return function() { var args = [].slice.call(arguments); args.unshift(nodeFn); return fromNodeCallback.apply(undefined, args); }; }; } var f = require('./dummy'); var makefakes = require('./fakemaker'); // A function taking n values or promises and returning // a promise function dummyP(n) { return lifter(f.dummy(n)); } // Throwing version of above function dummytP(n) { return lifter(f.dummyt(n)); } makefakes(dummyP, dummytP, lifter); ================================================ FILE: benchmark/lib/fakesP-ctx.js ================================================ var timers = require('./timers-ctx'); var fakemaker = require('./fakemaker'); var f = {}; f.dummy = function dummy(n) { return function dummy_n() { var cb = arguments[n - 1], ctx = arguments[n]; timers.setTimeout(cb, ctx, global.asyncTime || 100); } } // A throwing callback function f.dummyt = function dummyt(n) { return function dummy_throwing_n() { var cb = arguments[n - 1], ctx = arguments[n]; if (global.testThrow) throw(new Error("Exception happened")); setTimeout(function throwTimeout() { if (global.testThrowAsync) { throw(new Error("Exception happened")); } else if (global.testError) { return cb.call(ctx, new Error("Error happened")); } else cb.call(ctx); }, global.asyncTime || 100); } } //Currently promisifies only Node style callbacks //var lifter = require('bluebird').promisify; var Promise = require('bluebird'); function nodeback(err, result) { if (err == null) this.fulfill(result); else this.reject(err); } function lifter(f) { return function lifted(a1, a2, a3, a4, a5) { "use strict"; var len = arguments.length; var deferred = Promise.pending(); try { switch(len) { case 1: f(a1, nodeback, deferred); break; case 2: f(a1, a2, nodeback, deferred); break; case 0: f(nodeback, deferred); break; case 3: f(a1, a2, a3, nodeback, deferred); break; case 4: f(a1, a2, a3, a4, nodeback, deferred); break; case 5: f(a1, a2, a3, a4, a5, nodeback, deferred); break; } } catch (err) { deferred.reject(err); } return deferred.promise; } } // A function taking n values or promises and returning // a promise function dummyP(n) { return function lifted() { var deferred = Promise.pending(); timers.setTimeout(nodeback, deferred, global.asyncTime || 100); return deferred.promise; } } // Throwing version of above function dummytP(n) { return lifter(f.dummyt(n)); } fakemaker(dummyP, dummytP, lifter); ================================================ FILE: benchmark/lib/fakesP.js ================================================ if (global.useQ) var lifter = require('q').denodeify; else if (global.useBluebird) //Currently promisifies only Node style callbacks var lifter = require('../../js/release/bluebird.js').promisify; else if (global.useKew) { var q = require('kew'); var slicer = [].slice; var lifter = function lifter(nodefn) { return function() { var p = q.defer(); arguments[arguments.length++] = function(err, res) { if (err) p.reject(err); else p.resolve(res) }; try { nodefn.apply(this, arguments); } catch (e) { p.reject(e); } return p; } } } else if(global.useLie) { var Lie = require('lie'); var lifter = function(nodefn) { return function() { var self = this; var l = arguments.length; var args = new Array(l + 1); for (var i = 0; i < l; ++i) { args[i] = arguments[i]; } return new Lie(function(resolve, reject) { args[l] = function(err, val) { if (err) reject(err); else resolve(val); }; nodefn.apply(self, args); }); }; }; } else if(global.useThenPromise) { var lifter = require("promise").denodeify; } else if( global.useRSVP ) { var lifter = require("rsvp").denodeify; } else if( global.useDeferred) { var lifter = require("deferred").promisify; } else if( global.useDavy) { var lifter = require("davy").wrap; } else if (global.useNative) { try { if (Promise.race.toString() !== 'function race() { [native code] }') throw 0; } catch (e) { throw new Error("No ES6 promises available"); } var lifter = function(nodefn) { return function() { var self = this; var l = arguments.length; var args = new Array(l + 1); for (var i = 0; i < l; ++i) { args[i] = arguments[i]; } return new Promise(function(resolve, reject) { args[l] = function(err, val) { if (err) reject(err); else resolve(val); }; nodefn.apply(self, args); }); }; }; } else { var lifter = require('when/node').lift; } var f = require('./dummy'); var makefakes = require('./fakemaker'); // A function taking n values or promises and returning // a promise function dummyP(n) { return lifter(f.dummy(n)); } // Throwing version of above function dummytP(n) { return lifter(f.dummyt(n)); } makefakes(dummyP, dummytP, lifter); ================================================ FILE: benchmark/lib/fakesSJS-dst.js ================================================ var f,makefakes;function wrap(f){return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Nb(function(){return __oni_rt.Return(function (x,y){var err,val;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Suspend(function(__oni_env,resume){return __oni_rt.ex(__oni_rt.Nb(function(){return f(x,y,resume);},8),__oni_env)}, function() {err=arguments[0];val=arguments[1];}),__oni_rt.Nb(function(){if(err){throw err;}return __oni_rt.Return(val);},10)])});},12)])}function dummySJS0(){var inner;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Nb(function(){inner=f.dummy(1);return __oni_rt.Return(function (){var err,val;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Suspend(function(__oni_env,resume){return __oni_rt.ex(__oni_rt.Nb(function(){return inner(resume);},19),__oni_env)}, function() {err=arguments[0];val=arguments[1];}),__oni_rt.Nb(function(){if(err){throw err;}return __oni_rt.Return(val);},21)])});},17)])}function dummySJS1(){var inner;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Nb(function(){inner=f.dummy(2);return __oni_rt.Return(function (x){var err,val;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Suspend(function(__oni_env,resume){return __oni_rt.ex(__oni_rt.Nb(function(){return inner(x,resume);},30),__oni_env)}, function() {err=arguments[0];val=arguments[1];}),__oni_rt.Nb(function(){if(err){throw err;}return __oni_rt.Return(val);},32)])});},28)])}function dummySJS(n){if(n === 1){return dummySJS0();}if(n === 2){return dummySJS1();}}function dummytSJS(n){var inner;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Nb(function(){inner=f.dummyt(n);return __oni_rt.Return(function (){var args,err,val;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Suspend(function(__oni_env,resume){return __oni_rt.ex(__oni_rt.Nb(function(){args=Array.prototype.slice.apply(this.aobj);args.push(resume);inner.apply(this,args);},0),__oni_env)}, function() {err=arguments[0];val=arguments[1];}),__oni_rt.Nb(function(){if(err){throw err;}return __oni_rt.Return(val);},51)])});},44)])}__oni_rt.exseq(this.arguments,this,'lib/fakesSJS-src.sjs',[24,__oni_rt.Sc(3,function(_oniX){return f=_oniX;},__oni_rt.C(function(){return require('./dummy.js')},1)),__oni_rt.Sc(5,function(_oniX){return makefakes=_oniX;},__oni_rt.C(function(){return require('./fakemaker.js')},3)),__oni_rt.Nb(function(){return makefakes(dummySJS,dummytSJS,wrap,global);},56)]) ================================================ FILE: benchmark/lib/fakesSJS-src.sjs ================================================ var f = require('./dummy.js'); var makefakes = require('./fakemaker.js'); function wrap(f) { return function(x, y) { waitfor(var err, val) { f(x, y, resume); } if (err) throw err; return val; }; } function dummySJS0() { var inner = f.dummy(1); return function() { waitfor (var err, val) { inner(resume); } if (err) throw err; return val; } } function dummySJS1() { var inner = f.dummy(2); return function(x) { waitfor (var err, val) { inner(x, resume); } if (err) throw err; return val; } } function dummySJS(n) { if (n === 1) return dummySJS0(); if (n === 2) return dummySJS1(); } function dummytSJS(n) { var inner = f.dummyt(n); return function() { waitfor(var err, val) { var args = Array.prototype.slice.apply(arguments); args.push(resume); inner.apply(this, args); } if (err) throw err; return val; } } makefakes(dummySJS, dummytSJS, wrap, global); ================================================ FILE: benchmark/lib/promiseSupport.js ================================================ var when = require('when'); var fn = require('when/function'); exports.ternary = fn.lift(function(truthy, iftrue, iffalse) { return truthy ? iftrue: iffalse; }) exports.not = function not(truthyP) { return when(truthyP).then(function(truthyVal) { return !truthyVal; }); } exports.allObject = function allObject(objWithPromises) { return when(objWithPromises).then(function(objWithPromises) { var keys = Object.keys(objWithPromises); return when.all(keys.map(function(key) { return objWithPromise; })).then(function(vals) { var init = {}; for (var k = 0; k < keys.length; ++k) { init[keys[k]] = vals[k]; } return init; }); }); } exports.set = fn.lift(function(obj, values) { for (var key in values) obj[key] = values[key]; return obj; }); exports.if = function ifP (truthyP, fnTrue, fnFalse) { return truthyP.then(function(truthy) { if (truthy) return fnTrue(); else return fnFalse(); }); } exports.get = fn.lift(function (obj, key) { return obj[key]; }); exports.eventuallyCall = fn.lift(function(obj, fnkey) { var args = [].slice.call(arguments, 2); obj[fnkey].apply(obj, args); }); ================================================ FILE: benchmark/lib/timers-ctx.js ================================================ // Copyright Joyent, Inc. and other Node contributors. // // 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: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. var Timer = process.binding('timer_wrap').Timer; var L = require('_linklist'); var assert = require('assert').ok; var kOnTimeout = Timer.kOnTimeout | 0; // Timeout values > TIMEOUT_MAX are set to 1. var TIMEOUT_MAX = 2147483647; // 2^31-1 var debug = require('util').debuglog('timer'); // IDLE TIMEOUTS // // Because often many sockets will have the same idle timeout we will not // use one timeout watcher per item. It is too much overhead. Instead // we'll use a single watcher for all sockets with the same timeout value // and a linked list. This technique is described in the libev manual: // http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts // Object containing all lists, timers // key = time in milliseconds // value = list var lists = {}; // the main function - creates lists on demand and the watchers associated // with them. function insert(item, msecs) { item._idleStart = Timer.now(); item._idleTimeout = msecs; if (msecs < 0) return; var list; if (lists[msecs]) { list = lists[msecs]; } else { list = new Timer(); list.start(msecs, 0); L.init(list); lists[msecs] = list; list.msecs = msecs; list[kOnTimeout] = listOnTimeout; } L.append(list, item); assert(!L.isEmpty(list)); // list is not empty } function listOnTimeout() { var msecs = this.msecs; var list = this; debug('timeout callback %d', msecs); var now = Timer.now(); debug('now: %s', now); var first; while (first = L.peek(list)) { var diff = now - first._idleStart; if (diff < msecs) { list.start(msecs - diff, 0); debug('%d list wait because diff is %d', msecs, diff); return; } else { L.remove(first); assert(first !== L.peek(list)); if (!first._onTimeout) continue; // v0.4 compatibility: if the timer callback throws and the // domain or uncaughtException handler ignore the exception, // other timers that expire on this tick should still run. // // https://github.com/joyent/node/issues/2631 var domain = first.domain; if (domain && domain._disposed) continue; try { if (domain) domain.enter(); var threw = true; if (!first._ctx || first._ctx === first) first._onTimeout(); else first._onTimeout.call(first._ctx); if (domain) domain.exit(); threw = false; } finally { if (threw) { process.nextTick(function() { list[kOnTimeout](); }); } } } } debug('%d list empty', msecs); assert(L.isEmpty(list)); list.close(); delete lists[msecs]; } var unenroll = exports.unenroll = function(item) { L.remove(item); var list = lists[item._idleTimeout]; // if empty then stop the watcher debug('unenroll'); if (list && L.isEmpty(list)) { debug('unenroll: list empty'); list.close(); delete lists[item._idleTimeout]; } // if active is called later, then we want to make sure not to insert again item._idleTimeout = -1; }; // Does not start the time, just sets up the members needed. exports.enroll = function(item, msecs) { // if this item was already in a list somewhere // then we should unenroll it from that if (item._idleNext) unenroll(item); // Ensure that msecs fits into signed int32 if (msecs > 0x7fffffff) { msecs = 0x7fffffff; } item._idleTimeout = msecs; L.init(item); }; // call this whenever the item is active (not idle) // it will reset its timeout. exports.active = function(item) { var msecs = item._idleTimeout; if (msecs >= 0) { var list = lists[msecs]; if (!list || L.isEmpty(list)) { insert(item, msecs); } else { item._idleStart = Timer.now(); L.append(list, item); } } }; /* * DOM-style timers */ exports.setTimeout = function(callback, ctx, after) { var timer; after *= 1; // coalesce to number or NaN if (!(after >= 1 && after <= TIMEOUT_MAX)) { after = 1; // schedule on next tick, follows browser behaviour } timer = new Timeout(after); timer._ctx = ctx; timer._onTimeout = callback; if (process.domain) timer.domain = process.domain; exports.active(timer); return timer; }; exports.clearTimeout = function(timer) { if (timer && (timer[kOnTimeout] || timer._onTimeout)) { timer[kOnTimeout] = timer._onTimeout = null; if (timer instanceof Timeout) { timer.close(); // for after === 0 } else { exports.unenroll(timer); } } }; exports.setInterval = function(callback, repeat) { repeat *= 1; // coalesce to number or NaN if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) { repeat = 1; // schedule on next tick, follows browser behaviour } var timer = new Timeout(repeat); var args = Array.prototype.slice.call(arguments, 2); timer._onTimeout = wrapper; timer._repeat = true; if (process.domain) timer.domain = process.domain; exports.active(timer); return timer; function wrapper() { callback.apply(this, args); // If callback called clearInterval(). if (timer._repeat === false) return; // If timer is unref'd (or was - it's permanently removed from the list.) if (this._handle) { this._handle.start(repeat, 0); } else { timer._idleTimeout = repeat; exports.active(timer); } } }; exports.clearInterval = function(timer) { if (timer && timer._repeat) { timer._repeat = false; clearTimeout(timer); } }; var Timeout = function(after) { this._idleTimeout = after; this._idlePrev = this; this._idleNext = this; this._idleStart = null; this._onTimeout = null; this._repeat = false; this._ctx = this; }; Timeout.prototype.unref = function() { if (!this._handle) { var now = Timer.now(); if (!this._idleStart) this._idleStart = now; var delay = this._idleStart + this._idleTimeout - now; if (delay < 0) delay = 0; exports.unenroll(this); this._handle = new Timer(); this._handle[kOnTimeout] = this._onTimeout; this._handle.start(delay, 0); this._handle.domain = this.domain; this._handle.unref(); } else { this._handle.unref(); } }; Timeout.prototype.ref = function() { if (this._handle) this._handle.ref(); }; Timeout.prototype.close = function() { this._onTimeout = this._ctx = null; if (this._handle) { this._handle[kOnTimeout] = null; this._handle.close(); } else { exports.unenroll(this); } }; var immediateQueue = {}; L.init(immediateQueue); function processImmediate() { var queue = immediateQueue; immediateQueue = {}; L.init(immediateQueue); while (L.isEmpty(queue) === false) { var immediate = L.shift(queue); var domain = immediate.domain; if (domain) domain.enter(); immediate._onImmediate(); if (domain) domain.exit(); } // Only round-trip to C++ land if we have to. Calling clearImmediate() on an // immediate that's in |queue| is okay. Worst case is we make a superfluous // call to NeedImmediateCallbackSetter(). if (L.isEmpty(immediateQueue)) { process._needImmediateCallback = false; } } exports.setImmediate = function(callback) { var immediate = {}, args; L.init(immediate); immediate._onImmediate = callback; if (arguments.length > 1) { args = Array.prototype.slice.call(arguments, 1); immediate._onImmediate = function() { callback.apply(immediate, args); }; } if (!process._needImmediateCallback) { process._needImmediateCallback = true; process._immediateCallback = processImmediate; } if (process.domain) immediate.domain = process.domain; L.append(immediateQueue, immediate); return immediate; }; exports.clearImmediate = function(immediate) { if (!immediate) return; immediate._onImmediate = undefined; L.remove(immediate); if (L.isEmpty(immediateQueue)) { process._needImmediateCallback = false; } }; // Internal APIs that need timeouts should use timers._unrefActive isntead of // timers.active as internal timeouts shouldn't hold the loop open var unrefList, unrefTimer; function unrefTimeout() { var now = Timer.now(); debug('unrefTimer fired'); var first; while (first = L.peek(unrefList)) { var diff = now - first._idleStart; if (diff < first._idleTimeout) { diff = first._idleTimeout - diff; unrefTimer.start(diff, 0); unrefTimer.when = now + diff; debug('unrefTimer rescheudling for later'); return; } L.remove(first); var domain = first.domain; if (!first._onTimeout) continue; if (domain && domain._disposed) continue; try { if (domain) domain.enter(); var threw = true; debug('unreftimer firing timeout'); if (!first._ctx || first._ctx === first) first._onTimeout(); else first._onTimeout.call(first._ctx); threw = false; if (domain) domain.exit(); } finally { if (threw) process.nextTick(unrefTimeout); } } debug('unrefList is empty'); unrefTimer.when = -1; } exports._unrefActive = function(item) { var msecs = item._idleTimeout; if (!msecs || msecs < 0) return; assert(msecs >= 0); L.remove(item); if (!unrefList) { debug('unrefList initialized'); unrefList = {}; L.init(unrefList); debug('unrefTimer initialized'); unrefTimer = new Timer(); unrefTimer.unref(); unrefTimer.when = -1; unrefTimer[kOnTimeout] = unrefTimeout; } var now = Timer.now(); item._idleStart = now; if (L.isEmpty(unrefList)) { debug('unrefList empty'); L.append(unrefList, item); unrefTimer.start(msecs, 0); unrefTimer.when = now + msecs; debug('unrefTimer scheduled'); return; } var when = now + msecs; debug('unrefList find where we can insert'); var cur, them; for (cur = unrefList._idlePrev; cur != unrefList; cur = cur._idlePrev) { them = cur._idleStart + cur._idleTimeout; if (when < them) { debug('unrefList inserting into middle of list'); L.append(cur, item); if (unrefTimer.when > when) { debug('unrefTimer is scheduled to fire too late, reschedule'); unrefTimer.start(msecs, 0); unrefTimer.when = when; } return; } } debug('unrefList append to end'); L.append(unrefList, item); }; ================================================ FILE: benchmark/madeup-parallel/callbacks-baseline.js ================================================ require('../lib/fakes'); module.exports = function upload(stream, idOrPath, tag, done) { var tx = db.begin(); var current = 0; var total = global.parallelQueries; for( var i = 0; i < total; ++i ) { FileVersion.insert({index: i}).execWithin(tx, function onComplete(err) { if (onComplete.called) return; onComplete.called = true; if( err ) { tx.rollback(); done(err); } else { current++; if( current === total ) { tx.commit(); done(); } } }); } } ================================================ FILE: benchmark/madeup-parallel/callbacks-caolan-async-parallel.js ================================================ require('../lib/fakes'); var async = require('async'); function fileInsertFor(i, tx) { return function(callback) { FileVersion.insert({index: i}) .execWithin(tx, callback); }; } module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = fileInsertFor(i, tx); } async.parallel(queries, function(err, callback) { if (err) { tx.rollback(); done(err); } else { tx.commit(); done(); } }); } ================================================ FILE: benchmark/madeup-parallel/callbacks-suguru03-neo-async-parallel.js ================================================ require('../lib/fakes'); var async = require('neo-async'); function fileInsertFor(i, tx) { return function(callback) { FileVersion.insert({index: i}) .execWithin(tx, callback); }; } module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = fileInsertFor(i, tx); } async.parallel(queries, function(err, callback) { if (err) { tx.rollback(); done(err); } else { tx.commit(); done(); } }); } ================================================ FILE: benchmark/madeup-parallel/generators-tj-co.js ================================================ global.useNative = true; try { if (Promise.race.toString() !== 'function race() { [native code] }') throw 0; } catch (e) { throw new Error("No ES6 promises available"); } var co = require('co'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { return co(function* () { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } try { yield Promise.all(queries); tx.commit(); done(); } catch(e) { tx.rollback(); done(e); } }); }; ================================================ FILE: benchmark/madeup-parallel/promises-bluebird-generator.js ================================================ global.useBluebird = true; global.useQ = false; var bluebird = require('../../js/release/bluebird.js'); require('../lib/fakesP'); module.exports = bluebird.coroutine(function* upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } try { yield bluebird.all(queries); tx.commit(); done(); } catch(e) { tx.rollback(); done(e); } }); ================================================ FILE: benchmark/madeup-parallel/promises-bluebird.js ================================================ global.useBluebird = true; global.useQ = false; var Promise = require('../../js/release/bluebird.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } Promise.all(queries).then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); } ================================================ FILE: benchmark/madeup-parallel/promises-calvinmetcalf-lie.js ================================================ global.useLie = true; var Promise = require("lie"); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } Promise.all(queries).then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); } ================================================ FILE: benchmark/madeup-parallel/promises-cujojs-when.js ================================================ global.useWhen = true; var when = require('when'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } when.all(queries).then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); }; ================================================ FILE: benchmark/madeup-parallel/promises-dfilatov-vow.js ================================================ var vow = require('vow'), p = require('../lib/promiseSupport.js'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } vow.all(queries).then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); } ================================================ FILE: benchmark/madeup-parallel/promises-ecmascript6-native.js ================================================ global.useNative = true; try { if (Promise.race.toString() !== 'function race() { [native code] }') throw 0; } catch (e) { throw new Error("No ES6 promises available"); } require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } Promise.all(queries).then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); } ================================================ FILE: benchmark/madeup-parallel/promises-lvivski-davy.js ================================================ global.useDavy = true; var davy = require('davy'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } davy.all(queries).then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); } ================================================ FILE: benchmark/madeup-parallel/promises-medikoo-deferred.js ================================================ global.useDeferred = true; var deferred = require('deferred'); require('../lib/fakesP'); function identity(v) { return v; } module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } //Couldn't find .all in docs, this seems closest deferred.map(queries, identity)(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); } ================================================ FILE: benchmark/madeup-parallel/promises-native-async-await.js ================================================ global.useNative = true; try { if (Promise.race.toString() !== 'function race() { [native code] }') throw 0; } catch (e) { throw new Error("No ES6 promises available"); } require('../lib/fakesP'); module.exports = async function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } try { await Promise.all(queries); tx.commit(); done(); } catch(e) { tx.rollback(); done(e); } }; ================================================ FILE: benchmark/madeup-parallel/promises-obvious-kew.js ================================================ global.useKew = true; var q = require('kew'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } q.all(queries).then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); } ================================================ FILE: benchmark/madeup-parallel/promises-then-promise.js ================================================ global.useThenPromise = true; var Promise = require("promise"); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } Promise.all(queries).then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); } ================================================ FILE: benchmark/madeup-parallel/promises-tildeio-rsvp.js ================================================ global.useRSVP = true; var RSVP = require('rsvp'); require('../lib/fakesP'); module.exports = function upload(stream, idOrPath, tag, done) { var queries = new Array(global.parallelQueries); var tx = db.begin(); for( var i = 0, len = queries.length; i < len; ++i ) { queries[i] = FileVersion.insert({index: i}).execWithin(tx); } RSVP.all(queries).then(function() { tx.commit(); done(); }, function(err) { tx.rollback(); done(err); }); }; ================================================ FILE: benchmark/madeup-parallel/streamline-callbacks.js ================================================ 'use strict'; var regeneratorRuntime = typeof require === 'function' ? require('streamline-runtime/lib/callbacks/regenerator') : Streamline.require('streamline-runtime/lib/callbacks/regenerator'); var _streamline = typeof require === 'function' ? require('streamline-runtime/lib/callbacks/runtime') : Streamline.require('streamline-runtime/lib/callbacks/runtime'); var _filename = '/Users/bruno/dev/bluebird/benchmark/madeup-parallel/streamline._js'; var execWithin = _streamline.async(regeneratorRuntime.mark(function _$$execWithin$$(query, tx, _2) { return regeneratorRuntime.wrap(function _$$execWithin$$$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return _streamline.await(_filename, 5, query, 'execWithin', 1, null, false, [tx, true]); case 2: return _context.abrupt('return', _context.sent); case 3: case 'end': return _context.stop(); } } }, _$$execWithin$$, this); }), 2, 3); require('../lib/fakes'); // Futures work on streamlined function so we need to wrap execWithin module.exports = _streamline.async(regeneratorRuntime.mark(function _$$upload$$(stream, idOrPath, tag, _3) { var queries, tx, i, len; return regeneratorRuntime.wrap(function _$$upload$$$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: _context2.prev = 0; queries = new Array(global.parallelQueries); tx = db.begin(); for (i = 0, len = queries.length; i < len; ++i) { queries[i] = _streamline.future(_filename, 14, null, execWithin, 2, null, false, [FileVersion.insert({ index: i }), tx, false]); } i = 0, len = queries.length; case 5: if (!(i < len)) { _context2.next = 11; break; } _context2.next = 8; return _streamline.await(_filename, 18, queries, i, 0, null, false, [true]); case 8: ++i; _context2.next = 5; break; case 11: tx.commit(); _context2.next = 18; break; case 14: _context2.prev = 14; _context2.t0 = _context2['catch'](0); tx.rollback(); throw _context2.t0; case 18: case 'end': return _context2.stop(); } } }, _$$upload$$, this, [[0, 14]]); }), 3, 4); ================================================ FILE: benchmark/madeup-parallel/streamline-generators.js ================================================ 'use strict'; var _streamline = typeof require === 'function' ? require('streamline-runtime/lib/generators/runtime') : Streamline.require('streamline-runtime/lib/generators/runtime'); var _filename = '/Users/bruno/dev/bluebird/benchmark/madeup-parallel/streamline._js'; var execWithin = _streamline.async(function* _$$execWithin$$(query, tx, _2) { { return yield _streamline.await(_filename, 5, query, 'execWithin', 1, null, false, [tx, true]); } }, 2, 3); require('../lib/fakes'); // Futures work on streamlined function so we need to wrap execWithin module.exports = _streamline.async(function* _$$upload$$(stream, idOrPath, tag, _3) { { try { var queries = new Array(global.parallelQueries); var tx = db.begin(); for (var i = 0, len = queries.length; i < len; ++i) { queries[i] = _streamline.future(_filename, 14, null, execWithin, 2, null, false, [FileVersion.insert({ index: i }), tx, false]); } for (var i = 0, len = queries.length; i < len; ++i) { yield _streamline.await(_filename, 18, queries, i, 0, null, false, [true]); } tx.commit(); } catch (err) { tx.rollback(); throw err; } } }, 3, 4); ================================================ FILE: benchmark/madeup-parallel/streamline._js ================================================ require('../lib/fakes'); // Futures work on streamlined function so we need to wrap execWithin function execWithin(query, tx, _) { return query.execWithin(tx, _); } module.exports = function upload(stream, idOrPath, tag, _) { try { var queries = new Array(global.parallelQueries); var tx = db.begin(); for (var i = 0, len = queries.length; i < len; ++i ) { queries[i] = execWithin(FileVersion.insert({index: i}), tx, !_); } for (var i = 0, len = queries.length; i < len; ++i ) { queries[i](_); } tx.commit(); } catch (err) { tx.rollback(); throw err; } }; ================================================ FILE: benchmark/package.json ================================================ { "name": "async-compare", "version": "0.1.1", "description": "Compare the performance and code of multiple async patterns", "main": "perf.js", "dependencies": { "async": "^2.6.1", "davy": "^1.3.0", "deferred": "^0.7.9", "kew": "^0.7.0", "lie": "^3.3.0", "neo-async": "^2.5.1", "optimist": "^0.6.1", "promise": "^8.0.1", "q": "^1.5.1", "rsvp": "^4.8.3", "streamline": "^2.1.3", "streamline-runtime": "^1.1.15", "text-table": "^0.2.0", "vow": "^0.4.17", "when": "^3.7.8" }, "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "generators", "fibers", "promises", "callbacks", "comparison", "compare", "async" ], "author": "spion", "license": "MIT", "engines": { "node": ">8.11.0 <9" } } ================================================ FILE: benchmark/performance.js ================================================ var args = require('optimist').argv; var path = require('path'); global.LIKELIHOOD_OF_REJECTION = args.e || 0.1; global.triggerIntentionalError = function(){ if(LIKELIHOOD_OF_REJECTION && Math.random() <= LIKELIHOOD_OF_REJECTION) throw new Error("intentional failure"); } function printPlatform() { console.log("\nPlatform info:"); var os = require("os"); var v8 = process.versions.v8; var node = process.versions.node; var plat = os.type() + " " + os.release() + " " + os.arch() + "\nNode.JS " + node + "\nV8 " + v8; var cpus = os.cpus().map(function(cpu){ return cpu.model; }).reduce(function(o, model){ if( !o[model] ) o[model] = 0; o[model]++; return o; }, {}); cpus = Object.keys(cpus).map(function( key ){ return key + " \u00d7 " + cpus[key]; }).join("\n"); console.log(plat + "\n" + cpus + "\n"); } var perf = module.exports = function(args, done) { var errs = 0; var lastErr; var times = args.n; global.asyncTime = args.t; global.parallelQueries = args.p || 10; if (args.longStackSupport) { global.longStackSupport = require('q').longStackSupport = args.longStackSupport; require('bluebird').longStackTraces(); } var fn = require(args.file); var start = Date.now(); var warmedUp = 0; var tot = Math.min( 350, times ); for (var k = 0, kn = tot; k < kn; ++k) fn(k,'b','c', warmup); var memMax; var memStart; var start; function warmup() { warmedUp++ if( warmedUp === tot ) { start = Date.now(); memStart = process.memoryUsage().rss; for (var k = 0, kn = args.n; k < kn; ++k) fn(k, 'b', 'c', cb); memMax = process.memoryUsage().rss; } } function cb (err) { if (err && err.message !== "intentional failure") { ++errs; lastErr = err; } memMax = Math.max(memMax, process.memoryUsage().rss); if (!--times) { fn.end && fn.end(); done(null, { time: Date.now() - start, mem: (memMax - memStart)/1024/1024, errors: errs, lastErr: lastErr ? lastErr.stack : null }); } } } function report(err, res) { console.log(JSON.stringify(res)); } if (args.file) { perf(args, function(err, res) { report(err, res); if (res.lastErr) console.error(res.lastErr); }); } else { var cp = require('child_process') var async = require('async'); var table = require('text-table'); var files = args._.filter(function(f) { return !/^src-/.test(path.basename(f)); }); measure(files, args.n, args.t, args.p, function(err, res) { console.log(""); console.log("results for", args.n, "parallel executions,", args.t, "ms per I/O op"); if(args.e) console.log("Likelihood of rejection:", args.e); res.sort(function(r1, r2) { return parseFloat(r1.data.time) - parseFloat(r2.data.time) }); console.log(""); res = res.map(function(r) { var failText = 'OOM'; if (r.data.timeout) failText = 'T/O'; return [path.basename(r.file), r.data.mem != null ? r.data.time: failText, r.data.mem != null ? r.data.mem.toFixed(2) : failText] }); res = [['file', 'time(ms)', 'memory(MB)']].concat(res) console.log(table(res, {align: ['l', 'r', 'r']})); printPlatform(); }); } function measure(files, requests, time, parg, callback) { async.mapSeries(files, function(f, done) { console.log("benchmarking", f); var logFile = path.basename(f) + ".log"; var profileFlags = ["--prof", "--logfile=C:/etc/v8/" + logFile]; var argsFork = [__filename, '--n', requests, '--t', time, '--p', parg, '--file', f]; if (args.profile) argsFork = profileFlags.concat(argsFork); if (args.harmony) argsFork.unshift('--harmony'); if (args.longStackSupport) argsFork.push('--longStackSupport'); var p = cp.spawn(process.execPath, argsFork); var complete = false, timedout = false; if (args.timeout) setTimeout(function() { if (complete) return; timedout = true; p.kill(); }, args.timeout); var r = { file: f, data: [] }; p.stdout.on('data', function(d) { r.data.push(d.toString()); }); p.stdout.pipe(process.stdout); p.stderr.pipe(process.stderr); p.stdout.on('end', function(code) { complete = true; try { r.data = JSON.parse(r.data.join('')); } catch(e) { r.data = {time: Number.POSITIVE_INFINITY, mem: null, missing: true, timeout: timedout}; } done(null, r); }); }, callback); } ================================================ FILE: bower.json ================================================ { "name": "bluebird", "version": "3.7.2", "homepage": "https://github.com/petkaantonov/bluebird", "authors": [ "Petka Antonov " ], "description": "Bluebird is a full featured promise library with unmatched performance.", "main": "js/browser/bluebird.js", "license": "MIT", "ignore": [ "**/.*", "benchmark", "bower_components", "./browser", "node_modules", "test" ], "keywords": [ "promise", "performance", "promises", "promises-a", "promises-aplus", "async", "await", "deferred", "deferreds", "future", "flow control", "dsl", "fluent interface" ] } ================================================ FILE: build ================================================ #!/usr/bin/env bash node tools/build.js "$@" ================================================ FILE: changelog.md ================================================ [http://bluebirdjs.com/docs/changelog.html](http://bluebirdjs.com/docs/changelog.html) ================================================ FILE: deprecated_apis.md ================================================ [http://bluebirdjs.com/docs/deprecated-apis.html](http://bluebirdjs.com/docs/deprecated-apis.html) ================================================ FILE: docs/.gitignore ================================================ _site .sass-cache Gemfile.lock ================================================ FILE: docs/Gemfile ================================================ source "https://rubygems.org" gem "jekyll", '3.9.0' gem "jekyll-redirect-from" gem "sanitize", '4.0.1' gem "redcarpet" gem "pygments.rb" gem 'wdm', '>= 0.1.0' if Gem.win_platform? ================================================ FILE: docs/README.md ================================================ Requires ruby and [jekyll](http://jekyllrb.com/). See the gem file for dependencies. Change directory to `bluebird/docs` and run `jekyll serve`. The docs will be hosted under `/docs` directory in relation to the web root. Typically something like `http://localhost:4000/docs/` ================================================ FILE: docs/_config.yml ================================================ name: bluebird description: Bluebird is a fully featured JavaScript promises library with unmatched performance. url: "http://bluebirdjs.com" baseurl: "" title: bluebird timezone: Helsinki/Finland highlighter: pygments exclude: - Gemfile - Gemfile.lock - helpers.rb defaults: - scope: path: docs type: pages values: layout: page markdown: redcarpet redcarpet: extensions: - fenced_code_blocks version: 3.7.2 gems: - jekyll-redirect-from destination: ../gh-pages/ keep_files: - .git - .gitignore - logo.png - CNAME - coverage safe: false encoding: utf-8 host: 0.0.0.0 ================================================ FILE: docs/_layouts/api.html ================================================ --- layout: default ---
{{ content }}
================================================ FILE: docs/_layouts/default.html ================================================ {% if page.title %}{{ page.title }} | {{ site.title }}{% else %}{{ site.title }}{% endif %}
================================================ FILE: docs/_layouts/page.html ================================================ --- layout: default ---

{{ page.title }}

{{ content }}
================================================ FILE: docs/_plugins/mdate.rb ================================================ module Jekyll module MyFilters def file_date(input) File.mtime(input) end def check_active(page_path, link_type) if (link_type == "support" and page_path =~ /support/) or (link_type == "install" and page_path =~ /install/) or (link_type == "docs") "active" else "" end end end end Liquid::Template.register_filter(Jekyll::MyFilters) ================================================ FILE: docs/_plugins/plugin.rb ================================================ require "redcarpet" require "pygments" require_relative "../helpers" class Redcarpet::Render::HTML def header(title, level) anchor = Helpers.clean(title) return <<-eos #{title} eos end # Hacks to get markdown working inside html blocks .... def block_html(html) html.gsub(/([\d\D]+?)<\/markdown>/) {|_| extensions = {fenced_code_blocks: true} renderer = Redcarpet::Markdown.new(WithHighlights, extensions) renderer.render(Regexp.last_match[1]) } end def link(link, title, content) if link == "unfinished-article" return <<-eos
This article is partially or completely unfinished. You are welcome to create pull requests to help completing this article.
eos elsif link == "." if content =~ /#\d+/ url = "https://github.com/petkaantonov/bluebird/issues/" + content[1..-1] else url = "/docs/api/" + Helpers.clean(content) + ".html" end return "#{content}" else return "#{content}" end end end class WithHighlights < Redcarpet::Render::HTML def block_code(code, language) Pygments.highlight(code, :lexer => language) end end ================================================ FILE: docs/css/main.css ================================================ body { font-size: 16px; } body > .container { padding-bottom: 50px; } .navbar-bluebird { font-family: 'Raleway', sans-serif; font-size: 18px; margin-top: 50px; text-transform:uppercase; } .nav { font-family: 'Raleway', sans-serif; } .nav.bb-nav > li > a{ transition: all 0.3s ease-in-out; border-bottom: 1px solid rgba(0,0,0,0); } .nav.bb-nav > li > a:focus, .nav.bb-nav > li > a:hover, .nav.bb-nav > li.active > a { background-color:initial; border-bottom:1px solid #BE7306; } .nav a { color: #BE7306; } .nav.left-nav li a { padding: 2px 0px; font-size: 14px; font-family: 'Raleway', sans-serif; } .nav.left-nav li a:focus, .nav.left-nav li a:hover { background: none; text-decoration: underline; } .navbar-bluebird li { text-align:center; } h1,h2,h3,h4,h5,h6{ font-family: 'Raleway', sans-serif; /*color:#5275B4;*/ color: #1B4288; } .api-code-section h2, code, pre { font-family: 'Consolas', 'Lucida Console', 'Courier New', 'monospace' } body { margin-top:10px; } .icon-bar { background-color:#5275B4; } .promises-showroom .arrow { text-align: center; margin-top: 100px; color:#EC9313; } pre{ background-color: initial; border: none; margin:0px; padding:0px; } .promises-showroom .hljs{ border-radius: 6px; padding-left: 30px; padding-bottom: 15px; } .take-action{ position:relative; min-height:300px; } .take-action .cloud-bg{ width: 100%; height: 300px; border: none; } .take-action .action-buttons{ position:absolute; top:50%; text-align:center; width:100%; } .btn-bb { font-family: 'Raleway', sans-serif; color: white; } .btn-beak{ background-color: #EC9313; border-bottom: 4px solid #BE7306; } .btn-wings { background-color: #265584; border-bottom: 4px solid #1A3A59; } .btn-bb:hover{ color:white; } .undo-margin{ margin-left:-15px; } .main-line{ font-family: 'Raleway', sans-serif; margin-bottom: 59px; margin-top: -74px; font-size: 20px; color:white; } .tagline { font-family: 'Raleway', sans-serif; font-size:26px; color:#5275B4; font-weight:200; } .nav-child { margin-left:20px; } .title:hover{ text-decoration:none; } .header-anchor { opacity: 0; -webkit-transition: opacity 0.2s ease-in-out 0.1s; -moz-transition: opacity 0.2s ease-in-out 0.1s; -ms-transition: opacity 0.2s ease-in-out 0.1s; outline: none; } .header-anchor:active, .header-anchor:focus { outline: none; } h1:hover .header-anchor, h2:hover .header-anchor, h3:hover .header-anchor, h4:hover .header-anchor, h5:hover .header-anchor, h6:hover .header-anchor { opacity: 1; } .api-reference-menu { -moz-column-count: 2; -webkit-column-count: 2; columns: 2; margin-bottom: 50px; } .post-info { float: right; } .info-box { margin-top: 20px; color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; padding: 15px; margin-bottom: 20px; border: 1px dashed #8a6d3b; border-radius: 4px; } .supporter.reaktor { width: 200px; margin: 0px auto; } ================================================ FILE: docs/css/mono-blue.css ================================================ .highlight { display: block; overflow-x: auto; padding: 0.5em; margin: 15px 0px; background: #eaeef3; -webkit-text-size-adjust: none; color: #00193a; } code { color: #00193a; background: #eaeef3; } a code { color: #337AB7; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #738191; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #4c81c9; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #738191; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #BC7A00 } /* Comment.Preproc */ .highlight .c1 { color: #738191; font-style: italic } /* Comment.Single */ .highlight .cs { color: #738191; font-style: italic } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #808080 } /* Generic.Output */ .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #4c81c9; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #4c81c9; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #4c81c9; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #4c81c9 } /* Keyword.Pseudo */ .highlight .kr { color: #4c81c9; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #B00040 } /* Keyword.Type */ .highlight .m { color: #666666 } /* Literal.Number */ .highlight .s { color: #BE7306;} /* Literal.String */ .highlight .na { color: #7D9029 } /* Name.Attribute */ .highlight .nb { color: #4c81c9 } /* Name.Builtin */ .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ .highlight .no { color: #880000 } /* Name.Constant */ .highlight .nd { color: #AA22FF } /* Name.Decorator */ .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0000FF } /* Name.Function */ .highlight .nl { color: #A0A000 } /* Name.Label */ .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #4c81c9; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #19177C } /* Name.Variable */ .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #666666 } /* Literal.Number.Float */ .highlight .mh { color: #666666 } /* Literal.Number.Hex */ .highlight .mi { color: #666666 } /* Literal.Number.Integer */ .highlight .mo { color: #666666 } /* Literal.Number.Oct */ .highlight .sb { color: #BE7306 } /* Literal.String.Backtick */ .highlight .sc { color: #BE7306 } /* Literal.String.Char */ .highlight .sd { color: #BE7306; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #BE7306 } /* Literal.String.Double */ .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #BE7306 } /* Literal.String.Heredoc */ .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ .highlight .sx { color: #BE7306 } /* Literal.String.Other */ .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ .highlight .s1 { color: #BE7306 } /* Literal.String.Single */ .highlight .ss { color: #19177C } /* Literal.String.Symbol */ .highlight .bp { color: #4c81c9 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #19177C } /* Name.Variable.Class */ .highlight .vg { color: #19177C } /* Name.Variable.Global */ .highlight .vi { color: #19177C } /* Name.Variable.Instance */ .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ ================================================ FILE: docs/docs/anti-patterns.md ================================================ --- id: anti-patterns title: Anti-patterns --- This page will contain common promise anti-patterns that are exercised in the wild. - [The explicit construction anti-pattern](#the-explicit-construction-anti-pattern) - [The `.then(success, fail)` anti-pattern](#the-.then) ##The Explicit Construction Anti-Pattern This is the most common anti-pattern. It is easy to fall into this when you don't really understand promises and think of them as glorified event emitters or callback utility. It's also sometimes called the promise constructor anti-pattern. Let's recap: promises are about making asynchronous code retain most of the lost properties of synchronous code such as flat indentation and one exception channel. This pattern is also called the deferred anti-pattern. In the explicit construction anti-pattern, promise objects are created for no reason, complicating code. First example is creating deferred object when you already have a promise or thenable: ```js //Code copyright by Twisternha http://stackoverflow.com/a/19486699/995876 CC BY-SA 2.5 myApp.factory('Configurations', function (Restangular, MotorRestangular, $q) { var getConfigurations = function () { var deferred = $q.defer(); MotorRestangular.all('Motors').getList().then(function (Motors) { //Group by Config var g = _.groupBy(Motors, 'configuration'); //Map values var mapped = _.map(g, function (m) { return { id: m[0].configuration, configuration: m[0].configuration, sizes: _.map(m, function (a) { return a.sizeMm }) } }); deferred.resolve(mapped); }); return deferred.promise; }; return { config: getConfigurations() } }); ``` This superfluous wrapping is also dangerous, any kind of errors and rejections are swallowed and not propagated to the caller of this function. Instead of using the Deferred anti-pattern, the code should simply return the promise it already has and propagate values using `return`: ```js myApp.factory('Configurations', function (Restangular, MotorRestangular, $q) { var getConfigurations = function () { //Just return the promise we already have! return MotorRestangular.all('Motors').getList().then(function (Motors) { //Group by Cofig var g = _.groupBy(Motors, 'configuration'); //Return the mapped array as the value of this promise return _.map(g, function (m) { return { id: m[0].configuration, configuration: m[0].configuration, sizes: _.map(m, function (a) { return a.sizeMm }) } }); }); }; return { config: getConfigurations() } }); ``` Not only is the code shorter but more importantly, if there is any error it will propagate properly to the final consumer. Second example is creating a function that does nothing but manually wrap a callback API and doing a poor job at that: ```js function applicationFunction(arg1) { return new Promise(function(resolve, reject){ //Or Q.defer() in Q libraryFunction(arg1, function (err, value) { if (err) { reject(err); } else { resolve(value); } }); } ``` This is reinventing the square wheel because any callback API wrapping can and should be done immediately using the promise library's promisification methods: ```js var applicationFunction = Promise.promisify(libraryFunction); ``` The generic promisification is likely to be faster because it can use internals directly but also handles edge cases like `libraryFunction` throwing synchronously or using multiple success values. **So when should deferred be used?** Well simply, when you have to. You might have to use a deferred object when wrapping a callback API that doesn't follow the standard convention. Like `setTimeout`: ```js //setTimeout that returns a promise function delay(ms) { var deferred = Promise.defer(); // warning, defer is deprecated, use the promise constructor setTimeout(function(){ deferred.fulfill(); }, ms); return deferred.promise; } ``` Such wrappers should be rare, if they're common for the reason that the promise library cannot generically promisify them, you should file an issue. If you cannot do static promisification (promisify and promisifyAll perform too slowly to use at runtime), you may use [Promise.fromCallback](.). Also see [this StackOverflow question](http://stackoverflow.com/questions/23803743/what-is-the-deferred-antipattern-and-how-do-i-avoid-it) for more examples and a debate around it. ##The `.then(success, fail)` anti-pattern *Almost* a sure sign of using promises as glorified callbacks. Instead of `doThat(function(err, success))` you do `doThat().then(success, err)` and rationalize to yourself that at least the code is "less coupled" or something. The `.then` signature is mostly about interop, there is *almost* never a reason to use `.then(success, fail)` in application code. It is even awkward to express it in the sync parallel: ```js var t0; try { t0 = doThat(); } catch(e) { } //deal with t0 here and waste the try-catch var stuff = JSON.parse(t0); ``` It is more likely that you would write this instead in the sync world: ```js try { var stuff = JSON.parse(doThat()); } catch(e) { } ``` So please write the same when using promises too: ```js doThat() .then(function(v) { return JSON.parse(v); }) .catch(function(e) { }); ``` `.catch` is specified for built-in Javascript promises and is "sugar" for `.then(null, function(){})`. Since the way errors work in promises is almost the entire point (and the only thing jQuery never got right, even if it used `.pipe` as a `.then`), I really hope the implementation you are using provides this method for readability. ================================================ FILE: docs/docs/api/aggregateerror.md ================================================ --- layout: api id: aggregateerror title: AggregateError --- [← Back To API Reference](/docs/api-reference.html)
##AggregateError ```js new AggregateError() extends Array -> AggregateError ``` A collection of errors. `AggregateError` is an array-like object, with numeric indices and a `.length` property. It supports all generic array methods such as `.forEach` directly. `AggregateError`s are caught in [`.error`](.) handlers, even if the contained errors are not operational. [Promise.some](.) and [Promise.any](.) use `AggregateError` as rejection reason when they fail. Example: ```js //Assumes AggregateError has been made global var err = new AggregateError(); err.push(new Error("first error")); err.push(new Error("second error")); throw err; ```
================================================ FILE: docs/docs/api/all.md ================================================ --- layout: api id: all title: .all --- [← Back To API Reference](/docs/api-reference.html)
##.all ```js .all() -> Promise ``` Consume the resolved [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) and wait for all items to fulfill similar to [Promise.all()](.). [Promise.resolve(iterable).all()](.) is the same as [Promise.all(iterable)](.).
================================================ FILE: docs/docs/api/any.md ================================================ --- layout: api id: any title: .any --- [← Back To API Reference](/docs/api-reference.html)
##.any ```js .any() -> Promise ``` Same as [Promise.any(this)](.).
================================================ FILE: docs/docs/api/ascallback.md ================================================ --- layout: api id: ascallback title: .asCallback --- [← Back To API Reference](/docs/api-reference.html)
##.asCallback ```js .asCallback( [function(any error, any value) callback], [Object {spread: boolean=false} options] ) -> this ``` ```js .nodeify( [function(any error, any value) callback], [Object {spread: boolean=false} options] ) -> this ``` Register a node-style callback on this promise. When this promise is either fulfilled or rejected, the node callback will be called back with the node.js convention where error reason is the first argument and success value is the second argument. The error argument will be `null` in case of success. Returns back this promise instead of creating a new one. If the `callback` argument is not a function, this method does not do anything. This can be used to create APIs that both accept node-style callbacks and return promises: ```js function getDataFor(input, callback) { return dataFromDataBase(input).asCallback(callback); } ``` The above function can then make everyone happy. Promises: ```js getDataFor("me").then(function(dataForMe) { console.log(dataForMe); }); ``` Normal callbacks: ```js getDataFor("me", function(err, dataForMe) { if( err ) { console.error( err ); } console.log(dataForMe); }); ``` Promises can be rejected with falsy values (or no value at all, equal to rejecting with `undefined`), however `.asCallback` will call the callback with an `Error` object if the promise's rejection reason is a falsy value. You can retrieve the original falsy value from the error's `.cause` property. Example: ```js Promise.reject(null).asCallback(function(err, result) { // If is executed if (err) { // Logs 'null' console.log(err.cause); } }); ``` There is no effect on performance if the user doesn't actually pass a node-style callback function. ####Option: spread Some nodebacks expect more than 1 success value but there is no mapping for this in the promise world. You may specify the option `spread` to call the nodeback with multiple values when the fulfillment value is an array: ```js Promise.resolve([1,2,3]).asCallback(function(err, result) { // err == null // result is the array [1,2,3] }); Promise.resolve([1,2,3]).asCallback(function(err, a, b, c) { // err == null // a == 1 // b == 2 // c == 3 }, {spread: true}); Promise.resolve(123).asCallback(function(err, a, b, c) { // err == null // a == 123 // b == undefined // c == undefined }, {spread: true}); ```
================================================ FILE: docs/docs/api/bind.md ================================================ --- layout: api id: bind title: .bind --- [← Back To API Reference](/docs/api-reference.html)
##.bind ```js .bind(any|Promise thisArg) -> BoundPromise ``` Same as calling [Promise.bind(thisArg, thisPromise)](.).
================================================ FILE: docs/docs/api/built-in-error-types.md ================================================ --- layout: api id: built-in-error-types title: Built-in error types --- [← Back To API Reference](/docs/api-reference.html)
##Built-in error types Bluebird includes a few built-in error types for common usage. All error types have the same identity across different copies of bluebird module so that pattern matching works in [`.catch`](.). All error types have a constructor taking a message string as their first argument, with that message becoming the `.message` property of the error object. By default the error types need to be referenced from the Promise constructor, e.g. to get a reference to [TimeoutError](.), do `var TimeoutError = Promise.TimeoutError`. However, for convenience you will probably want to just make the references global.
================================================ FILE: docs/docs/api/call.md ================================================ --- layout: api id: call title: .call --- [← Back To API Reference](/docs/api-reference.html)
##.call ```js .call( String methodName, [any args...] ) ``` This is a convenience method for doing: ```js promise.then(function(obj) { return obj[methodName].call(obj, arg...); }); ``` For example ([`some` is a built-in array method](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/some)): ```js var Promise = require("bluebird"); var fs = Promise.promisifyAll(require("fs")); var path = require("path"); var thisPath = process.argv[2] || "."; var now = Date.now(); fs.readdirAsync(thisPath) .map(function(fileName) { return fs.statAsync(path.join(thisPath, fileName)); }) .call("some", function(stat) { return (now - new Date(stat.mtime)) < 10000; }) .then(function(someFilesHaveBeenModifiedLessThanTenSecondsAgo) { console.log(someFilesHaveBeenModifiedLessThanTenSecondsAgo) ; }); ``` Chaining lo-dash or underscore methods (Copy-pasteable example): ```js var Promise = require("bluebird"); var pmap = Promise.map; var props = Promise.props; var _ = require("lodash"); var fs = Promise.promisifyAll(require("fs")); function getTotalSize(paths) { return pmap(paths, function(path) { return fs.statAsync(path).get("size"); }).reduce(function(a, b) { return a + b; }, 0); } fs.readdirAsync(".").then(_) .call("groupBy", function(fileName) { return fileName.charAt(0); }) .call("map", function(fileNames, firstCh) { return props({ firstCh: firstCh, count: fileNames.length, totalSize: getTotalSize(fileNames) }); }) // Since the currently wrapped array contains promises we need to unwrap it and call .all() before continuing the chain // If the currently wrapped thing was an object with properties that might be promises, we would call .props() instead .call("value").all().then(_) .call("sortBy", "count") .call("reverse") .call("map", function(data) { return data.count + " total files beginning with " + data.firstCh + " with total size of " + data.totalSize + " bytes"; }) .call("join", "\n") .then(console.log) ```
================================================ FILE: docs/docs/api/cancel.md ================================================ --- layout: api id: cancel title: .cancel --- [← Back To API Reference](/docs/api-reference.html)
##.cancel ```js .cancel() -> undefined ``` Cancel this promise. Will not do anything if this promise is already settled or if the [Cancellation](.) feature has not been enabled. See [Cancellation](.) for how to use cancellation.
================================================ FILE: docs/docs/api/cancellation.md ================================================ --- layout: api id: cancellation title: Cancellation --- [← Back To API Reference](/docs/api-reference.html)
##Cancellation Cancellation has been redesigned for bluebird 3.x, any code that relies on 2.x cancellation semantics won't work in 3.x. The cancellation feature is **by default turned off**, you can enable it using [Promise.config](.). The new cancellation has "don't care" semantics while the old cancellation had abort semantics. Cancelling a promise simply means that its handler callbacks will not be called. The advantages of the new cancellation compared to the old cancellation are: - [.cancel()](.) is synchronous. - no setup code required to make cancellation work - composes with other bluebird features, like [Promise.all](.). - [reasonable semantics for multiple consumer cancellation](#what-about-promises-that-have-multiple-consumers) As an optimization, the cancellation signal propagates upwards the promise chain so that an ongoing operation e.g. network request can be aborted. However, *not* aborting the network request still doesn't make any operational difference as the callbacks are still not called either way. You may register an optional cancellation hook at a root promise by using the `onCancel` argument that is passed to the executor function when cancellation is enabled: ```js function makeCancellableRequest(url) { return new Promise(function(resolve, reject, onCancel) { var xhr = new XMLHttpRequest(); xhr.on("load", resolve); xhr.on("error", reject); xhr.open("GET", url, true); xhr.send(null); // Note the onCancel argument only exists if cancellation has been enabled! onCancel(function() { xhr.abort(); }); }); } ``` Note that the `onCancel` hook is really an optional disconnected optimization, there is no real requirement to register any cancellation hooks for cancellation to work. As such, any errors that may occur while inside the `onCancel` callback are not caught and turned into rejections. While `cancel().` is synchronous - `onCancel()` is called asynchronously (in the next turn) just like `then` handlers. Example: ```js var searchPromise = Promise.resolve(); // Dummy promise to avoid null check. document.querySelector("#search-input").addEventListener("input", function() { // The handlers of the previous request must not be called searchPromise.cancel(); var url = "/search?term=" + encodeURIComponent(this.value.trim()); showSpinner(); searchPromise = makeCancellableRequest(url) .then(function(results) { return transformData(results); }) .then(function(transformedData) { document.querySelector("#search-results").innerHTML = transformedData; }) .catch(function(e) { document.querySelector("#search-results").innerHTML = renderErrorBox(e); }) .finally(function() { // This check is necessary because `.finally` handlers are always called. if (!searchPromise.isCancelled()) { hideSpinner(); } }); }); ``` As shown in the example the handlers registered with `.finally` are called even if the promise is cancelled. Another such exception is [.reflect()](.). No other types of handlers will be called in case of cancellation. This means that in `.then(onSuccess, onFailure)` neither `onSuccess` or `onFailure` handler is called. This is similar to how [`Generator#return`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return) works - only active `finally` blocks are executed and then the generator exits. ###What about promises that have multiple consumers? It is often said that promises cannot be cancellable because they can have multiple consumers. For instance: ```js var result = makeCancellableRequest(...); var firstConsumer = result.then(...); var secondConsumer = result.then(...); ``` Even though in practice most users of promises will never have any need to take advantage of the fact that you can attach multiple consumers to a promise, it is nevertheless possible. The problem: "what should happen if [.cancel()](.) is called on `firstConsumer`?" Propagating the cancellation signal (and therefore making it abort the request) would be very bad as the second consumer might still be interested in the result despite the first consumer's disinterest. What actually happens is that `result` keeps track of how many consumers it has, in this case 2, and only if all the consumers signal cancel will the request be aborted. However, as far as `firstConsumer` can tell, the promise was successfully cancelled and its handlers will not be called. Note that it is an error to consume an already cancelled promise, doing such a thing will give you a promise that is rejected with `new CancellationError("late cancellation observer")` as the rejection reason.
================================================ FILE: docs/docs/api/cancellationerror.md ================================================ --- layout: api id: cancellationerror title: CancellationError --- [← Back To API Reference](/docs/api-reference.html)
##CancellationError ```js new CancellationError(String message) -> CancellationError ``` Signals that an operation has been aborted or cancelled. The default reason used by [`.cancel`](.).
================================================ FILE: docs/docs/api/catch.md ================================================ --- layout: api id: catch title: .catch --- [← Back To API Reference](/docs/api-reference.html)
##.catch `.catch` is a convenience method for handling errors in promise chains. It comes in two variants - A catch-all variant similar to the synchronous `catch(e) {` block. This variant is compatible with native promises. - A filtered variant (like other non-JS languages typically have) that lets you only handle specific errors. **This variant is usually preferable and is significantly safer**. ### A note on promise exception handling. Promise exception handling mirrors native exception handling in JavaScript. A synchronous function `throw`ing is similar to a promise rejecting. Here is an example to illustrate it: ```js function getItems(param) { try { var items = getItemsSync(); if(!items) throw new InvalidItemsError(); } catch(e) { // can address the error here, either from getItemsSync returning a falsey value or throwing itself throw e; // need to re-throw the error unless I want it to be considered handled. } return process(items); } ``` Similarly, with promises: ```js function getItems(param) { return getItemsAsync().then(items => { if(!items) throw new InvalidItemsError(); return items; }).catch(e => { // can address the error here and recover from it, from getItemsAsync rejects or returns a falsey value throw e; // Need to rethrow unless we actually recovered, just like in the synchronous version }).then(process); } ``` ### Catch-all ```js .catch(function(any error) handler) -> Promise ``` ```js .caught(function(any error) handler) -> Promise ``` This is a catch-all exception handler, shortcut for calling [`.then(null, handler)`](.) on this promise. Any exception happening in a `.then`-chain will propagate to nearest `.catch` handler. *For compatibility with earlier ECMAScript versions, an alias `.caught` is provided for [`.catch`](.).* ### Filtered Catch ```js .catch( class ErrorClass|function(any error)|Object predicate..., function(any error) handler ) -> Promise ``` ```js .caught( class ErrorClass|function(any error)|Object predicate..., function(any error) handler ) -> Promise ``` This is an extension to [`.catch`](.) to work more like catch-clauses in languages like Java or C#. Instead of manually checking `instanceof` or `.name === "SomeError"`, you may specify a number of error constructors which are eligible for this catch handler. The catch handler that is first met that has eligible constructors specified, is the one that will be called. Example: ```js somePromise.then(function() { return a.b.c.d(); }).catch(TypeError, function(e) { //If it is a TypeError, will end up here because //it is a type error to reference property of undefined }).catch(ReferenceError, function(e) { //Will end up here if a was never declared at all }).catch(function(e) { //Generic catch-the rest, error wasn't TypeError nor //ReferenceError }); ``` You may also add multiple filters for a catch handler: ```js somePromise.then(function() { return a.b.c.d(); }).catch(TypeError, ReferenceError, function(e) { //Will end up here on programmer error }).catch(NetworkError, TimeoutError, function(e) { //Will end up here on expected everyday network errors }).catch(function(e) { //Catch any unexpected errors }); ``` For a parameter to be considered a type of error that you want to filter, you need the constructor to have its `.prototype` property be `instanceof Error`. Such a constructor can be minimally created like so: ```js function MyCustomError() {} MyCustomError.prototype = Object.create(Error.prototype); ``` Using it: ```js Promise.resolve().then(function() { throw new MyCustomError(); }).catch(MyCustomError, function(e) { //will end up here now }); ``` However if you want stack traces and cleaner string output, then you should do: *in Node.js and other V8 environments, with support for `Error.captureStackTrace`* ```js function MyCustomError(message) { this.message = message; this.name = "MyCustomError"; Error.captureStackTrace(this, MyCustomError); } MyCustomError.prototype = Object.create(Error.prototype); MyCustomError.prototype.constructor = MyCustomError; ``` Using CoffeeScript's `class` for the same: ```coffee class MyCustomError extends Error constructor: (@message) -> @name = "MyCustomError" Error.captureStackTrace(this, MyCustomError) ``` This method also supports predicate-based filters. If you pass a predicate function instead of an error constructor, the predicate will receive the error as an argument. The return result of the predicate will be used determine whether the error handler should be called. Predicates should allow for very fine grained control over caught errors: pattern matching, error-type sets with set operations and many other techniques can be implemented on top of them. Example of using a predicate-based filter: ```js var Promise = require("bluebird"); var request = Promise.promisify(require("request")); function ClientError(e) { return e.code >= 400 && e.code < 500; } request("http://www.google.com").then(function(contents) { console.log(contents); }).catch(ClientError, function(e) { //A client error like 400 Bad Request happened }); ``` Predicate functions that only check properties have a handy shorthand. In place of a predicate function, you can pass an object, and its properties will be checked against the error object for a match: ```js fs.readFileAsync(...) .then(...) .catch({code: 'ENOENT'}, function(e) { console.log("file not found: " + e.path); }); ``` The object predicate passed to `.catch` in the above code (`{code: 'ENOENT'}`) is shorthand for a predicate function `function predicate(e) { return isObject(e) && e.code == 'ENOENT' }`, I.E. loose equality is used. *For compatibility with earlier ECMAScript version, an alias `.caught` is provided for [`.catch`](.).*
By not returning a rejected value or `throw`ing from a catch, you "recover from failure" and continue the chain: ```js Promise.reject(Error('fail!')) .catch(function(e) { // fallback with "recover from failure" return Promise.resolve('success!'); // promise or value }) .then(function(result) { console.log(result); // will print "success!" }); ``` This is exactly like the synchronous code: ```js var result; try { throw Error('fail'); } catch(e) { result = 'success!'; } console.log(result); ```
================================================ FILE: docs/docs/api/catchreturn.md ================================================ --- layout: api id: catchreturn title: .catchReturn --- [← Back To API Reference](/docs/api-reference.html)
##.catchReturn ```js .catchReturn( [class ErrorClass|function(any error) predicate], any value ) -> Promise ``` Convenience method for: ```js .catch(function() { return value; }); ``` You may optionally prepend one predicate function or ErrorClass to pattern match the error (the generic [.catch](.) methods accepts multiple) Same limitations regarding to the binding time of `value` to apply as with [`.return`](.).
================================================ FILE: docs/docs/api/catchthrow.md ================================================ --- layout: api id: catchthrow title: .catchThrow --- [← Back To API Reference](/docs/api-reference.html)
##.catchThrow ```js .catchThrow( [class ErrorClass|function(any error) predicate], any reason ) -> Promise ``` Convenience method for: ```js .catch(function() { throw reason; }); ``` You may optionally prepend one predicate function or ErrorClass to pattern match the error (the generic [.catch](.) methods accepts multiple) Same limitations regarding to the binding time of `reason` to apply as with [`.return`](.).
================================================ FILE: docs/docs/api/collections.md ================================================ --- layout: api id: collections title: Collections --- [← Back To API Reference](/docs/api-reference.html)
##Collections Methods of `Promise` instances and core static methods of the Promise class to deal with collections of promises or mixed promises and values. All collection methods have a static equivalent on the Promise object, e.g. `somePromise.map(...)...` is same as `Promise.map(somePromise, ...)...`, `somePromise.all` is same as [`Promise.all`](.) and so on. None of the collection methods modify the original input. Holes in arrays are treated as if they were defined with the value `undefined`.
================================================ FILE: docs/docs/api/core.md ================================================ --- layout: api id: core title: Core --- [← Back To API Reference](/docs/api-reference.html)
##Core Core methods of `Promise` instances and core static methods of the Promise class.
================================================ FILE: docs/docs/api/deferred-migration.md ================================================ --- layout: api id: deferred-migration title: Deferred migration --- [← Back To API Reference](/docs/api-reference.html)
##Deferred migration Deferreds are deprecated in favor of the promise constructor. If you need deferreds for some reason, you can create them trivially using the constructor: ```js function defer() { var resolve, reject; var promise = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); return { resolve: resolve, reject: reject, promise: promise }; } ``` For old code that still uses deferred objects, see [the deprecated API docs ](//bluebirdjs.com/docs/deprecated-apis.html#promise-resolution).
================================================ FILE: docs/docs/api/delay.md ================================================ --- layout: api id: delay title: .delay --- [← Back To API Reference](/docs/api-reference.html)
##.delay ```js .delay(int ms) -> Promise ``` Same as calling [Promise.delay(ms, this)](.).
================================================ FILE: docs/docs/api/disposer.md ================================================ --- layout: api id: disposer title: .disposer --- [← Back To API Reference](/docs/api-reference.html)
##.disposer ```js .disposer(function(any resource, Promise usingOutcomePromise) disposer) -> Disposer ``` A meta method used to specify the disposer method that cleans up a resource when using [`Promise.using`](/docs/api/promise.using.html). Returns a Disposer object which encapsulates both the resource as well as the method to clean it up. The user can pass this object to `Promise.using` to get access to the resource when it becomes available, as well as to ensure it's automatically cleaned up. The second argument passed to a disposer is the result promise of the using block, which you can inspect synchronously. Example: ```js // This function doesn't return a promise but a Disposer // so it's very hard to use it wrong (not passing it to `using`) function getConnection() { return db.connect().disposer(function(connection, promise) { connection.close(); }); } ``` In the above example, the connection returned by `getConnection` can only be used via `Promise.using`, like so: ```js function useConnection(query) { return Promise.using(getConnection(), function(connection) { return connection.sendQuery(query).then(function(results) { return process(results); }) }); } ``` This will ensure that `connection.close()` will be called once the promise returned from the `Promise.using` closure is resolved or if an exception was thrown in the closure body. Real example: ```js var pg = require("pg"); // Uncomment if pg has not been properly promisified yet //var Promise = require("bluebird"); //Promise.promisifyAll(pg, { // filter: function(methodName) { // return methodName === "connect" // }, // multiArgs: true //}); // Promisify rest of pg normally //Promise.promisifyAll(pg); function getSqlConnection(connectionString) { var close; return pg.connectAsync(connectionString).spread(function(client, done) { close = done; return client; }).disposer(function() { if (close) close(); }); } module.exports = getSqlConnection; ``` Real example 2: ```js var mysql = require("mysql"); // Uncomment if mysql has not been properly promisified yet // var Promise = require("bluebird"); // Promise.promisifyAll(mysql); // Promise.promisifyAll(require("mysql/lib/Connection").prototype); // Promise.promisifyAll(require("mysql/lib/Pool").prototype); var pool = mysql.createPool({ connectionLimit: 10, host: 'example.org', user: 'bob', password: 'secret' }); function getSqlConnection() { return pool.getConnectionAsync().disposer(function(connection) { connection.release(); }); } module.exports = getSqlConnection; ``` #### Note about disposers in node If a disposer method throws or returns a rejected promise, it's highly likely that it failed to dispose of the resource. In that case, Bluebird has two options - it can either ignore the error and continue with program execution or throw an exception (crashing the process in node.js). In bluebird we've chosen to do the latter because resources are typically scarce. For example, if a database connection cannot be disposed of and Bluebird ignores that, the connection pool will be quickly depleted and the process will become unusable (all requests that query the database will wait forever). Since Bluebird doesn't know how to handle that, the only sensible default is to crash the process. That way, rather than getting a useless process that cannot fulfill more requests, we can swap the faulty worker with a new one letting the OS clean up the resources for us. As a result, if you anticipate thrown errors or promise rejections while disposing of the resource you should use a `try..catch` block (or Promise.try) and write the appropriate catch code to handle the errors. If it's not possible to sensibly handle the error, letting the process crash is the next best option. This also means that disposers should not contain code that does anything other than resource disposal. For example, you cannot write code inside a disposer to commit or rollback a transaction, because there is no mechanism for the disposer to signal a failure of the commit or rollback action without crashing the process. For transactions, you can use the following similar pattern instead: ```js function withTransaction(fn) { return Promise.using(pool.acquireConnection(), function(connection) { var tx = connection.beginTransaction() return Promise .try(fn, tx) .then(function(res) { return connection.commit().thenReturn(res) }, function(err) { return connection.rollback() .catch(function(e) {/* maybe add the rollback error to err */}) .thenThrow(err); }); }); } // If the withTransaction block completes successfully, the transaction is automatically committed // Any error or rejection will automatically roll it back withTransaction(function(tx) { return tx.queryAsync(...).then(function() { return tx.queryAsync(...) }).then(function() { return tx.queryAsync(...) }); }); ```
================================================ FILE: docs/docs/api/done.md ================================================ --- layout: api id: done title: .done --- [← Back To API Reference](/docs/api-reference.html)
##.done ```js .done( [function(any value) fulfilledHandler], [function(any error) rejectedHandler] ) -> undefined ``` Like [`.then`](.), but any unhandled rejection that ends up here will crash the process (in node) or be thrown as an error (in browsers). The use of this method is heavily discouraged and it only exists for historical reasons.
================================================ FILE: docs/docs/api/each.md ================================================ --- layout: api id: each title: .each --- [← Back To API Reference](/docs/api-reference.html)
##.each ```js .each(function(any item, int index, int length) iterator) -> Promise ``` Iterate over an array, or a promise of an array, which contains promises (or a mix of promises and values) with the given `iterator` function with the signature `(value, index, length)` where `value` is the resolved value of a respective promise in the input array. Iteration happens serially. If any promise in the input array is rejected the returned promise is rejected as well. Resolves to the original array unmodified, this method is meant to be used for side effects. If the iterator function returns a promise or a thenable, then the result of the promise is awaited, before continuing with next iteration. Example where you might want to utilize `.each`: ```js // Source: http://jakearchibald.com/2014/es7-async-functions/ function loadStory() { return getJSON('story.json') .then(function(story) { addHtmlToPage(story.heading); return story.chapterURLs.map(getJSON); }) .each(function(chapter) { addHtmlToPage(chapter.html); }) .then(function() { addTextToPage("All done"); }) .catch(function(err) { addTextToPage("Argh, broken: " + err.message); }) .then(function() { document.querySelector('.spinner').style.display = 'none'; }); } ```
================================================ FILE: docs/docs/api/environment-variables.md ================================================ --- layout: api id: environment-variables title: Environment variables --- [← Back To API Reference](/docs/api-reference.html)
##Environment variables This section only applies to node.js or io.js. You can change bluebird behavior globally with various environment variables. These global variables affect all instances of bluebird that are running in your environment, rather than just the one you have `require`d in your application. The effect an environment variable has depends on the bluebird version. Environment variables supported by 2.x: - `BLUEBIRD_DEBUG` - Set to any truthy value this will enable long stack traces and warnings - `NODE_ENV` - If set exactly to `development` it will have the same effect as if the `BLUEBIRD_DEBUG` variable was set. Environment variables supported by 3.x: - `BLUEBIRD_DEBUG` - If set this will enable long stack traces and warnings, unless those are explicitly disabled. Setting this to exactly `0` can be used to override `NODE_ENV=development` enabling long stack traces and warnings. - `NODE_ENV` - If set exactly to `development` it will have the same effect as if the `BLUEBIRD_DEBUG` variable was set. - `BLUEBIRD_WARNINGS` - if set exactly to `0` it will explicitly disable warnings and this overrides any other setting that might enable warnings. If set to any truthy value, it will explicitly enable warnings. - `BLUEBIRD_LONG_STACK_TRACES` - if set exactly to `0` it will explicitly disable long stack traces and this overrides any other setting that might enable long stack traces. If set to any truthy value, it will explicitly enable long stack traces.
================================================ FILE: docs/docs/api/error-management-configuration.md ================================================ --- layout: api id: error-management-configuration title: Error management configuration --- [← Back To API Reference](/docs/api-reference.html)
##Error management configuration The default approach of bluebird is to immediately log the stack trace when there is an unhandled rejection. This is similar to how uncaught exceptions cause the stack trace to be logged so that you have something to work with when something is not working as expected. However because it is possible to handle a rejected promise at any time in the indeterminate future, some programming patterns will result in false positives. Because such programming patterns are not necessary and can always be refactored to never cause false positives, we recommend doing that to keep debugging as easy as possible . You may however feel differently so bluebird provides hooks to implement more complex failure policies. Such policies could include: - Logging after the promise became GCd (requires a native node.js module) - Showing a live list of rejected promises - Using no hooks and using [`.done`](.) to manually to mark end points where rejections will not be handled - Swallowing all errors (challenge your debugging skills) - ...
###Global rejection events Starting from 2.7.0 all bluebird instances also fire rejection events globally so that applications can register one universal hook for them. The global events are: - `"unhandledRejection"` (corresponds to the local [`Promise.onPossiblyUnhandledRejection`](.)) - `"rejectionHandled"` (corresponds to the local [`Promise.onUnhandledRejectionHandled`](.)) Attaching global rejection event handlers in **node.js**: ```js // NOTE: event name is camelCase as per node convention process.on("unhandledRejection", function(reason, promise) { // See Promise.onPossiblyUnhandledRejection for parameter documentation }); // NOTE: event name is camelCase as per node convention process.on("rejectionHandled", function(promise) { // See Promise.onUnhandledRejectionHandled for parameter documentation }); ``` Attaching global rejection event handlers in **browsers**: Using DOM3 `addEventListener` APIs (support starting from IE9+): ```js // NOTE: event name is all lower case as per DOM convention window.addEventListener("unhandledrejection", function(e) { // NOTE: e.preventDefault() must be manually called to prevent the default // action which is currently to log the stack trace to console.warn e.preventDefault(); // NOTE: parameters are properties of the event detail property var reason = e.detail.reason; var promise = e.detail.promise; // See Promise.onPossiblyUnhandledRejection for parameter documentation }); // NOTE: event name is all lower case as per DOM convention window.addEventListener("rejectionhandled", function(e) { // NOTE: e.preventDefault() must be manually called prevent the default // action which is currently unset (but might be set to something in the future) e.preventDefault(); // NOTE: parameters are properties of the event detail property var promise = e.detail.promise; // See Promise.onUnhandledRejectionHandled for parameter documentation }); ``` In Web Workers you may use `self.addEventListener`. Using legacy APIs (support starting from IE6+): ```js // NOTE: event name is all lower case as per legacy convention window.onunhandledrejection = function(reason, promise) { // See Promise.onPossiblyUnhandledRejection for parameter documentation }; // NOTE: event name is all lower case as per legacy convention window.onrejectionhandled = function(promise) { // See Promise.onUnhandledRejectionHandled for parameter documentation }; ```
================================================ FILE: docs/docs/api/error.md ================================================ --- layout: api id: error title: .error --- [← Back To API Reference](/docs/api-reference.html)
##.error ```js .error([function(any error) rejectedHandler]) -> Promise ``` Like [`.catch`](.) but instead of catching all types of exceptions, it only catches operational errors. *Note, "errors" mean errors, as in objects that are `instanceof Error` - not strings, numbers and so on. See [a string is not an error](http://www.devthought.com/2011/12/22/a-string-is-not-an-error/).* It is equivalent to the following [`.catch`](.) pattern: ```js // Assumes OperationalError has been made global function isOperationalError(e) { if (e == null) return false; return (e instanceof OperationalError) || (e.isOperational === true); } // Now this bit: .catch(isOperationalError, function(e) { // ... }) // Is equivalent to: .error(function(e) { // ... }); ``` For example, if a promisified function errbacks the node-style callback with an error, that could be caught with [`.error`](.). However if the node-style callback **throws** an error, only `.catch` would catch that. In the following example you might want to handle just the `SyntaxError` from JSON.parse and Filesystem errors from `fs` but let programmer errors bubble as unhandled rejections: ```js var fs = Promise.promisifyAll(require("fs")); fs.readFileAsync("myfile.json").then(JSON.parse).then(function (json) { console.log("Successful json") }).catch(SyntaxError, function (e) { console.error("file contains invalid json"); }).error(function (e) { console.error("unable to read file, because: ", e.message); }); ``` Now, because there is no catch-all handler, if you typed `console.lag` (causes an error you don't expect), you will see: ``` Possibly unhandled TypeError: Object # has no method 'lag' at application.js:8:13 From previous event: at Object. (application.js:7:4) at Module._compile (module.js:449:26) at Object.Module._extensions..js (module.js:467:10) at Module.load (module.js:349:32) at Function.Module._load (module.js:305:12) at Function.Module.runMain (module.js:490:10) at startup (node.js:121:16) at node.js:761:3 ``` *( If you don't get the above - you need to enable [long stack traces](/docs/api/promise.config.html) )* And if the file contains invalid JSON: ``` file contains invalid json ``` And if the `fs` module causes an error like file not found: ``` unable to read file, because: ENOENT, open 'not_there.txt' ```
================================================ FILE: docs/docs/api/filter.md ================================================ --- layout: api id: filter title: .filter --- [← Back To API Reference](/docs/api-reference.html)
##.filter ```js .filter( function(any item, int index, int length) filterer, [Object {concurrency: int=Infinity} options] ) -> Promise ``` Same as [Promise.filter(this, filterer, options)](.).
================================================ FILE: docs/docs/api/finally.md ================================================ --- layout: api id: finally title: .finally --- [← Back To API Reference](/docs/api-reference.html)
##.finally ```js .finally(function() handler) -> Promise ``` ```js .lastly(function() handler) -> Promise ``` Pass a handler that will be called regardless of this promise's fate. Returns a new promise chained from this promise. There are special semantics for [`.finally`](.) in that the final value cannot be modified from the handler. *Note: using [`.finally`](.) for resource management has better alternatives, see [resource management](/docs/api/resource-management.html)* Consider the example: ```js function anyway() { $("#ajax-loader-animation").hide(); } function ajaxGetAsync(url) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest; xhr.addEventListener("error", reject); xhr.addEventListener("load", resolve); xhr.open("GET", url); xhr.send(null); }).then(anyway, anyway); } ``` This example doesn't work as intended because the `then` handler actually swallows the exception and returns `undefined` for any further chainers. The situation can be fixed with `.finally`: ```js function ajaxGetAsync(url) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest; xhr.addEventListener("error", reject); xhr.addEventListener("load", resolve); xhr.open("GET", url); xhr.send(null); }).finally(function() { $("#ajax-loader-animation").hide(); }); } ``` Now the animation is hidden but, unless it throws an exception, the function has no effect on the fulfilled or rejected value of the returned promise. This is similar to how the synchronous `finally` keyword behaves. If the handler function passed to `.finally` returns a promise, the promise returned by `.finally` will not be settled until the promise returned by the handler is settled. If the handler fulfills its promise, the returned promise will be fulfilled or rejected with the original value. If the handler rejects its promise, the returned promise will be rejected with the handler's value. This is similar to throwing an exception in a synchronous `finally` block, causing the original value or exception to be forgotten. This delay can be useful if the actions performed by the handler are done asynchronously. For example: ```js function ajaxGetAsync(url) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest; xhr.addEventListener("error", reject); xhr.addEventListener("load", resolve); xhr.open("GET", url); xhr.send(null); }).finally(function() { return Promise.fromCallback(function(callback) { $("#ajax-loader-animation").fadeOut(1000, callback); }); }); } ``` If the fade out completes successfully, the returned promise will be fulfilled or rejected with the value from `xhr`. If `.fadeOut` throws an exception or passes an error to the callback, the returned promise will be rejected with the error from `.fadeOut`. *For compatibility with earlier ECMAScript version, an alias `.lastly` is provided for [`.finally`](.).*
================================================ FILE: docs/docs/api/generators.md ================================================ --- layout: api id: generators title: Generators --- [← Back To API Reference](/docs/api-reference.html)
##Generators Using ECMAScript6 generators feature to implement C# 5.0 `async/await` like syntax.
================================================ FILE: docs/docs/api/get.md ================================================ --- layout: api id: get title: .get --- [← Back To API Reference](/docs/api-reference.html)
##.get ```js .get(String propertyName|int index) -> Promise ``` This is a convenience method for doing: ```js promise.then(function(obj) { return obj[propertyName]; }); ``` For example: ```js db.query("...") .get(0) .then(function(firstRow) { }); ``` If `index` is negative, the indexed load will become `obj.length + index`. So that -1 can be used to read last item in the array, -2 to read the second last and so on. For example: ```js Promise.resolve([1,2,3]).get(-1).then(function(value) { console.log(value); // 3 }); ``` If the `index` is still negative after `obj.length + index`, it will be clamped to 0.
================================================ FILE: docs/docs/api/iscancelled.md ================================================ --- layout: api id: iscancelled title: .isCancelled --- [← Back To API Reference](/docs/api-reference.html)
##.isCancelled ```js .isCancelled() -> boolean ``` See if this `promise` has been cancelled.
================================================ FILE: docs/docs/api/isfulfilled.md ================================================ --- layout: api id: isfulfilled title: .isFulfilled --- [← Back To API Reference](/docs/api-reference.html)
##.isFulfilled ```js .isFulfilled() -> boolean ``` See if this promise has been fulfilled.
================================================ FILE: docs/docs/api/ispending.md ================================================ --- layout: api id: ispending title: .isPending --- [← Back To API Reference](/docs/api-reference.html)
##.isPending ```js .isPending() -> boolean ``` See if this `promise` is pending (not fulfilled or rejected or cancelled).
================================================ FILE: docs/docs/api/isrejected.md ================================================ --- layout: api id: isrejected title: .isRejected --- [← Back To API Reference](/docs/api-reference.html)
##.isRejected ```js .isRejected() -> boolean ``` See if this promise has been rejected.
================================================ FILE: docs/docs/api/map.md ================================================ --- layout: api id: map title: .map --- [← Back To API Reference](/docs/api-reference.html)
##.map ```js .map( function(any item, int index, int length) mapper, [Object {concurrency: int=Infinity} options] ) -> Promise ``` Same as [Promise.map(this, mapper, options)](.).
================================================ FILE: docs/docs/api/mapseries.md ================================================ --- layout: api id: mapseries title: .mapseries --- [← Back To API Reference](/docs/api-reference.html)
##.mapSeries ```js .mapSeries(function(any item, int index, int length) mapper) -> Promise ``` Same as [Promise.mapSeries(this, iterator)](.).
================================================ FILE: docs/docs/api/new-promise.md ================================================ --- layout: api id: new-promise title: new Promise --- [← Back To API Reference](/docs/api-reference.html)
##new Promise ```js new Promise(function(function resolve, function reject) resolver) -> Promise ``` Create a new promise. The passed in function will receive functions `resolve` and `reject` as its arguments which can be called to seal the fate of the created promise. *Note: See [explicit construction anti-pattern]({{ "/docs/anti-patterns.html#the-explicit-construction-anti-pattern" | prepend: site.baseurl }}) before creating promises yourself* Example: ```js function ajaxGetAsync(url) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest; xhr.addEventListener("error", reject); xhr.addEventListener("load", resolve); xhr.open("GET", url); xhr.send(null); }); } ``` If you pass a promise object to the `resolve` function, the created promise will follow the state of that promise.
To make sure a function that returns a promise is following the implicit but critically important contract of promises, you can start a function with `new Promise` if you cannot start a chain immediately: ```js function getConnection(urlString) { return new Promise(function(resolve) { //Without new Promise, this throwing will throw an actual exception var params = parse(urlString); resolve(getAdapter(params).getConnection()); }); } ``` The above ensures `getConnection` fulfills the contract of a promise-returning function of never throwing a synchronous exception. Also see [`Promise.try`](.) and [`Promise.method`](.) The resolver is called synchronously (the following is for documentation purposes and not idiomatic code): ```js function getPromiseResolveFn() { var res; new Promise(function (resolve) { res = resolve; }); // res is guaranteed to be set return res; } ```
================================================ FILE: docs/docs/api/operationalerror.md ================================================ --- layout: api id: operationalerror title: OperationalError --- [← Back To API Reference](/docs/api-reference.html)
##OperationalError ```js new OperationalError(String message) -> OperationalError ``` Represents an error is an explicit promise rejection as opposed to a thrown error. For example, if an error is errbacked by a callback API promisified through [`Promise.promisify`](.) or [`Promise.promisifyAll`](.) and is not a typed error, it will be converted to a `OperationalError` which has the original error in the `.cause` property. `OperationalError`s are caught in [`.error`](.) handlers.
================================================ FILE: docs/docs/api/progression-migration.md ================================================ --- layout: api id: progression-migration title: Progression migration --- [← Back To API Reference](/docs/api-reference.html)
##Progression migration Progression has been removed as there are composability and chaining issues with APIs that use promise progression handlers. Implementing the common use case of progress bars can be accomplished using a pattern similar to [IProgress](http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx) in C#. For old code that still uses it, see [the progression docs in the deprecated API documentation](/docs/deprecated-apis.html#progression). Using jQuery before: ```js Promise.resolve($.get(...)) .progressed(function() { // ... }) .then(function() { // ... }) .catch(function(e) { // ... }) ``` Using jQuery after: ```js Promise.resolve($.get(...).progress(function() { // ... })) .then(function() { // ... }) .catch(function(e) { // ... }) ``` Implementing general progress interfaces like in C#: ```js function returnsPromiseWithProgress(progressHandler) { return doFirstAction().tap(function() { progressHandler(0.33); }).then(doSecondAction).tap(function() { progressHandler(0.66); }).then(doThirdAction).tap(function() { progressHandler(1.00); }); } returnsPromiseWithProgress(function(progress) { ui.progressbar.setWidth((progress * 200) + "px"); // update width on client side }).then(function(value) { // action complete // entire chain is complete. }).catch(function(e) { // error }); ``` Another example using `coroutine`: ```js var doNothing = function() {}; var progressSupportingCoroutine = Promise.coroutine(function* (progress) { progress = typeof progress === "function" ? progress : doNothing; var first = yield getFirstValue(); // 33% done progress(0.33); var second = yield getSecondValue(); progress(0.67); var third = yield getThirdValue(); progress(1); return [first, second, third]; }); var progressConsumingCoroutine = Promise.coroutine(function* () { var allValues = yield progressSupportingCoroutine(function(p) { ui.progressbar.setWidth((p * 200) + "px"); }); var second = allValues[1]; // ... }); ```
================================================ FILE: docs/docs/api/promise.all.md ================================================ --- layout: api id: promise.all title: Promise.all --- [← Back To API Reference](/docs/api-reference.html)
##Promise.all ```js Promise.all(Iterable|Promise> input) -> Promise> ``` This method is useful for when you want to wait for more than one promise to complete. Given an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)\(arrays are `Iterable`\), or a promise of an `Iterable`, which produces promises (or a mix of promises and values), iterate over all the values in the `Iterable` into an array and return a promise that is fulfilled when all the items in the array are fulfilled. The promise's fulfillment value is an array with fulfillment values at respective positions to the original array. If any promise in the array rejects, the returned promise is rejected with the rejection reason. ```js var files = []; for (var i = 0; i < 100; ++i) { files.push(fs.writeFileAsync("file-" + i + ".txt", "", "utf-8")); } Promise.all(files).then(function() { console.log("all the files were created"); }); ``` This method is compatible with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) from native promises.
================================================ FILE: docs/docs/api/promise.any.md ================================================ --- layout: api id: promise.any title: Promise.any --- [← Back To API Reference](/docs/api-reference.html)
##Promise.any ```js Promise.any(Iterable|Promise> input) -> Promise ``` Like [Promise.some](.), with 1 as `count`. However, if the promise fulfills, the fulfillment value is not an array of 1 but the value directly.
================================================ FILE: docs/docs/api/promise.bind.md ================================================ --- layout: api id: promise.bind title: Promise.bind --- [← Back To API Reference](/docs/api-reference.html)
##Promise.bind ```js Promise.bind( any|Promise thisArg, [any|Promise value=undefined] ) -> BoundPromise ``` Create a promise that follows this promise or in the static method is resolved with the given `value`, but is bound to the given `thisArg` value. A bound promise will call its handlers with the bound value set to `this`. Additionally promises derived from a bound promise will also be bound promises with the same `thisArg` binding as the original promise. If `thisArg` is a promise or thenable, its resolution will be awaited for and the bound value will be the promise's fulfillment value. If `thisArg` rejects then the returned promise is rejected with the `thisArg's` rejection reason. Note that this means you cannot use `this` without checking inside catch handlers for promises that bind to promise because in case of rejection of `thisArg`, `this` will be `undefined`.
Without arrow functions that provide lexical `this`, the correspondence between async and sync code breaks down when writing object-oriented code. [`.bind`](.) alleviates this. Consider: ```js MyClass.prototype.method = function() { try { var contents = fs.readFileSync(this.file); var url = urlParse(contents); var result = this.httpGetSync(url); var refined = this.refine(result); return this.writeRefinedSync(refined); } catch (e) { this.error(e.stack); } }; ``` The above has a direct translation: ```js MyClass.prototype.method = function() { return fs.readFileAsync(this.file).bind(this) .then(function(contents) { var url = urlParse(contents); return this.httpGetAsync(url); }).then(function(result) { var refined = this.refine(result); return this.writeRefinedAsync(refined); }).catch(function(e) { this.error(e.stack); }); }; ``` `.bind` is the most efficient way of utilizing `this` with promises. The handler functions in the above code are not closures and can therefore even be hoisted out if needed. There is literally no overhead when propagating the bound value from one promise to another.
`.bind` also has a useful side purpose - promise handlers don't need to share a function to use shared state: ```js somethingAsync().bind({}) .spread(function (aValue, bValue) { this.aValue = aValue; this.bValue = bValue; return somethingElseAsync(aValue, bValue); }) .then(function (cValue) { return this.aValue + this.bValue + cValue; }); ``` The above without [`.bind`](.) could be achieved with: ```js var scope = {}; somethingAsync() .spread(function (aValue, bValue) { scope.aValue = aValue; scope.bValue = bValue; return somethingElseAsync(aValue, bValue); }) .then(function (cValue) { return scope.aValue + scope.bValue + cValue; }); ``` However, there are many differences when you look closer: - Requires a statement so cannot be used in an expression context - If not there already, an additional wrapper function is required to undefined leaking or sharing `scope` - The handler functions are now closures, thus less efficient and not reusable
Note that bind is only propagated with promise transformation. If you create new promise chains inside a handler, those chains are not bound to the "upper" `this`: ```js something().bind(var1).then(function() { //`this` is var1 here return Promise.all(getStuff()).then(function(results) { //`this` is undefined here //refine results here etc }); }).then(function() { //`this` is var1 here }); ``` However, if you are utilizing the full bluebird API offering, you will *almost never* need to resort to nesting promises in the first place. The above should be written more like: ```js something().bind(var1).then(function() { //`this` is var1 here return getStuff(); }).map(function(result) { //`this` is var1 here //refine result here }).then(function() { //`this` is var1 here }); ``` Also see this [Stackoverflow answer](http://stackoverflow.com/a/24412873/191693) as an additional example.
If you don't want to return a bound promise to the consumers of a promise, you can rebind the chain at the end: ```js MyClass.prototype.method = function() { return fs.readFileAsync(this.file).bind(this) .then(function(contents) { var url = urlParse(contents); return this.httpGetAsync(url); }).then(function(result) { var refined = this.refine(result); return this.writeRefinedAsync(refined); }).catch(function(e) { this.error(e.stack); }).bind(); //The `thisArg` is implicitly undefined - I.E. the default promise `this` value }; ``` Rebinding can also be abused to do something gratuitous like this: ```js Promise.resolve("my-element") .bind(document) .then(document.getElementById) .bind(console) .then(console.log); ``` The above does a `console.log` of `my-element`. Doing it this way is necessary because neither of the methods (`getElementById`, `console.log`) can be called as stand-alone methods.
================================================ FILE: docs/docs/api/promise.config.md ================================================ --- layout: api id: promise.config title: Promise.config --- [← Back To API Reference](/docs/api-reference.html)
##Promise.config ```js Promise.config(Object { warnings: boolean=false, longStackTraces: boolean=false, cancellation: boolean=false, monitoring: boolean=false, asyncHooks: boolean=false } options) -> Object; ``` Configure long stack traces, warnings, monitoring, [async hooks](https://nodejs.org/api/async_hooks.html) and cancellation. Note that even though `false` is the default here, a development environment might be detected which automatically enables long stack traces and warnings. For **webpack** and **browserify** *development* environment is *always* enabled. See [installation](/docs/install.html#browserify-and-webpack) on how to configure webpack and browserify for production. ```js Promise.config({ // Enable warnings warnings: true, // Enable long stack traces longStackTraces: true, // Enable cancellation cancellation: true, // Enable monitoring monitoring: true, // Enable async hooks asyncHooks: true, }); ``` You can configure the warning for checking forgotten return statements with `wForgottenReturn`: ```js Promise.config({ // Enables all warnings except forgotten return statements. warnings: { wForgottenReturn: false } }); ``` `wForgottenReturn` is the only warning type that can be separately configured. The corresponding environmental variable key is `BLUEBIRD_W_FORGOTTEN_RETURN`.
In Node.js you may configure warnings and long stack traces for the entire process using environment variables: ``` BLUEBIRD_LONG_STACK_TRACES=1 BLUEBIRD_WARNINGS=1 node app.js ``` Both features are automatically enabled if the `BLUEBIRD_DEBUG` environment variable has been set or if the `NODE_ENV` environment variable is equal to `"development"`. Using the value `0` will explicitly disable a feature despite debug environment otherwise activating it: ``` # Warnings are disabled despite being in development environment NODE_ENV=development BLUEBIRD_WARNINGS=0 node app.js ``` Cancellation is always configured separately per bluebird instance. # Async hooks Bluebird supports [async hooks](https://nodejs.org/api/async_hooks.html) in node versions 9.6.0 and later. After it is enabled promises from the bluebird instance are assigned unique asyncIds: ```js // Async hooks disabled for bluebird const ah = require('async_hooks'); const Promise = require("bluebird"); Promise.resolve().then(() => { console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`); // }); ``` ```js // Async hooks enabled for bluebird const ah = require('async_hooks'); const Promise = require("bluebird"); Promise.config({asyncHooks: true}); Promise.resolve().then(() => { console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`); // }); ```
================================================ FILE: docs/docs/api/promise.coroutine.addyieldhandler.md ================================================ --- layout: api id: promise.coroutine.addyieldhandler title: Promise.coroutine.addYieldHandler --- [← Back To API Reference](/docs/api-reference.html)
##Promise.coroutine.addYieldHandler ```js Promise.coroutine.addYieldHandler(function handler) -> undefined ``` By default you can only yield Promises and Thenables inside coroutines. You can use this function to add yielding support for arbitrary types. For example, if you wanted `yield 500` to be same as `yield Promise.delay`: ```js Promise.coroutine.addYieldHandler(function(value) { if (typeof value === "number") return Promise.delay(value); }); ``` Yield handlers are called when you yield something that is not supported by default. The first yield handler to return a promise or a thenable will be used. If no yield handler returns a promise or a thenable then an error is raised. An example of implementing callback support with `addYieldHandler`: *This is a demonstration of how powerful the feature is and not the recommended usage. For best performance you need to use `promisifyAll` and yield promises directly.* ```js var Promise = require("bluebird"); var fs = require("fs"); var _ = (function() { var promise = null; Promise.coroutine.addYieldHandler(function(v) { if (v === undefined && promise != null) { return promise; } promise = null; }); return function() { var def = Promise.defer(); promise = def.promise; return def.callback; }; })(); var readFileJSON = Promise.coroutine(function* (fileName) { var contents = yield fs.readFile(fileName, "utf8", _()); return JSON.parse(contents); }); ``` An example of implementing thunks support with `addYieldHandler`: *This is a demonstration of how powerful the feature is and not the recommended usage. For best performance you need to use `promisifyAll` and yield promises directly.* ```js var Promise = require("bluebird"); var fs = require("fs"); Promise.coroutine.addYieldHandler(function(v) { if (typeof v === "function") { return Promise.fromCallback(function(cb) { v(cb); }); } }); var readFileThunk = function(fileName, encoding) { return function(cb) { return fs.readFile(fileName, encoding, cb); }; }; var readFileJSON = Promise.coroutine(function* (fileName) { var contents = yield readFileThunk(fileName, "utf8"); return JSON.parse(contents); }); ``` An example of handling promises in parallel by adding an `addYieldHandler` for arrays : ```js var Promise = require("bluebird"); var fs = Promise.promisifyAll(require("fs")); Promise.coroutine.addYieldHandler(function(yieldedValue) { if (Array.isArray(yieldedValue)) return Promise.all(yieldedValue); }); var readFiles = Promise.coroutine(function* (fileNames) { return yield fileNames.map(function (fileName) { return fs.readFileAsync(fileName, "utf8"); }); }); ``` A custom yield handler can also be used just for a single call to `Promise.coroutine()`: ```js var Promise = require("bluebird"); var fs = Promise.promisifyAll(require("fs")); var readFiles = Promise.coroutine(function* (fileNames) { return yield fileNames.map(function (fileName) { return fs.readFileAsync(fileName, "utf8"); }); }, { yieldHandler: function(yieldedValue) { if (Array.isArray(yieldedValue)) return Promise.all(yieldedValue); } }); ```
================================================ FILE: docs/docs/api/promise.coroutine.md ================================================ --- layout: api id: promise.coroutine title: Promise.coroutine --- [← Back To API Reference](/docs/api-reference.html)
##Promise.coroutine ```js Promise.coroutine(GeneratorFunction(...arguments) generatorFunction, Object options) -> function ``` Returns a function that can use `yield` to yield promises. Control is returned back to the generator when the yielded promise settles. This can lead to less verbose code when doing lots of sequential async calls with minimal processing in between. Requires node.js 0.12+, io.js 1.0+ or Google Chrome 40+. ```js var Promise = require("bluebird"); function PingPong() { } PingPong.prototype.ping = Promise.coroutine(function* (val) { console.log("Ping?", val); yield Promise.delay(500); this.pong(val+1); }); PingPong.prototype.pong = Promise.coroutine(function* (val) { console.log("Pong!", val); yield Promise.delay(500); this.ping(val+1); }); var a = new PingPong(); a.ping(0); ``` Running the example: $ node test.js Ping? 0 Pong! 1 Ping? 2 Pong! 3 Ping? 4 Pong! 5 Ping? 6 Pong! 7 Ping? 8 ... When called, the coroutine function will start an instance of the generator and returns a promise for its final value. Doing `Promise.coroutine` is almost like using the C# `async` keyword to mark the function, with `yield` working as the `await` keyword. Promises are somewhat like `Task`s. **Tip** You are able to yield non-promise values by adding your own yield handler using [`Promise.coroutine.addYieldHandler`](.) or calling `Promise.coroutine()` with a yield handler function as `options.yieldHandler`.
================================================ FILE: docs/docs/api/promise.delay.md ================================================ --- layout: api id: promise.delay title: Promise.delay --- [← Back To API Reference](/docs/api-reference.html)
##Promise.delay ```js Promise.delay( int ms, [any|Promise value=undefined] ) -> Promise ``` Returns a promise that will be resolved with `value` (or `undefined`) after given `ms` milliseconds. If `value` is a promise, the delay will start counting down when it is fulfilled and the returned promise will be fulfilled with the fulfillment value of the `value` promise. If `value` is a rejected promise, the resulting promise will be rejected immediately. ```js Promise.delay(500).then(function() { console.log("500 ms passed"); return "Hello world"; }).delay(500).then(function(helloWorldString) { console.log(helloWorldString); console.log("another 500 ms passed") ; }); ```
================================================ FILE: docs/docs/api/promise.each.md ================================================ --- layout: api id: promise.each title: Promise.each --- [← Back To API Reference](/docs/api-reference.html)
##Promise.each ```js Promise.each( Iterable|Promise> input, function(any value, int index, int arrayLength) iterator ) -> Promise> ``` Given an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) (an array, for example), or a promise of an `Iterable`, iterates serially over all the values in it, executing the given `iterator` on each element. If an element is a promise, the iterator will wait for it before proceeding. The `iterator` function has signature `(value, index, arrayLength)` where `value` is the current element (or its resolved value if it is a promise). If, at any step: * The iterator returns a promise or a thenable, it is awaited before continuing to the next iteration. * The current element of the iteration is a *pending* promise, that promise will be awaited before running the iterator. * The current element of the iteration is a *rejected* promise, the iteration will stop and be rejected as well (with the same reason). If all iterations resolve successfully, the `Promise.each` call resolves to a new array containing the resolved values of the original input elements. `Promise.each` is very similar to [Promise.mapSeries](.). The difference between `Promise.each` and `Promise.mapSeries` is their resolution value. `Promise.each` resolves with an array as explained above, while `Promise.mapSeries` resolves with an array containing the *outputs* of the iterator function on each step. This way, `Promise.each` is meant to be mainly used for side-effect operations (since the outputs of the iterator are essentially discarded), just like the native `.forEach()` method of arrays, while `Promise.map` is meant to be used as an async version of the native `.map()` method of arrays. Basic example: ```js // The array to be iterated over can be a mix of values and promises. var fileNames = ["1.txt", Promise.resolve("2.txt"), "3.txt", Promise.delay(3000, "4.txt"), "5.txt"]; Promise.each(fileNames, function(fileName, index, arrayLength) { // The iteration will be performed sequentially, awaiting for any // promises in the process. return fs.readFileAsync(fileName).then(function(fileContents) { // ... // The final resolution value of the iterator is is irrelevant, // since the result of the `Promise.each` has nothing to do with // the outputs of the iterator. return "anything"; // Doesn't matter }); }).then(function(result) { // This will run after the last step is done console.log("Done!") console.log(result); // ["1.txt", "2.txt", "3.txt", "4.txt", "5.txt"] }); ``` ##Example ```js let stories = [ '8952', '8884']; Promise.each(stories, (item, idx, length) => { return request.getAsync(`https://hacker-news.firebaseio.com/v0/item/${item}.json?print=pretty`) .then(res => { // need to do explicit operations here }) }) .then(result => { console.log('ALL done', result);//['8952', '8884'] }) ``` [api/promise.each](unfinished-article) ======= Example with a rejected promise in the array: ```js // If one of the promises in the original array rejects, // the iteration will stop once it reaches it var items = ["A", Promise.delay(8000, "B"), Promise.reject("C"), "D"]; Promise.each(items, function(item) { return Promise.delay(4000).then(function() { console.log("On iterator: " + item); }); }).then(function(result) { // This not run }).catch(function(rejection) { console.log("Catch: " + rejection); }); // The code above outputs the following after 12 seconds (not 16!): // On iterator: A // On iterator: B // Catch: C ```
================================================ FILE: docs/docs/api/promise.filter.md ================================================ --- layout: api id: promise.filter title: Promise.filter --- [← Back To API Reference](/docs/api-reference.html)
##Promise.filter ```js Promise.filter( Iterable|Promise> input, function(any item, int index, int length) filterer, [Object {concurrency: int=Infinity} options] ) -> Promise ``` Given an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)\(arrays are `Iterable`\), or a promise of an `Iterable`, which produces promises (or a mix of promises and values), iterate over all the values in the `Iterable` into an array and [filter the array to another](http://en.wikipedia.org/wiki/Filter_\(higher-order_function\)) using the given `filterer` function. It is essentially an efficient shortcut for doing a [.map](.) and then [`Array#filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter): ```js Promise.map(valuesToBeFiltered, function(value, index, length) { return Promise.all([filterer(value, index, length), value]); }).then(function(values) { return values.filter(function(stuff) { return stuff[0] == true }).map(function(stuff) { return stuff[1]; }); }); ``` Example for filtering files that are accessible directories in the current directory: ```js var Promise = require("bluebird"); var E = require("core-error-predicates"); var fs = Promise.promisifyAll(require("fs")); fs.readdirAsync(process.cwd()).filter(function(fileName) { return fs.statAsync(fileName) .then(function(stat) { return stat.isDirectory(); }) .catch(E.FileAccessError, function() { return false; }); }).each(function(directoryName) { console.log(directoryName, " is an accessible directory"); }); ``` ####Filter Option: concurrency See [Map Option: concurrency](#map-option-concurrency)
================================================ FILE: docs/docs/api/promise.fromcallback.md ================================================ --- layout: api id: promise.fromcallback title: Promise.fromCallback --- [← Back To API Reference](/docs/api-reference.html)
##Promise.fromCallback ```js Promise.fromCallback( function(function callback) resolver, [Object {multiArgs: boolean=false} options] ) -> Promise ``` ```js Promise.fromNode( function(function callback) resolver, [Object {multiArgs: boolean=false} options] ) -> Promise ``` Returns a promise that is resolved by a node style callback function. This is the most fitting way to do on the fly promisification when libraries don't expose classes for automatic promisification by undefined. The resolver function is passed a callback that expects to be called back according to error-first node conventions. Setting `multiArgs` to `true` means the resulting promise will always fulfill with an array of the callback's success value(s). This is needed because promises only support a single success value while some callback API's have multiple success value. The default is to ignore all but the first success value of a callback function. Using manual resolver: ```js var Promise = require("bluebird"); // "email-templates" doesn't expose prototypes for promisification var emailTemplates = Promise.promisify(require('email-templates')); var templatesDir = path.join(__dirname, 'templates'); emailTemplates(templatesDir).then(function(template) { return Promise.fromCallback(function(callback) { return template("newsletter", callback); }, {multiArgs: true}).spread(function(html, text) { console.log(html, text); }); }); ``` The same can also be written more concisely with `Function.prototype.bind`: ```js var Promise = require("bluebird"); // "email-templates" doesn't expose prototypes for promisification var emailTemplates = Promise.promisify(require('email-templates')); var templatesDir = path.join(__dirname, 'templates'); emailTemplates(templatesDir).then(function(template) { return Promise.fromCallback(template.bind(null, "newsletter"), {multiArgs: true}) .spread(function(html, text) { console.log(html, text); }); }); ```
================================================ FILE: docs/docs/api/promise.getnewlibrarycopy.md ================================================ --- layout: api id: promise.getnewlibrarycopy title: Promise.getNewLibraryCopy --- [← Back To API Reference](/docs/api-reference.html)
##Promise.getNewLibraryCopy ```js Promise.getNewLibraryCopy() -> Object ``` Returns a new independent copy of the Bluebird library. This method should be used before you use any of the methods which would otherwise alter the global `Bluebird` object - to avoid polluting global state. A basic example: ```js var Promise = require('bluebird'); var Promise2 = Promise.getNewLibraryCopy(); Promise2.x = 123; console.log(Promise2 == Promise); // false console.log(Promise2.x); // 123 console.log(Promise.x); // undefined ``` `Promise2` is independent to `Promise`. Any changes to `Promise2` do not affect the copy of Bluebird returned by `require('bluebird')`. In practice: ```js var Promise = require('bluebird').getNewLibraryCopy(); Promise.coroutine.addYieldHandler( function() { /* */ } ); // alters behavior of `Promise.coroutine()` // somewhere in another file or module in same app var Promise = require('bluebird'); Promise.coroutine(function*() { // this code is unaffected by the yieldHandler defined above // because it was defined on an independent copy of Bluebird }); ```
================================================ FILE: docs/docs/api/promise.join.md ================================================ --- layout: api id: promise.join title: Promise.join --- [← Back To API Reference](/docs/api-reference.html)
##Promise.join ```js Promise.join( Promise|any values..., function handler ) -> Promise ``` For coordinating multiple concurrent discrete promises. While [`.all`](.) is good for handling a dynamically sized list of uniform promises, `Promise.join` is much easier (and more performant) to use when you have a fixed amount of discrete promises that you want to coordinate concurrently. The final parameter, handler function, will be invoked with the result values of all of the fulfilled promises. For example: ```js var Promise = require("bluebird"); var join = Promise.join; join(getPictures(), getComments(), getTweets(), function(pictures, comments, tweets) { console.log("in total: " + pictures.length + comments.length + tweets.length); }); ``` ```js var Promise = require("bluebird"); var fs = Promise.promisifyAll(require("fs")); var pg = require("pg"); Promise.promisifyAll(pg, { filter: function(methodName) { return methodName === "connect" }, multiArgs: true }); // Promisify rest of pg normally Promise.promisifyAll(pg); var join = Promise.join; var connectionString = "postgres://username:password@localhost/database"; var fContents = fs.readFileAsync("file.txt", "utf8"); var fStat = fs.statAsync("file.txt"); var fSqlClient = pg.connectAsync(connectionString).spread(function(client, done) { client.close = done; return client; }); join(fContents, fStat, fSqlClient, function(contents, stat, sqlClient) { var query = " \ INSERT INTO files (byteSize, contents) \ VALUES ($1, $2) \ "; return sqlClient.queryAsync(query, [stat.size, contents]).thenReturn(query); }) .then(function(query) { console.log("Successfully ran the Query: " + query); }) .finally(function() { // This is why you want to use Promise.using for resource management if (fSqlClient.isFulfilled()) { fSqlClient.value().close(); } }); ``` *Note: In 1.x and 0.x `Promise.join` used to be a `Promise.all` that took the values in as arguments instead of an array. This behavior has been deprecated but is still supported partially - when the last argument is an immediate function value the new semantics will apply*
================================================ FILE: docs/docs/api/promise.longstacktraces.md ================================================ --- layout: api id: promise.longstacktraces title: Promise.longStackTraces --- [← Back To API Reference](/docs/api-reference.html)
## ~~Promise.longStackTraces~~ This method is deprecated. Use [Promise.config](/docs/api/promise.config.html) instead. ```js Promise.config({ longStackTraces: true }) ``` --- ```js Promise.longStackTraces() -> undefined ``` Call this right after the library is loaded to enable long stack traces. Long stack traces cannot be disabled after being enabled, and cannot be enabled after promises have already been created. Long stack traces imply a substantial performance penalty, around 4-5x for throughput and 0.5x for latency. Long stack traces are enabled by default in the debug build. To enable them in all instances of bluebird in node.js, use the environment variable `BLUEBIRD_DEBUG`: ``` BLUEBIRD_DEBUG=1 node server.js ``` Setting the environment variable `NODE_ENV` to `"development"` also automatically enables long stack traces. You should enabled long stack traces if you want better debugging experience. For example: ```js Promise.longStackTraces(); Promise.resolve().then(function outer() { return Promise.resolve().then(function inner() { return Promise.resolve().then(function evenMoreInner() { a.b.c.d() }).catch(function catcher(e) { console.error(e.stack); }); }); }); ``` Gives ReferenceError: a is not defined at evenMoreInner (:6:13) From previous event: at inner (:5:24) From previous event: at outer (:4:20) From previous event: at :3:9 at Object.InjectedScript._evaluateOn (:581:39) at Object.InjectedScript._evaluateAndWrap (:540:52) at Object.InjectedScript.evaluate (:459:21) While with long stack traces disabled, you would get: ReferenceError: a is not defined at evenMoreInner (:6:13) at tryCatch1 (:41:19) at Promise$_resolvePromise [as _resolvePromise] (:1739:13) at Promise$_resolveLast [as _resolveLast] (:1520:14) at Async$_consumeFunctionBuffer [as _consumeFunctionBuffer] (:560:33) at Async$consumeFunctionBuffer (:515:14) at MutationObserver.Promise$_Deferred (:433:17) On client side, long stack traces currently only work in recent Firefoxes, Chrome and Internet Explorer 10+.
================================================ FILE: docs/docs/api/promise.map.md ================================================ --- layout: api id: promise.map title: Promise.map --- [← Back To API Reference](/docs/api-reference.html)
##Promise.map ```js Promise.map( Iterable|Promise> input, function(any item, int index, int length) mapper, [Object {concurrency: int=Infinity} options] ) -> Promise ``` Given a finite [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)\(arrays are `Iterable`\), or a promise of an `Iterable`, which produces promises (or a mix of promises and values), iterate over all the values in the `Iterable` into an array and [map the array to another](http://en.wikipedia.org/wiki/Map_\(higher-order_function\)) using the given `mapper` function. Promises returned by the `mapper` function are awaited for and the returned promise doesn't fulfill until all mapped promises have fulfilled as well. If any promise in the array is rejected, or any promise returned by the `mapper` function is rejected, the returned promise is rejected as well. The mapper function for a given item is called as soon as possible, that is, when the promise for that item's index in the input array is fulfilled. This doesn't mean that the result array has items in random order, it means that `.map` can be used for concurrency coordination unlike `.all`. A common use of `Promise.map` is to replace the `.push`+`Promise.all` boilerplate: ```js var promises = []; for (var i = 0; i < fileNames.length; ++i) { promises.push(fs.readFileAsync(fileNames[i])); } Promise.all(promises).then(function() { console.log("done"); }); // Using Promise.map: Promise.map(fileNames, function(fileName) { // Promise.map awaits for returned promises as well. return fs.readFileAsync(fileName); }).then(function() { console.log("done"); }); // Using Promise.map and async/await: await Promise.map(fileNames, function(fileName) { // Promise.map awaits for returned promises as well. return fs.readFileAsync(fileName); }); console.log("done"); ``` A more involved example: ```js var Promise = require("bluebird"); var join = Promise.join; var fs = Promise.promisifyAll(require("fs")); fs.readdirAsync(".").map(function(fileName) { var stat = fs.statAsync(fileName); var contents = fs.readFileAsync(fileName).catch(function ignore() {}); return join(stat, contents, function(stat, contents) { return { stat: stat, fileName: fileName, contents: contents } }); // The return value of .map is a promise that is fulfilled with an array of the mapped values // That means we only get here after all the files have been statted and their contents read // into memory. If you need to do more operations per file, they should be chained in the map // callback for concurrency. }).call("sort", function(a, b) { return a.fileName.localeCompare(b.fileName); }).each(function(file) { var contentLength = file.stat.isDirectory() ? "(directory)" : file.contents.length + " bytes"; console.log(file.fileName + " last modified " + file.stat.mtime + " " + contentLength) }); ``` ####Map Option: concurrency You may optionally specify a concurrency limit: ```js ...map(..., {concurrency: 3}); ``` The concurrency limit applies to Promises returned by the mapper function and it basically limits the number of Promises created. For example, if `concurrency` is `3` and the mapper callback has been called enough so that there are three returned Promises currently pending, no further callbacks are called until one of the pending Promises resolves. So the mapper function will be called three times and it will be called again only after at least one of the Promises resolves. Playing with the first example with and without limits, and seeing how it affects the duration when reading 20 files: ```js var Promise = require("bluebird"); var join = Promise.join; var fs = Promise.promisifyAll(require("fs")); var concurrency = parseFloat(process.argv[2] || "Infinity"); console.time("reading files"); fs.readdirAsync(".").map(function(fileName) { var stat = fs.statAsync(fileName); var contents = fs.readFileAsync(fileName).catch(function ignore() {}); return join(stat, contents, function(stat, contents) { return { stat: stat, fileName: fileName, contents: contents } }); // The return value of .map is a promise that is fulfilled with an array of the mapped values // That means we only get here after all the files have been statted and their contents read // into memory. If you need to do more operations per file, they should be chained in the map // callback for concurrency. }, {concurrency: concurrency}).call("sort", function(a, b) { return a.fileName.localeCompare(b.fileName); }).then(function() { console.timeEnd("reading files"); }); ``` ```bash $ sync && echo 3 > /proc/sys/vm/drop_caches $ node test.js 1 reading files 35ms $ sync && echo 3 > /proc/sys/vm/drop_caches $ node test.js Infinity reading files: 9ms ``` The order `map` calls the mapper function on the array elements is not specified, there is no guarantee on the order in which it'll execute the `map`er on the elements. For order guarantee in sequential execution - see [Promise.mapSeries](.).
================================================ FILE: docs/docs/api/promise.mapseries.md ================================================ --- layout: api id: promise.mapseries title: Promise.mapSeries --- [← Back To API Reference](/docs/api-reference.html)
##Promise.mapSeries ```js Promise.mapSeries( Iterable|Promise> input, function(any value, int index, int arrayLength) mapper ) -> Promise> ``` Given an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) (an array, for example), or a promise of an `Iterable`, iterates serially over all the values in it, executing the given `mapper` on each element. If an element is a promise, the mapper will wait for it before proceeding. The `mapper` function has signature `(value, index, arrayLength)` where `value` is the current element (or its resolved value if it is a promise). If, at any step: * The mapper returns a promise or a thenable, it is awaited before continuing to the next iteration. * The current element of the iteration is a *pending* promise, that promise will be awaited before running the mapper. * The current element of the iteration is a *rejected* promise, the iteration will stop and be rejected as well (with the same reason). If all iterations resolve successfully, the `Promise.mapSeries` call resolves to a new array containing the results of each `mapper` execution, in order. `Promise.mapSeries` is very similar to [Promise.each](.). The difference between `Promise.each` and `Promise.mapSeries` is their resolution value. `Promise.mapSeries` resolves with an array as explained above, while `Promise.each` resolves with an array containing the *resolved values of the input elements* (ignoring the outputs of the iteration steps). This way, `Promise.each` is meant to be mainly used for side-effect operations (since the outputs of the iterator are essentially discarded), just like the native `.forEach()` method of arrays, while `Promise.map` is meant to be used as an async version of the native `.map()` method of arrays. Basic example: ```js // The array to be mapped over can be a mix of values and promises. var fileNames = ["1.txt", Promise.resolve("2.txt"), "3.txt", Promise.delay(3000, "4.txt"), "5.txt"]; Promise.mapSeries(fileNames, function(fileName, index, arrayLength) { // The iteration will be performed sequentially, awaiting for any // promises in the process. return fs.readFileAsync(fileName).then(function(fileContents) { // ... return fileName + "!"; }); }).then(function(result) { // This will run after the last step is done console.log("Done!") console.log(result); // ["1.txt!", "2.txt!", "3.txt!", "4.txt!", "5.txt!"] }); ``` Example with a rejected promise in the array: ```js // If one of the promises in the original array rejects, // the iteration will stop once it reaches it var items = ["A", Promise.delay(8000, "B"), Promise.reject("C"), "D"]; Promise.each(items, function(item) { return Promise.delay(4000).then(function() { console.log("On mapper: " + item); }); }).then(function(result) { // This not run }).catch(function(rejection) { console.log("Catch: " + rejection); }); // The code above outputs the following after 12 seconds (not 16!): // On mapper: A // On mapper: B // Catch: C ```
================================================ FILE: docs/docs/api/promise.method.md ================================================ --- layout: api id: promise.method title: Promise.method --- [← Back To API Reference](/docs/api-reference.html)
##Promise.method ```js Promise.method(function(...arguments) fn) -> function ``` Returns a new function that wraps the given function `fn`. The new function will always return a promise that is fulfilled with the original functions return values or rejected with thrown exceptions from the original function. This method is convenient when a function can sometimes return synchronously or throw synchronously. Example without using `Promise.method`: ```js MyClass.prototype.method = function(input) { if (!this.isValid(input)) { return Promise.reject(new TypeError("input is not valid")); } if (this.cache(input)) { return Promise.resolve(this.someCachedValue); } return db.queryAsync(input).bind(this).then(function(value) { this.someCachedValue = value; return value; }); }; ``` Using the same function `Promise.method`, there is no need to manually wrap direct return or throw values into a promise: ```js MyClass.prototype.method = Promise.method(function(input) { if (!this.isValid(input)) { throw new TypeError("input is not valid"); } if (this.cache(input)) { return this.someCachedValue; } return db.queryAsync(input).bind(this).then(function(value) { this.someCachedValue = value; return value; }); }); ```
================================================ FILE: docs/docs/api/promise.noconflict.md ================================================ --- layout: api id: promise.noconflict title: Promise.noConflict --- [← Back To API Reference](/docs/api-reference.html)
##Promise.noConflict ```js Promise.noConflict() -> Object ``` This is relevant to browser environments with no module loader. Release control of the `Promise` namespace to whatever it was before this library was loaded. Returns a reference to the library namespace so you can attach it to something else. ```html ```
================================================ FILE: docs/docs/api/promise.onpossiblyunhandledrejection.md ================================================ --- layout: api id: promise.onpossiblyunhandledrejection title: Promise.onPossiblyUnhandledRejection --- [← Back To API Reference](/docs/api-reference.html)
##Promise.onPossiblyUnhandledRejection ```js Promise.onPossiblyUnhandledRejection(function(any error, Promise promise) handler) -> undefined ``` *Note: this hook is specific to the bluebird instance it's called on, application developers should use [global rejection events](/docs/api/error-management-configuration.html#global-rejection-events)* Add `handler` as the handler to call when there is a possibly unhandled rejection. The default handler logs the error stack to stderr or `console.error` in browsers. ```js Promise.onPossiblyUnhandledRejection(function(e, promise) { throw e; }); ``` Passing no value or a non-function will have the effect of removing any kind of handling for possibly unhandled rejections.
================================================ FILE: docs/docs/api/promise.onunhandledrejectionhandled.md ================================================ --- layout: api id: promise.onunhandledrejectionhandled title: Promise.onUnhandledRejectionHandled --- [← Back To API Reference](/docs/api-reference.html)
##Promise.onUnhandledRejectionHandled ```js Promise.onUnhandledRejectionHandled(function(Promise promise) handler) -> undefined ``` *Note: this hook is specific to the bluebird instance its called on, application developers should use [global rejection events](/docs/api/error-management-configuration.html#global-rejection-events)* Add `handler` as the handler to call when a rejected promise that was reported as "possibly unhandled rejection" became handled. Together with `onPossiblyUnhandledRejection` these hooks can be used to implement a debugger that will show a list of unhandled promise rejections updated in real time as promises become handled. For example: ```js var unhandledPromises = []; Promise.onPossiblyUnhandledRejection(function(reason, promise) { unhandledPromises.push(promise); //Update some debugger UI }); Promise.onUnhandledRejectionHandled(function(promise) { var index = unhandledPromises.indexOf(promise); unhandledPromises.splice(index, 1); //Update the debugger UI }); ```
================================================ FILE: docs/docs/api/promise.promisify.md ================================================ --- layout: api id: promise.promisify title: Promise.promisify --- [← Back To API Reference](/docs/api-reference.html)
##Promise.promisify ```js Promise.promisify( function(any arguments..., function callback) nodeFunction, [Object { multiArgs: boolean=false, context: any=this } options] ) -> function ``` Returns a function that will wrap the given `nodeFunction`. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument. If the `nodeFunction` calls its callback with multiple success values, the fulfillment value will be the first fulfillment item. Setting `multiArgs` to `true` means the resulting promise will always fulfill with an array of the callback's success value(s). This is needed because promises only support a single success value while some callback API's have multiple success value. The default is to ignore all but the first success value of a callback function. If you pass a `context`, the `nodeFunction` will be called as a method on the `context`. Example of promisifying the asynchronous `readFile` of node.js `fs`-module: ```js var readFile = Promise.promisify(require("fs").readFile); readFile("myfile.js", "utf8").then(function(contents) { return eval(contents); }).then(function(result) { console.log("The result of evaluating myfile.js", result); }).catch(SyntaxError, function(e) { console.log("File had syntax error", e); //Catch any other error }).catch(function(e) { console.log("Error reading file", e); }); ``` Note that if the node function is a method of some object, you can pass the object as the second argument like so: ```js var redisGet = Promise.promisify(redisClient.get, {context: redisClient}); redisGet('foo').then(function() { //... }); ``` But this will also work: ```js var getAsync = Promise.promisify(redisClient.get); getAsync.call(redisClient, 'foo').then(function() { //... }); ```
================================================ FILE: docs/docs/api/promise.promisifyall.md ================================================ --- layout: api id: promise.promisifyall title: Promise.promisifyAll --- [← Back To API Reference](/docs/api-reference.html)
##Promise.promisifyAll ```js Promise.promisifyAll( Object target, [Object { suffix: String="Async", multiArgs: boolean=false, filter: boolean function(String name, function func, Object target, boolean passesDefaultFilter), promisifier: function(function originalFunction, function defaultPromisifier) } options] ) -> Object ``` Promisifies the entire object by going through the object's properties and creating an async equivalent of each function on the object and its prototype chain. The promisified method name will be the original method name suffixed with `suffix` (default is `"Async"`). Any class properties of the object (which is the case for the main export of many modules) are also promisified, both static and instance methods. Class property is a property with a function value that has a non-empty `.prototype` object. Returns the input object. Note that the original methods on the object are not overwritten but new methods are created with the `Async`-suffix. For example, if you `promisifyAll` the node.js `fs` object use `fs.statAsync` to call the promisified `stat` method. Example: ```js Promise.promisifyAll(require("redis")); //Later on, all redis client instances have promise returning functions: redisClient.hexistsAsync("myhash", "field").then(function(v) { }).catch(function(e) { }); ``` It also works on singletons or specific instances: ```js var fs = Promise.promisifyAll(require("fs")); fs.readFileAsync("myfile.js", "utf8").then(function(contents) { console.log(contents); }).catch(function(e) { console.error(e.stack); }); ``` See [promisification](#promisification) for more examples. The entire prototype chain of the object is promisified on the object. Only enumerable are considered. If the object already has a promisified version of the method, it will be skipped. The target methods are assumed to conform to node.js callback convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument. If the node method calls its callback with multiple success values, the fulfillment value will be an array of them. If a method name already has an `"Async"`-suffix, an exception will be thrown. ####Option: suffix Optionally, you can define a custom suffix through the options object: ```js var fs = Promise.promisifyAll(require("fs"), {suffix: "MySuffix"}); fs.readFileMySuffix(...).then(...); ``` All the above limitations apply to custom suffices: - Choose the suffix carefully, it must not collide with anything - PascalCase the suffix - The suffix must be a valid JavaScript identifier using ASCII letters - Always use the same suffix everywhere in your application, you could create a wrapper to make this easier: ```js module.exports = function myPromisifyAll(target) { return Promise.promisifyAll(target, {suffix: "MySuffix"}); }; ``` ####Option: multiArgs Setting `multiArgs` to `true` means the resulting promise will always fulfill with an array of the callback's success value(s). This is needed because promises only support a single success value while some callback API's have multiple success value. The default is to ignore all but the first success value of a callback function. If a module has multiple argument callbacks as an exception rather than the rule, you can filter out the multiple argument methods in first go and then promisify rest of the module in second go: ```js Promise.promisifyAll(something, { filter: function(name) { return name === "theMultiArgMethodIwant"; }, multiArgs: true }); // Rest of the methods Promise.promisifyAll(something); ``` ####Option: filter Optionally, you can define a custom filter through the options object: ```js Promise.promisifyAll(..., { filter: function(name, func, target, passesDefaultFilter) { // name = the property name to be promisified without suffix // func = the function // target = the target object where the promisified func will be put with name + suffix // passesDefaultFilter = whether the default filter would be passed // return boolean (return value is coerced, so not returning anything is same as returning false) return passesDefaultFilter && ... } }) ``` The default filter function will ignore properties that start with a leading underscore, properties that are not valid JavaScript identifiers and constructor functions (function which have enumerable properties in their `.prototype`). ####Option: promisifier Optionally, you can define a custom promisifier, so you could promisifyAll e.g. the chrome APIs used in Chrome extensions. The promisifier gets a reference to the original method and should return a function which returns a promise. ```js function DOMPromisifier(originalMethod) { // return a function return function promisified() { var args = [].slice.call(arguments); // Needed so that the original method can be called with the correct receiver var self = this; // which returns a promise return new Promise(function(resolve, reject) { args.push(resolve, reject); originalMethod.apply(self, args); }); }; } // Promisify e.g. chrome.browserAction Promise.promisifyAll(chrome.browserAction, {promisifier: DOMPromisifier}); // Later chrome.browserAction.getTitleAsync({tabId: 1}) .then(function(result) { }); ``` Combining `filter` with `promisifier` for the restler module to promisify event emitter: ```js var Promise = require("bluebird"); var restler = require("restler"); var methodNamesToPromisify = "get post put del head patch json postJson putJson".split(" "); function EventEmitterPromisifier(originalMethod) { // return a function return function promisified() { var args = [].slice.call(arguments); // Needed so that the original method can be called with the correct receiver var self = this; // which returns a promise return new Promise(function(resolve, reject) { // We call the originalMethod here because if it throws, // it will reject the returned promise with the thrown error var emitter = originalMethod.apply(self, args); emitter .on("success", function(data, response) { resolve([data, response]); }) .on("fail", function(data, response) { // Erroneous response like 400 resolve([data, response]); }) .on("error", function(err) { reject(err); }) .on("abort", function() { reject(new Promise.CancellationError()); }) .on("timeout", function() { reject(new Promise.TimeoutError()); }); }); }; }; Promise.promisifyAll(restler, { filter: function(name) { return methodNamesToPromisify.indexOf(name) > -1; }, promisifier: EventEmitterPromisifier }); // ... // Later in some other file var restler = require("restler"); restler.getAsync("http://...", ...,).spread(function(data, response) { }) ``` Using `defaultPromisifier` parameter to add enhancements on top of normal node promisification: ```js var fs = Promise.promisifyAll(require("fs"), { promisifier: function(originalFunction, defaultPromisifer) { var promisified = defaultPromisifier(originalFunction); return function() { // Enhance normal promisification by supporting promises as // arguments var args = [].slice.call(arguments); var self = this; return Promise.all(args).then(function(awaitedArgs) { return promisified.apply(self, awaitedArgs); }); }; } }); // All promisified fs functions now await their arguments if they are promises var version = fs.readFileAsync("package.json", "utf8").then(JSON.parse).get("version"); fs.writeFileAsync("the-version.txt", version, "utf8"); ``` ####Promisifying multiple classes in one go You can promisify multiple classes in one go by constructing an array out of the classes and passing it to `promisifyAll`: ```js var Pool = require("mysql/lib/Pool"); var Connection = require("mysql/lib/Connection"); Promise.promisifyAll([Pool, Connection]); ``` This works because the array acts as a "module" where the indices are the "module"'s properties for classes.
================================================ FILE: docs/docs/api/promise.props.md ================================================ --- layout: api id: promise.props title: Promise.props --- [← Back To API Reference](/docs/api-reference.html)
##Promise.props ```js Promise.props(Object|Map|Promise input) -> Promise ``` Like [`.all`](.) but for object properties or `Map`s\* entries instead of iterated values. Returns a promise that is fulfilled when all the properties of the object or the `Map`'s' values\*\* are fulfilled. The promise's fulfillment value is an object or a `Map` with fulfillment values at respective keys to the original object or a `Map`. If any promise in the object or `Map` rejects, the returned promise is rejected with the rejection reason. If `object` is a trusted `Promise`, then it will be treated as a promise for object rather than for its properties. All other objects (except `Map`s) are treated for their properties as is returned by `Object.keys` - the object's own enumerable properties. *\*Only the native [ECMAScript 6 `Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) implementation that is provided by the environment as is is supported* *\*\*If the map's keys happen to be `Promise`s, they are not awaited for and the resulting `Map` will still have those same `Promise` instances as keys* ```js Promise.props({ pictures: getPictures(), comments: getComments(), tweets: getTweets() }).then(function(result) { console.log(result.tweets, result.pictures, result.comments); }); ``` ```js var Promise = require("bluebird"); var fs = Promise.promisifyAll(require("fs")); var _ = require("lodash"); var path = require("path"); var util = require("util"); function directorySizeInfo(root) { var counts = {dirs: 0, files: 0}; var stats = (function reader(root) { return fs.readdirAsync(root).map(function(fileName) { var filePath = path.join(root, fileName); return fs.statAsync(filePath).then(function(stat) { stat.filePath = filePath; if (stat.isDirectory()) { counts.dirs++; return reader(filePath) } counts.files++; return stat; }); }).then(_.flatten); })(root).then(_.chain); var smallest = stats.call("min", "size").call("pick", "size", "filePath").call("value"); var largest = stats.call("max", "size").call("pick", "size", "filePath").call("value"); var totalSize = stats.call("pluck", "size").call("reduce", function(a, b) { return a + b; }, 0); return Promise.props({ counts: counts, smallest: smallest, largest: largest, totalSize: totalSize }); } directorySizeInfo(process.argv[2] || ".").then(function(sizeInfo) { console.log(util.format(" \n\ %d directories, %d files \n\ Total size: %d bytes \n\ Smallest file: %s with %d bytes \n\ Largest file: %s with %d bytes \n\ ", sizeInfo.counts.dirs, sizeInfo.counts.files, sizeInfo.totalSize, sizeInfo.smallest.filePath, sizeInfo.smallest.size, sizeInfo.largest.filePath, sizeInfo.largest.size)); }); ``` Note that if you have no use for the result object other than retrieving the properties, it is more convenient to use [`Promise.join`](.): ```js Promise.join(getPictures(), getComments(), getTweets(), function(pictures, comments, tweets) { console.log(pictures, comments, tweets); }); ```
================================================ FILE: docs/docs/api/promise.race.md ================================================ --- layout: api id: promise.race title: Promise.race --- [← Back To API Reference](/docs/api-reference.html)
##Promise.race ```js Promise.race(Iterable|Promise> input) -> Promise ``` Given an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)\(arrays are `Iterable`\), or a promise of an `Iterable`, which produces promises (or a mix of promises and values), iterate over all the values in the `Iterable` into an array and return a promise that is fulfilled or rejected as soon as a promise in the array is fulfilled or rejected with the respective rejection reason or fulfillment value. This method is only implemented because it's in the ES6 standard. If you want to race promises to fulfillment the [`.any`](.) method is more appropriate as it doesn't qualify a rejected promise as the winner. It also has less surprises: `.race` must become infinitely pending if an empty array is passed but passing an empty array to [`.any`](.) is more usefully a `RangeError`
================================================ FILE: docs/docs/api/promise.reduce.md ================================================ --- layout: api id: promise.reduce title: Promise.reduce --- [← Back To API Reference](/docs/api-reference.html)
##Promise.reduce ```js Promise.reduce( Iterable|Promise> input, function(any accumulator, any item, int index, int length) reducer, [any initialValue] ) -> Promise ``` Given an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)\(arrays are `Iterable`\), or a promise of an `Iterable`, which produces promises (or a mix of promises and values), iterate over all the values in the `Iterable` into an array and [reduce the array to a value](http://en.wikipedia.org/wiki/Fold_\(higher-order_function\)) using the given `reducer` function. If the reducer function returns a promise, then the result of the promise is awaited, before continuing with next iteration. If any promise in the array is rejected or a promise returned by the reducer function is rejected, the result is rejected as well. Read given files sequentially while summing their contents as an integer. Each file contains just the text `10`. ```js Promise.reduce(["file1.txt", "file2.txt", "file3.txt"], function(total, fileName) { return fs.readFileAsync(fileName, "utf8").then(function(contents) { return total + parseInt(contents, 10); }); }, 0).then(function(total) { //Total is 30 }); ``` *If `initialValue` is `undefined` (or a promise that resolves to `undefined`) and the iterable contains only 1 item, the callback will not be called and the iterable's single item is returned. If the iterable is empty, the callback will not be called and `initialValue` is returned (which may be `undefined`).* `Promise.reduce` will start calling the reducer as soon as possible, this is why you might want to use it over `Promise.all` (which awaits for the entire array before you can call [`Array#reduce`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) on it).
================================================ FILE: docs/docs/api/promise.reject.md ================================================ --- layout: api id: promise.reject title: Promise.reject --- [← Back To API Reference](/docs/api-reference.html)
##Promise.reject ```js Promise.reject(any error) -> Promise ``` Create a promise that is rejected with the given `error`.
================================================ FILE: docs/docs/api/promise.resolve.md ================================================ --- layout: api id: promise.resolve title: Promise.resolve --- [← Back To API Reference](/docs/api-reference.html)
## Promise.resolve ```js Promise.resolve(Promise|any value) -> Promise ``` Create a promise that is resolved with the given value. If `value` is already a trusted `Promise`, it is returned as is. If `value` is not a thenable, a fulfilled Promise is returned with `value` as its fulfillment value. If `value` is a thenable (Promise-like object, like those returned by jQuery's `$.ajax`), returns a trusted Promise that assimilates the state of the thenable. This can be useful if a function returns a promise (say into a chain) but can optionally return a static value. Say, for a lazy-loaded value. Example: ```js var someCachedValue; var getValue = function() { if (someCachedValue) { return Promise.resolve(someCachedValue); } return db.queryAsync().then(function(value) { someCachedValue = value; return value; }); }; ``` Another example with handling jQuery castable objects (`$` is jQuery) ```js Promise.resolve($.get("http://www.google.com")).then(function() { //Returning a thenable from a handler is automatically //cast to a trusted Promise as per Promises/A+ specification return $.post("http://www.yahoo.com"); }).then(function() { }).catch(function(e) { //jQuery doesn't throw real errors so use catch-all console.log(e.statusText); }); ```
================================================ FILE: docs/docs/api/promise.setscheduler.md ================================================ --- layout: api id: promise.setscheduler title: Promise.setScheduler --- [← Back To API Reference](/docs/api-reference.html)
##Promise.setScheduler ```js Promise.setScheduler(function(function fn) scheduler) -> function ``` Scheduler should be a function that asynchronously schedules the calling of the passed in function: ```js // This is just an example of how to use the api, there is no reason to do this Promise.setScheduler(function(fn) { setTimeout(fn, 0); }); ``` Setting a custom scheduler could be necessary when you need a faster way to schedule functions than bluebird does by default. It also makes bluebird possible to use in platforms where normal timing constructs like `setTimeout` and `process.nextTick` are not available (like Nashhorn). You can also use it as a hook: ```js // This will synchronize bluebird promise queue flushing with angulars queue flushing // Angular is also now responsible for choosing the actual scheduler Promise.setScheduler(function(fn) { $rootScope.$evalAsync(fn); }); ``` > **Danger** - in order to keep bluebird promises [Promises/A+](https://promisesaplus.com/) compliant a scheduler that executes the function asynchronously (like the examples in this page) must be used.
================================================ FILE: docs/docs/api/promise.some.md ================================================ --- layout: api id: promise.some title: Promise.some --- [← Back To API Reference](/docs/api-reference.html)
##Promise.some ```js Promise.some( Iterable|Promise> input, int count ) -> Promise ``` Given an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)\(arrays are `Iterable`\), or a promise of an `Iterable`, which produces promises (or a mix of promises and values), iterate over all the values in the `Iterable` into an array and return a promise that is fulfilled as soon as `count` promises are fulfilled in the array. The fulfillment value is an array with `count` values in the order they were fulfilled. This example pings 4 nameservers, and logs the fastest 2 on console: ```js Promise.some([ ping("ns1.example.com"), ping("ns2.example.com"), ping("ns3.example.com"), ping("ns4.example.com") ], 2).spread(function(first, second) { console.log(first, second); }); ``` If too many promises are rejected so that the promise can never become fulfilled, it will be immediately rejected with an [AggregateError](.) of the rejection reasons in the order they were thrown in. You can get a reference to [AggregateError](.) from `Promise.AggregateError`. ```js Promise.some(...) .then(...) .then(...) .catch(Promise.AggregateError, function(err) { err.forEach(function(e) { console.error(e.stack); }); }); ```
================================================ FILE: docs/docs/api/promise.try.md ================================================ --- layout: api id: promise.try title: Promise.try --- [← Back To API Reference](/docs/api-reference.html)
##Promise.try ```js Promise.try(function() fn) -> Promise ``` ```js Promise.attempt(function() fn) -> Promise ``` Start the chain of promises with `Promise.try`. Any synchronous exceptions will be turned into rejections on the returned promise. ```js function getUserById(id) { return Promise.try(function() { if (typeof id !== "number") { throw new Error("id must be a number"); } return db.getUserById(id); }); } ``` Now if someone uses this function, they will catch all errors in their Promise `.catch` handlers instead of having to handle both synchronous and asynchronous exception flows. *For compatibility with earlier ECMAScript version, an alias `Promise.attempt` is provided for [`Promise.try`](.).*
================================================ FILE: docs/docs/api/promise.using.md ================================================ --- layout: api id: promise.using title: Promise.using --- [← Back To API Reference](/docs/api-reference.html)
##Promise.using ```js Promise.using( Promise|Disposer|any resource, Promise|Disposer|any resource..., function(any resources...) handler ) -> Promise ``` ```js Promise.using( Array resources, function(Array resources) handler ) -> Promise ``` In conjunction with [`.disposer`](.), `using` will make sure that no matter what, the specified disposer will be called when the promise returned by the callback passed to `using` has settled. The disposer is necessary because there is no standard interface in node for disposing resources. Here is a simple example (where `getConnection()` has been defined to return a proper Disposer object) ```js using(getConnection(), function(connection) { // Don't leak the `connection` variable anywhere from here // it is only guaranteed to be open while the promise returned from // this callback is still pending return connection.queryAsync("SELECT * FROM TABLE"); // Code that is chained from the promise created in the line above // still has access to `connection` }).then(function(rows) { // The connection has been closed by now console.log(rows); }); ``` Using multiple resources: ```js using(getConnection(), function(conn1) { return using(getConnection(), function(conn2) { // use conn1 and conn 2 here }); }).then(function() { // Both connections closed by now }) ``` The above can also be written as (with a caveat, see below) ```js using(getConnection(), getConnection(), function(conn1, conn2) { // use conn1 and conn2 }).then(function() { // Both connections closed by now }) ``` However, if the second `getConnection` throws **synchronously**, the first connection is leaked. This will not happen when using APIs through bluebird promisified methods though. You can wrap functions that could throw in [`Promise.method`](.) which will turn synchronous rejections into rejected promises. Note that you can mix promises and disposers, so that you can acquire all the things you need in parallel instead of sequentially ```js // The files don't need resource management but you should // still start the process of reading them even before you have the connection // instead of waiting for the connection // The connection is always closed, no matter what fails at what point using(readFile("1.txt"), readFile("2.txt"), getConnection(), function(txt1, txt2, conn) { // use conn and have access to txt1 and txt2 }); ```
You can also pass the resources in an array in the first argument. In this case the handler function will only be called with one argument that is the array containing the resolved resources in respective positions in the array. Example: ```js var connectionPromises = [getConnection(), getConnection()]; using(connectionPromises, function(connections) { var conn1 = connections[0]; var conn2 = connections[1]; // use conn1 and conn2 }).then(function() { // Both connections closed by now }) ```
================================================ FILE: docs/docs/api/promiseinspection.md ================================================ --- layout: api id: promiseinspection title: PromiseInspection --- [← Back To API Reference](/docs/api-reference.html)
##PromiseInspection ```js interface PromiseInspection { any reason() any value() boolean isPending() boolean isRejected() boolean isFulfilled() boolean isCancelled() } ``` This interface is implemented by `Promise` instances as well as the `PromiseInspection` result given by [.reflect()](.).
================================================ FILE: docs/docs/api/promisification.md ================================================ --- layout: api id: promisification title: Promisification --- [← Back To API Reference](/docs/api-reference.html)
##Promisification Promisification means converting an existing promise-unaware API to a promise-returning API. The usual way to use promises in node is to [Promise.promisifyAll](.) some API and start exclusively calling promise returning versions of the APIs methods. E.g. ```js var fs = require("fs"); Promise.promisifyAll(fs); // Now you can use fs as if it was designed to use bluebird promises from the beginning fs.readFileAsync("file.js", "utf8").then(...) ``` Note that the above is an exceptional case because `fs` is a singleton instance. Most libraries can be promisified by requiring the library's classes (constructor functions) and calling promisifyAll on the `.prototype`. This only needs to be done once in the entire application's lifetime and after that you may use the library's methods exactly as they are documented, except by appending the `"Async"`-suffix to method calls and using the promise interface instead of the callback interface. As a notable exception in `fs`, `fs.existsAsync` doesn't work as expected, because Node's `fs.exists` doesn't call back with error as first argument. More at [#418](.). One possible workaround is using `fs.statAsync`. Some examples of the above practice applied to some popular libraries: ```js // The most popular redis module var Promise = require("bluebird"); Promise.promisifyAll(require("redis")); ``` ```js // The most popular mongodb module var Promise = require("bluebird"); Promise.promisifyAll(require("mongodb")); ``` ```js // The most popular mysql module var Promise = require("bluebird"); // Note that the library's classes are not properties of the main export // so we require and promisifyAll them manually Promise.promisifyAll(require("mysql/lib/Connection").prototype); Promise.promisifyAll(require("mysql/lib/Pool").prototype); ``` ```js // Mongoose var Promise = require("bluebird"); Promise.promisifyAll(require("mongoose")); ``` ```js // Request var Promise = require("bluebird"); Promise.promisifyAll(require("request")); // Use request.getAsync(...) not request(..), it will not return a promise ``` ```js // mkdir var Promise = require("bluebird"); Promise.promisifyAll(require("mkdirp")); // Use mkdirp.mkdirpAsync not mkdirp(..), it will not return a promise ``` ```js // winston var Promise = require("bluebird"); Promise.promisifyAll(require("winston")); ``` ```js // rimraf var Promise = require("bluebird"); // The module isn't promisified but the function returned is var rimrafAsync = Promise.promisify(require("rimraf")); ``` ```js // xml2js var Promise = require("bluebird"); Promise.promisifyAll(require("xml2js")); ``` ```js // jsdom var Promise = require("bluebird"); Promise.promisifyAll(require("jsdom")); ``` ```js // fs-extra var Promise = require("bluebird"); Promise.promisifyAll(require("fs-extra")); ``` ```js // prompt var Promise = require("bluebird"); Promise.promisifyAll(require("prompt")); ``` ```js // Nodemailer var Promise = require("bluebird"); Promise.promisifyAll(require("nodemailer")); ``` ```js // ncp var Promise = require("bluebird"); Promise.promisifyAll(require("ncp")); ``` ```js // pg var Promise = require("bluebird"); Promise.promisifyAll(require("pg")); ``` In all of the above cases the library made its classes available in one way or another. If this is not the case, you can still promisify by creating a throwaway instance: ```js var ParanoidLib = require("..."); var throwAwayInstance = ParanoidLib.createInstance(); Promise.promisifyAll(Object.getPrototypeOf(throwAwayInstance)); // Like before, from this point on, all new instances + even the throwAwayInstance suddenly support promises ``` See also [`Promise.promisifyAll`](.).
================================================ FILE: docs/docs/api/props.md ================================================ --- layout: api id: props title: .props --- [← Back To API Reference](/docs/api-reference.html)
##.props ```js .props() -> Promise ``` Same as [Promise.props(this)](.).
================================================ FILE: docs/docs/api/race.md ================================================ --- layout: api id: race title: .race --- [← Back To API Reference](/docs/api-reference.html)
##.race ```js .race() -> Promise ``` Same as [Promise.race(this)](.).
================================================ FILE: docs/docs/api/reason.md ================================================ --- layout: api id: reason title: .reason --- [← Back To API Reference](/docs/api-reference.html)
##.reason ```js .reason() -> any ``` Get the rejection reason of this promise. Throws an error if the promise isn't rejected - it is a bug to call this method on an unrejected promise. You should check if this promise is [.isRejected()](.) in code paths where it's guaranteed that this promise is rejected.
================================================ FILE: docs/docs/api/reduce.md ================================================ --- layout: api id: reduce title: .reduce --- [← Back To API Reference](/docs/api-reference.html)
##.reduce ```js .reduce( function(any accumulator, any item, int index, int length) reducer, [any initialValue] ) -> Promise ``` Same as [Promise.reduce(this, reducer, initialValue)](.).
================================================ FILE: docs/docs/api/reflect.md ================================================ --- layout: api id: reflect title: .reflect --- [← Back To API Reference](/docs/api-reference.html)
##.reflect ```js .reflect() -> Promise ``` The [`.reflect`](.) method returns a promise that is always successful when this promise is settled. Its fulfillment value is an object that implements the [PromiseInspection](.) interface and reflects the resolution of this promise. Using `.reflect()` to implement `settleAll` (wait until all promises in an array are either rejected or fulfilled) functionality ```js var promises = [getPromise(), getPromise(), getPromise()]; Promise.all(promises.map(function(promise) { return promise.reflect(); })).each(function(inspection) { if (inspection.isFulfilled()) { console.log("A promise in the array was fulfilled with", inspection.value()); } else { console.error("A promise in the array was rejected with", inspection.reason()); } }); ``` Using `.reflect()` to implement `settleProps` (like settleAll for an object's properties) functionality ```js var object = { first: getPromise1(), second: getPromise2() }; Promise.props(Object.keys(object).reduce(function(newObject, key) { newObject[key] = object[key].reflect(); return newObject; }, {})).then(function(object) { if (object.first.isFulfilled()) { console.log("first was fulfilled with", object.first.value()); } else { console.error("first was rejected with", object.first.reason()); } }) ```
================================================ FILE: docs/docs/api/resource-management.md ================================================ --- layout: api id: resource-management title: Resource management --- [← Back To API Reference](/docs/api-reference.html)
##Resource management Managing resources properly without leaks can be challenging. Simply using `.finally` is not enough as the following example demonstrates: ```js function doStuff() { return Promise.all([ connectionPool.getConnectionAsync(), fs.readFileAsync("file.sql", "utf8") ]).spread(function(connection, fileContents) { return connection.query(fileContents).finally(function() { connection.close(); }); }).then(function() { console.log("query successful and connection closed"); }); } ``` It is very subtle but over time this code will exhaust the entire connection pool and the server needs to be restarted. This is because reading the file may fail and then of course `.spread` is not called at all and thus the connection is not closed. One could solve this by either reading the file first or connecting first, and only proceeding if the first step succeeds. However, this would lose a lot of the benefits of using asynchronity and we might almost as well go back to using simple synchronous code. We can do better, retaining concurrency and not leaking resources, by using: * [disposers](disposer.html), objects that wrap a resource and a method to release that resource, together with * [`Promise.using`](promise.using.html), a function to safely use disposers in a way that automatically calls their release method ```js var using = Promise.using; using(getConnection(), fs.readFileAsync("file.sql", "utf8"), function(connection, fileContents) { return connection.query(fileContents); }).then(function() { console.log("query successful and connection closed"); }); ``` Continue by reading about [disposers](disposer.html) and [`Promise.using`](promise.using.html)
================================================ FILE: docs/docs/api/return.md ================================================ --- layout: api id: return title: .return --- [← Back To API Reference](/docs/api-reference.html)
##.return ```js .return(any value) -> Promise ``` ```js .thenReturn(any value) -> Promise ``` Convenience method for: ```js .then(function() { return value; }); ``` in the case where `value` doesn't change its value because its binding time is different than when using a closure. That means `value` is bound at the time of calling [`.return`](.) so this will not work as expected: ```js function getData() { var data; return query().then(function(result) { data = result; }).return(data); } ``` because `data` is `undefined` at the time `.return` is called. Function that returns the full path of the written file: ```js var Promise = require("bluebird"); var fs = Promise.promisifyAll(require("fs")); var baseDir = process.argv[2] || "."; function writeFile(path, contents) { var fullpath = require("path").join(baseDir, path); return fs.writeFileAsync(fullpath, contents).return(fullpath); } writeFile("test.txt", "this is text").then(function(fullPath) { console.log("Successfully file at: " + fullPath); }); ``` *For compatibility with earlier ECMAScript version, an alias `.thenReturn` is provided for [`.return`](.).*
================================================ FILE: docs/docs/api/some.md ================================================ --- layout: api id: some title: .some --- [← Back To API Reference](/docs/api-reference.html)
##.some ```js .some(int count) -> Promise ``` Same as [Promise.some(this, count)](.).
================================================ FILE: docs/docs/api/spread.md ================================================ --- layout: api id: spread title: .spread --- [← Back To API Reference](/docs/api-reference.html)
##.spread ```js .spread( [function(any values...) fulfilledHandler] ) -> Promise ``` Like calling `.then`, but the fulfillment value _must be_ an array, which is flattened to the formal parameters of the fulfillment handler. ```js Promise.all([ fs.readFileAsync("file1.txt"), fs.readFileAsync("file2.txt") ]).spread(function(file1text, file2text) { if (file1text === file2text) { console.log("files are equal"); } else { console.log("files are not equal"); } }); ``` When chaining `.spread`, returning an array of promises also works: ```js Promise.delay(500).then(function() { return [fs.readFileAsync("file1.txt"), fs.readFileAsync("file2.txt")] ; }).spread(function(file1text, file2text) { if (file1text === file2text) { console.log("files are equal"); } else { console.log("files are not equal"); } }); ``` Note that if using ES6, the above can be replaced with [.then()](.) and destructuring: ```js Promise.delay(500).then(function() { return [fs.readFileAsync("file1.txt"), fs.readFileAsync("file2.txt")] ; }).all().then(function([file1text, file2text]) { if (file1text === file2text) { console.log("files are equal"); } else { console.log("files are not equal"); } }); ``` Note that [.spread()](.) implicitly does [.all()](.) but the ES6 destructuring syntax doesn't, hence the manual `.all()` call in the above code. If you want to coordinate several discrete concurrent promises, use [`Promise.join`](.)
================================================ FILE: docs/docs/api/suppressunhandledrejections.md ================================================ --- layout: api id: suppressunhandledrejections title: .suppressUnhandledRejections --- [← Back To API Reference](/docs/api-reference.html)
##.suppressUnhandledRejections ```js .suppressUnhandledRejections() -> undefined ``` Basically sugar for doing: ```js somePromise.catch(function(){}); ``` Which is needed in case error handlers are attached asynchronously to the promise later, which would otherwise result in premature unhandled rejection reporting. Example: ```js var tweets = fetchTweets(); $(document).on("ready", function() { tweets.then(function() { // Render tweets }).catch(function(e) { alert("failed to fetch tweets because: " + e); }); }); ``` If fetching tweets fails before the document is ready the rejection is reported as unhandled even though it will be eventually handled when the document is ready. This is of course impossible to determine automatically, but you can explicitly do so using `.suppressUnhandledRejections()`: ```js var tweets = fetchTweets(); tweets.suppressUnhandledRejections(); $(document).on("ready", function() { tweets.then(function() { // Render tweets }).catch(function(e) { alert("failed to fetch tweets because: " + e); }); }); ``` It should be noted that there is no real need to attach the handlers asynchronously. Exactly the same effect can be achieved with: ```js fetchTweets() .finally(function() { return $.ready.promise(); }) // DOM guaranteed to be ready after this point .then(function() { // Render tweets }) .catch(function(e) { alert("failed to fetch tweets because: " + e); }); ``` The advantage of using `.suppressUnhandledRejections()` over `.catch(function(){})` is that it doesn't increment the branch count of the promise. Branch counts matter when using cancellation because a promise will only be cancelled if all of its branches want to cancel it.
================================================ FILE: docs/docs/api/synchronous-inspection.md ================================================ --- layout: api id: synchronous-inspection title: Synchronous inspection --- [← Back To API Reference](/docs/api-reference.html)
##Synchronous inspection Often it is known in certain code paths that a promise is guaranteed to be fulfilled at that point - it would then be extremely inconvenient to use [`.then`](.) to get at the promise's value as the callback is always called asynchronously. **Note**: In recent versions of Bluebird a design choice was made to expose [.reason()](.) and [.value()](.) as well as other inspection methods on promises directly in order to make the below use case easier to work with. Every promise implements the [PromiseInspection](.) interface. For example, if you need to use values of earlier promises in the chain, you could nest: ```js // From Q Docs https://github.com/kriskowal/q/#chaining // MIT License Copyright 2009–2014 Kristopher Michael Kowal. function authenticate() { return getUsername().then(function (username) { return getUser(username); // chained because we will not need the user name in the next event }).then(function (user) { // nested because we need both user and password next return getPassword().then(function (password) { if (user.passwordHash !== hash(password)) { throw new Error("Can't authenticate"); } }); }); } ``` Or you could take advantage of the fact that if we reach password validation, then the user promise must be fulfilled: ```js function authenticate() { var user = getUsername().then(function(username) { return getUser(username); }); return user.then(function(user) { return getPassword(); }).then(function(password) { // Guaranteed that user promise is fulfilled, so .value() can be called here if (user.value().passwordHash !== hash(password)) { throw new Error("Can't authenticate"); } }); } ``` In the latter the indentation stays flat no matter how many previous variables you need, whereas with the former each additional previous value would require an additional nesting level.
================================================ FILE: docs/docs/api/tap.md ================================================ --- layout: api id: tap title: .tap --- [← Back To API Reference](/docs/api-reference.html)
##.tap ```js .tap(function(any value) handler) -> Promise ``` Essentially like `.then()`, except that the value passed in is the value returned. This means you can insert `.tap()` into a `.then()` chain without affecting what is passed through the chain. (See example below). Unlike [`.finally`](.) this is not called for rejections. ```js getUser().tap(function(user) { //Like in finally, if you return a promise from the handler //the promise is awaited for before passing the original value through return recordStatsAsync(); }).then(function(user) { //user is the user from getUser(), not recordStatsAsync() }); ``` Common case includes adding logging to an existing promise chain: ```js doSomething() .then(...) .then(...) .then(...) .then(...) ``` ```js doSomething() .then(...) .then(...) .tap(console.log) .then(...) .then(...) ``` *Note: in browsers it is necessary to call `.tap` with `console.log.bind(console)` because console methods can not be called as stand-alone functions.*
================================================ FILE: docs/docs/api/tapcatch.md ================================================ --- layout: api id: tapCatch title: .tapCatch --- [← Back To API Reference](/docs/api-reference.html)
##.tapCatch `.tapCatch` is a convenience method for reacting to errors without handling them with promises - similar to `finally` but only called on rejections. Useful for logging errors. It comes in two variants. - A tapCatch-all variant similar to [`.catch`](.) block. This variant is compatible with native promises. - A filtered variant (like other non-JS languages typically have) that lets you only handle specific errors. **This variant is usually preferable**. ### `tapCatch` all ```js .tapCatch(function(any value) handler) -> Promise ``` Like [`.finally`](.) that is not called for fulfillments. ```js getUser().tapCatch(function(err) { return logErrorToDatabase(err); }).then(function(user) { //user is the user from getUser(), not logErrorToDatabase() }); ``` Common case includes adding logging to an existing promise chain: #### Rate Limiting ```js Promise. try(logIn). then(respondWithSuccess). tapCatch(countFailuresForRateLimitingPurposes). catch(respondWithError); ``` #### Circuit Breakers ```js Promise. try(makeRequest). then(respondWithSuccess). tapCatch(adjustCircuitBreakerState). catch(respondWithError); ``` #### Logging ```js Promise. try(doAThing). tapCatch(logErrorsRelatedToThatThing). then(respondWithSuccess). catch(respondWithError); ``` *Note: in browsers it is necessary to call `.tapCatch` with `console.log.bind(console)` because console methods can not be called as stand-alone functions.* ### Filtered `tapCatch` ```js .tapCatch( class ErrorClass|function(any error), function(any error) handler ) -> Promise ``` ```js .tapCatch( class ErrorClass|function(any error), function(any error) handler ) -> Promise ``` This is an extension to [`.tapCatch`](.) to filter exceptions similarly to languages like Java or C#. Instead of manually checking `instanceof` or `.name === "SomeError"`, you may specify a number of error constructors which are eligible for this tapCatch handler. The tapCatch handler that is first met that has eligible constructors specified, is the one that will be called. Usage examples include: #### Rate Limiting ```js Promise. try(logIn). then(respondWithSuccess). tapCatch(InvalidCredentialsError, countFailuresForRateLimitingPurposes). catch(respondWithError); ``` #### Circuit Breakers ```js Promise. try(makeRequest). then(respondWithSuccess). tapCatch(RequestError, adjustCircuitBreakerState). catch(respondWithError); ``` #### Logging ```js Promise. try(doAThing). tapCatch(logErrorsRelatedToThatThing). then(respondWithSuccess). catch(respondWithError); ```
================================================ FILE: docs/docs/api/then.md ================================================ --- layout: api id: then title: .then --- [← Back To API Reference](/docs/api-reference.html)
##.then ```js .then( [function(any value) fulfilledHandler], [function(any error) rejectedHandler] ) -> Promise ``` [Promises/A+ `.then`](http://promises-aplus.github.io/promises-spec/). If you are new to promises, see the [Beginner's Guide]({{ "/docs/beginners-guide.html" | prepend: site.baseurl }}).
================================================ FILE: docs/docs/api/throw.md ================================================ --- layout: api id: throw title: .throw --- [← Back To API Reference](/docs/api-reference.html)
##.throw ```js .throw(any reason) -> Promise ``` ```js .thenThrow(any reason) -> Promise ``` Convenience method for: ```js .then(function() { throw reason; }); ``` Same limitations regarding to the binding time of `reason` to apply as with [`.return`](.). *For compatibility with earlier ECMAScript version, an alias `.thenThrow` is provided for [`.throw`](.).*
================================================ FILE: docs/docs/api/timeout.md ================================================ --- layout: api id: timeout title: .timeout --- [← Back To API Reference](/docs/api-reference.html)
##.timeout ```js .timeout( int ms, [String message="operation timed out"] ) -> Promise ``` ```js .timeout( int ms, [Error error] ) -> Promise ``` Returns a promise that will be fulfilled with this promise's fulfillment value or rejection reason. However, if this promise is not fulfilled or rejected within `ms` milliseconds, the returned promise is rejected with a [`TimeoutError`](.) or the `error` as the reason. When using the first signature, you may specify a custom error message with the `message` parameter. ```js var Promise = require("bluebird"); var fs = Promise.promisifyAll(require('fs')); fs.readFileAsync("huge-file.txt").timeout(100).then(function(fileContents) { }).catch(Promise.TimeoutError, function(e) { console.log("could not read file within 100ms"); }); ```
================================================ FILE: docs/docs/api/timeouterror.md ================================================ --- layout: api id: timeouterror title: TimeoutError --- [← Back To API Reference](/docs/api-reference.html)
##TimeoutError ```js new TimeoutError(String message) -> TimeoutError ``` Signals that an operation has timed out. Used as a custom cancellation reason in [`.timeout`](.).
================================================ FILE: docs/docs/api/timers.md ================================================ --- layout: api id: timers title: Timers --- [← Back To API Reference](/docs/api-reference.html)
##Timers Methods to delay and time promises out.
================================================ FILE: docs/docs/api/utility.md ================================================ --- layout: api id: utility title: Utility --- [← Back To API Reference](/docs/api-reference.html)
##Utility Functions that could potentially be handy in some situations.
================================================ FILE: docs/docs/api/value.md ================================================ --- layout: api id: value title: .value --- [← Back To API Reference](/docs/api-reference.html)
##.value ```js .value() -> any ``` Get the fulfillment value of this promise. Throws an error if the promise isn't fulfilled - it is a bug to call this method on an unfulfilled promise. You should check if this promise is [.isFulfilled()](.) in code paths where it's not guaranteed that this promise is fulfilled.
================================================ FILE: docs/docs/api-reference.md ================================================ --- id: api-reference title: API Reference redirect_from: "/docs/api/index.html" ---
- [Core](api/core.html) - [new Promise](api/new-promise.html) - [.then](api/then.html) - [.spread](api/spread.html) - [.catch](api/catch.html) - [.error](api/error.html) - [.finally](api/finally.html) - [.bind](api/bind.html) - [Promise.join](api/promise.join.html) - [Promise.try](api/promise.try.html) - [Promise.method](api/promise.method.html) - [Promise.resolve](api/promise.resolve.html) - [Promise.reject](api/promise.reject.html) - [Synchronous inspection](api/synchronous-inspection.html) - [PromiseInspection](api/promiseinspection.html) - [.isFulfilled](api/isfulfilled.html) - [.isRejected](api/isrejected.html) - [.isPending](api/ispending.html) - [.isCancelled](api/iscancelled.html) - [.value](api/value.html) - [.reason](api/reason.html) - [Collections](api/collections.html) - [Promise.all](api/promise.all.html) - [Promise.props](api/promise.props.html) - [Promise.any](api/promise.any.html) - [Promise.some](api/promise.some.html) - [Promise.map](api/promise.map.html) - [Promise.reduce](api/promise.reduce.html) - [Promise.filter](api/promise.filter.html) - [Promise.each](api/promise.each.html) - [Promise.mapSeries](api/promise.mapseries.html) - [Promise.race](api/promise.race.html) - [.all](api/all.html) - [.props](api/props.html) - [.any](api/any.html) - [.some](api/some.html) - [.map](api/map.html) - [.reduce](api/reduce.html) - [.filter](api/filter.html) - [.each](api/each.html) - [.mapSeries](api/mapseries.html) - [Resource management](api/resource-management.html) - [Promise.using](api/promise.using.html) - [.disposer](api/disposer.html) - [Promisification](api/promisification.html) - [Promise.promisify](api/promise.promisify.html) - [Promise.promisifyAll](api/promise.promisifyall.html) - [Promise.fromCallback](api/promise.fromcallback.html) - [.asCallback](api/ascallback.html) - [Timers](api/timers.html) - [Promise.delay](api/promise.delay.html) - [.delay](api/delay.html) - [.timeout](api/timeout.html) - [Cancellation](api/cancellation.html) - [.cancel](api/cancel.html) - [Generators](api/generators.html) - [Promise.coroutine](api/promise.coroutine.html) - [Promise.coroutine.addYieldHandler](api/promise.coroutine.addyieldhandler.html) - [Utility](api/utility.html) - [.tap](api/tap.html) - [.tapCatch](api/tapcatch.html) - [.call](api/call.html) - [.get](api/get.html) - [.return](api/return.html) - [.throw](api/throw.html) - [.catchReturn](api/catchreturn.html) - [.catchThrow](api/catchthrow.html) - [.reflect](api/reflect.html) - [Promise.getNewLibraryCopy](api/promise.getnewlibrarycopy.html) - [Promise.noConflict](api/promise.noconflict.html) - [Promise.setScheduler](api/promise.setscheduler.html) - [Built-in error types](api/built-in-error-types.html) - [OperationalError](api/operationalerror.html) - [TimeoutError](api/timeouterror.html) - [CancellationError](api/cancellationerror.html) - [AggregateError](api/aggregateerror.html) - [Configuration](api/error-management-configuration.html) - [Global rejection events](api/error-management-configuration.html#global-rejection-events) - [Local rejection events](api/promise.onpossiblyunhandledrejection.html) - [Promise.config](api/promise.config.html) - [.suppressUnhandledRejections](api/suppressunhandledrejections.html) - [.done](api/done.html) - [Progression migration](api/progression-migration.html) - [Deferred migration](api/deferred-migration.html) - [Environment variables](api/environment-variables.html)
================================================ FILE: docs/docs/async-dialogs.md ================================================ --- id: async-dialogs title: Async Dialogs --- [async-dialogs](unfinished-article) Typically *promises* are used in conjunction with asynchronous tasks such as a network request or a `setTimeout`; a lesser explored use is dealing with user input. Since a program has to wait for a user to continue some actions it makes sense to consider it an asynchronous event. For comparison I'll start with an example of a *synchronous* user interaction using `window.prompt` and then move to an *asynchronous* interaction by making our own DOM based prompt. To begin, here is a template for a simple HTML page: ```html Async Dislogs Example

The current time is .

Your name is .

``` `window.prompt` blocks the web page from processing while it waits for the user to enter in data. It has to block because the input is returned and the next line of code needs that result. But for sake of this tutorial we are going to convert the typical conditional code into a promise API using a [promise constructor](api/new-promise.html). ```javascript function promptPromise(message) { return new Promise(function(resolve, reject) { var result = window.prompt(message); if (result != null) { resolve(result); } else { reject(new Error('User cancelled')); } }); } var button = document.getElementById('action'); var output = document.getElementById('prompt'); button.addEventListener('click', function() { promptPromise('What is your name?') .then(function(name) { output.innerHTML = String(name); }) .catch(function() { output.innerHTML = '¯\\_(ツ)_/¯'; }); }); ``` [Run example on JSBin][Example1] This doesn't add much much using `window.prompt`; however, one advantage is the API that promises provide. In the case where we call `promptPromise(…)` we can easily react to the result of the dialog without having to worry about how it is implemented. In our example we've implemented the `window.prompt` but our call to `promptPromise()` doesn't care. This makes a change to an *asynchronous* dialog a little more future proof. To drive home the synchronous nature of the `window.prompt` notice that the time stops ticking when the prompt dialog is displayed. Let's fix that by making our own prompt. Since our dialog is just DOM manipulation the page won't be blocked while waiting for user input. First add the prompt dialog to the HTML: ```html ``` We will want to keep the same API so our change will be only to the `promisePrompt`. It will find the dialog DOM elements, attach events to the elements, show the dialog box, return a promise that is resolved based on the attached events, and finally detaches the events and cleans up after itself (hiding the dialog box for another use later). ```javascript function promptPromise(message) { var dialog = document.getElementById('dialog'); var input = dialog.querySelector('input'); var okButton = dialog.querySelector('button.ok'); var cancelButton = dialog.querySelector('button.cancel'); dialog.querySelector('.message').innerHTML = String(message); dialog.className = ''; return new Promise(function(resolve, reject) { dialog.addEventListener('click', function handleButtonClicks(e) { if (e.target.tagName !== 'BUTTON') { return; } dialog.removeEventListener('click', handleButtonClicks); dialog.className = 'hidden'; if (e.target === okButton) { resolve(input.value); } else { reject(new Error('User cancelled')); } }); }); } ``` [Run example on JSBin][Example2] Now when the user presses the **Set Name** button the clock continues to update while the dialog is visible. Because the `removeEventListener` requires a reference to the original function that was used with the `addEventListener` it makes it difficult to clean up after itself without storing the references in a scope higher then the handler itself. Using a named function we can reference it when a user clicks the button. To help with performance and to avoid duplicating code the example uses [event delegation][1] to capture both buttons in one *click* handler. [1]: https://davidwalsh.name/event-delegate The same thing can be done with less code using jQuery's [event namespacing](https://api.jquery.com/on/#event-names). ```javascript return new Promise(function(resolve, reject) { $('#okButton').on('click.promptDialog', function() { resolve(input.value); }); $('#cancelButton').on('click.promptDialog', reject); }) .finally(function() { $('#okButton').off('click.promptDialog'); $('#cancelButton').off('click.promptDialog'); }); ``` There are still a few problems with the earlier code example. It feels like it is doing too much. A *squint* test reveals behavior for showing the dialog, set the dialog's message, attach two DOM events, construct a promise, event delegation, hide the dialog, and finally detach DOM events. That is a lot for one little function. A refactoring can help. Abstraction is the key here. We will make an *object* (or class) that is responsible for managing the dialog box. Its interface will manage only two function references (callbacks): when the user clicks ok and when user clicks cancel. And it will offer the value when asked. Using an abstraction like this the `promisePrompt` no longer needs to know anything about the DOM and concentrates on just providing a promise. This will also make things easier to create a promised version of a progress bar or confirmation dialog or any other type of UI that we want to have a value for. All we will need to do is write a class for that dialog type with the same interface and just pass that class into our promise making method. The dialog interface might look like this: ```javascript var noop = function() { return this; }; function Dialog() { this.setCallbacks(noop, noop); } Dialog.prototype.setCallbacks = function(okCallback, cancelCallback) { this._okCallback = okCallback; this._cancelCallback = cancelCallback; return this; }; Dialog.prototype.waitForUser = function() { var _this = this; return new Promise(function(resolve, reject) { _this.setCallbacks(resolve, reject); }); }; Dialog.prototype.show = noop; Dialog.prototype.hide = noop; ``` Initially the Dialog class sets the two callbacks to *noop* functions. It is up to the child class to call them when necessary. We break down the promise creation to one function `waitForUser()` that sets the callbacks and returns a promise. At this level the `show()` and `hide()` are just *noop* functions as well and will be implemented by the child classes. Our `PromptDialog` class is responsible for inheriting from `Dialog` and setting up the required DOM scaffolding and eventually call `this._okCallback` or `this._cancelCallback` as appropriate. It might look like this: ```javascript function PromptDialog() { Dialog.call(this); this.el = document.getElementById('dialog'); this.inputEl = this.el.querySelector('input'); this.messageEl = this.el.querySelector('.message'); this.okButton = this.el.querySelector('button.ok'); this.cancelButton = this.el.querySelector('button.cancel'); this.attachDomEvents(); } PromptDialog.prototype = Object.create(Dialog.prototype); PromptDialog.prototype.attachDomEvents = function() { var _this = this; this.okButton.addEventListener('click', function() { _this._okCallback(_this.inputEl.value); }); this.cancelButton.addEventListener('click', function() { _this._cancelCallback(); }); }; PromptDialog.prototype.show = function(message) { this.messageEl.innerHTML = String(message); this.el.className = ''; return this; }; PromptDialog.prototype.hide = function() { this.el.className = 'hidden'; return this; }; ``` Notice that use of `return this;` in most of the functions? That pattern will allow method chaining as you'll see shortly. This inherits from `Dialog` and stores references to the required DOM elements that this dialog uses. It then attaches the require DOM events (`attachDomEvents()`) which eventually call the callbacks. Then it implements the `show()` and `hide()` methods. Its usage is more flexible and verbose: ```javascript var output = document.getElementById('prompt'); var prompt = new PromptDialog(); prompt.show('What is your name?') .waitForUser() .then(function(name) { output.innerHTML = String(name); }) .catch(function() { output.innerHTML = '¯\\_(ツ)_/¯'; }) .finally(function() { prompt.hide(); }); ``` [Run example on JSBin][Example3] This abstraction can be expanded on in other ways. For example a notification dialog: ```javascript function NotifyDialog() { Dialog.call(this); var _this = this; this.el = document.getElementById('notify-dialog'); this.messageEl = this.el.querySelector('.message'); this.okButton = this.el.querySelector('button.ok'); this.okButton.addEventListener('click', function() { _this._okCallback(); }); } NotifyDialog.prototype = Object.create(Dialog.prototype); NotifyDialog.prototype.show = function(message) { this.messageEl.innerHTML = String(message); this.el.className = ''; return this; }; NotifyDialog.prototype.show = function() { this.el.className = 'hidden'; return this; }; ``` #### Exercises for the student 1. Write a function that takes a `Dialog` instance and a default value. Have it return a promise that resolves to the default value if the user clicks cancel. 2. With the use of abstract classes can the similarities between `PromptDialog` and `NotifyDialog` be abstracted? Make a sub class of `Dialog` that abstracts the common DOM code (`DOMDialog`). Then refactor the `PromptDialog` and `NotifyDialog` to inherate from `DOMDialog` but references the correct DOM selectors. ## Cancellation Something missing from the above example is proper error handling. When it comes to promises it is a best practise to always *reject a promise with an Error* and not with plain data such as an object, string, number, or null/undefined. The reasoning for this is promises are best used as a way to regain some of the syntax you have with the standard `try {} catch() {}` blocks with asynchronous code. An advantage of using `Error`s is the ability to test why a promise was rejected and make decisions on that. This ability is also baked into how Bluebird works. You can pass in a predicate to the `catch()` block allowing you to have more than one block based on what `Error` it was rejected with. For example: ```javascript doSomething().then(function(value) { // Do something with value or fail with an error. throw new Error('testing errors'); }) .catch(ArgumentError, function(e) { console.log('You buggered up something with the arguments.', e); }) .catch(SyntaxError, function(e) { console.log('Check your syntax!', e); }) .catch(function(e) { // e is an Error object. console.log('Well something genaric happened.', e); }); ``` In our dialog example perhaps we want to differentiate between a rejected promise because of some problem (bad AJAX, programming error, etc.) or because the user pressed the cancel button. To do this we will have two `catch()` functions one for `UserCanceledError` and one for any other `Error`. We can make a custom error like so: ```javascript function UserCanceledError() { this.name = 'UserCanceledError'; this.message = 'Dialog cancelled'; } UserCanceledError.prototype = Object.create(Error.prototype); ``` See [this StackOverflow answer](http://stackoverflow.com/a/17891099/227176) for a more detailed and feature complete way to make custom errors. Now we can add a `cancel()` reject with this in our event listener: ```javascript Dialog.prototype.cancel = function() { this._cancelCallback(new UserCanceledError()); }; … PromptDialog.prototype.attachDomEvents = function() { var _this = this; this.okButton.addEventListener('click', function() { _this._okCallback(_this.inputEl.value); }); this.cancelButton.addEventListener('click', function() { _this.cancel(); }); }; ``` And in our usage case we can test for it: ```javascript // Timeout the dialog in five seconds. setTimeout(function() { prompt.cancel(); }, 5000); prompt.show('What is your name?') .waitForUser() .then(function(name) { output.innerHTML = String(name); }) .catch(UserCanceledError, function() { output.innerHTML = '¯\\_(ツ)_/¯'; }) .catch(function(e) { console.log('Something bad happened!', e); }) .finally(function() { prompt.hide(); }); ``` [Run example on JSBin][Example4] **NOTE:** Bluebird supports [cancellation](api/cancellation.html) as an optional feature that is turned off by default. However, its implementation (since version 3.0) is meant to stop the then and catch callbacks from firing. It is not helpful in the example of a user cancellation as described here. ## Progress bar When there are asynchronous tasks that have the ability to notify progress as they complete it can be tempting to want that in the promise that represents that task. Unfortunately this is a bit of an anti-pattern. That is because the point of promises is to represent a value as if it was natural (like it is in normal synchronous code) and not to be over glorified callback management. So how then could we represent a progress bar like dialog? Well the answer is to manage the progress through callbacks outside the promise API. Bluebird has since [deprecated the progression feature](deprecated-apis.html#progression) and offers an alternative which I hope to illustrate here. Another key difference between a *progress bar* dialog and any other dialog we've discussed here is that a progress bar represents information on another task and *not* user import. Instead of the program waiting for the user to provide a value the dialog box is waiting on the program to provide a value (resolved: 100% complete, rejected: aborted half way through). Because of this the *progress bar* dialog would have a different interface then the previous dialogs we've covered. However, there can still be some user interaction so in essence we are dealing with two promises. Bluebird has a way to manage more than one promise simultaneously. When you want to know if more then one promise completes there is a `Promise.all()` function that takes an array of promises and returns a new promise waiting for them all to resolve. But if any one is rejected the returned promise is immediately rejected. Bluebird also has a `Promise.race()` function which does the same thing but doesn't wait for all of them to finish. That is what we want. An example how this might look: ```javascript function showProgress(otherPromise) { var progress = new ProgressbarDialog().show('Uploading…'); return Promise.race([otherPromise, promise.waitForUser()]) .finally(function() { progress.hide(); }); } ``` Here is some example HTML for the Progress Dialog: ```html
``` The JavaScript is the same as the `PromptDialog` only we will add a `setProgress()` method: ```javascript function ProgressDialog() { Dialog.call(this); this.el = document.getElementById('progress-dialog'); this.messageEl = this.el.querySelector('.message'); this.progressBar = this.el.querySelector('.progress-bar>div'); this.cancelButton = this.el.querySelector('button.cancel'); this.attachDomEvents(); } ProgressDialog.prototype = Object.create(Dialog.prototype); ProgressDialog.prototype.attachDomEvents = function() { var _this = this; this.cancelButton.addEventListener('click', function() { _this.cancel(); }); }; ProgressDialog.prototype.show = function(message) { this.messageEl.innerHTML = String(message); this.el.className = ''; return this; }; ProgressDialog.prototype.hide = function() { this.el.className = 'hidden'; return this; }; ProgressDialog.prototype.setProgress = function(percent) { this.progressBar.style.width = percent + '%'; }; ``` A common misconception is that promises are a form of callback management. This is not the case and is why the idea of having a progress callback is not part of the Promise spec. However, much like the Promise library passes in a `resolve` and `reject` callback when you create a new promise (`new Promise(…)`) we can do the same patter for a progress callback. Now to the fun part. For this tutorial we will *fake* a lengthy file upload by using `setTimeout`. The intent is to provide a promise and to allow a progress to be periodically ticked away. We will expect a function to be passed which is called whenever the progress needs updating. And it returns a promise. ```javascript function delayedPromise(progressCallback) { var step = 10; return new Promise(function(resolve, reject) { var progress = 0 - step; // So first run of nextTick will set progress to 0 function nextTick() { if (progress >= 100 ) { resolve('done'); } else { progress += step; progressCallback(progress); setTimeout(nextTick, 500); } } nextTick(); }); } ``` When we construct our `ProgressDialog` we use the `waitForUser()` method to capture the user interaction promise and then use `delayedPromise()` to capture the fake network promise and finally `Promise.reace()` to manage the two simultaneously and end with a single promise as usual. ```javascript document.addEventListener('DOMContentLoaded', function() { var button = document.getElementById('action'); var output = document.getElementById('output'); var prompt = new ProgressDialog(); button.addEventListener('click', function() { var pendingProgress = true; var waitForPromise = delayedPromise(function(progress) { if (pendingProgress) { prompt.setProgress(progress); } }); // Prevent user from pressing button while dialog is visible. button.disabled = true; prompt.show('Simulating a file upload.'); Promise.race([waitForPromise, prompt.waitForUser()]) .then(function() { output.innerHTML = 'Progress completed'; }) .catch(UserCanceledError, function() { output.innerHTML = 'Progress canceled by user'; }) .catch(function(e) { console.log('Error', e); }) .finally(function() { pendingProgress = false; button.disabled = false; prompt.hide(); }); }); }); ``` [Run example on JSBin][Example5] I hope this helps illustrate some concepts available with Promises and a different perspective on how promises can represent more then just AJAX data. Although the code may look verbose it does provide the benefit that it is modular and can be easily changed. A trait difficult to achieve with a more procedural style. Happy coding, [@sukima](https://github.com/sukima). [Example1]: http://jsbin.com/kowama/edit?js,output [Example2]: http://jsbin.com/fucofu/edit?js,output [Example3]: http://jsbin.com/wupixi/edit?js,output [Example4]: http://jsbin.com/yaropo/edit?js,output [Example5]: http://jsbin.com/bipeve/edit?js,output ================================================ FILE: docs/docs/beginners-guide.md ================================================ --- id: beginners-guide title: Beginner's Guide --- [beginners-guide](unfinished-article) ================================================ FILE: docs/docs/benchmarks.md ================================================ --- id: benchmarks title: Benchmarks --- Benchmarks have been ran with the following versions of modules. ``` ├── async@1.5.2 ├── babel@5.8.35 ├── davy@1.1.0 ├── deferred@0.7.5 ├── kew@0.7.0 ├── lie@3.0.2 ├── neo-async@1.7.3 ├── optimist@0.6.1 ├── promise@7.1.1 ├── q@1.4.1 ├── rsvp@3.2.1 ├── streamline@2.0.16 ├── streamline-runtime@1.0.38 ├── text-table@0.2.0 ├── vow@0.4.12 └── when@3.7.7 ``` ###1\. DoxBee sequential This is Gorki Kosev's benchmark used in the article [Analysis of generators and other async patterns in node](http://spion.github.io/posts/analysis-generators-and-other-async-patterns-node.html). The benchmark emulates a situation where N=10000 requests are being made concurrently to execute some mixed async/sync action with fast I/O response times. This is a throughput benchmark. Every implementation runs in a freshly created isolated process which is warmed up to the benchmark code before timing it. The memory column represents the highest snapshotted RSS memory (as reported by `process.memoryUsage().rss`) during processing. Command: `./bench doxbee` (needs cloned repository) The implementations for this benchmark are found in [`benchmark/doxbee-sequential`](https://github.com/petkaantonov/bluebird/tree/master/benchmark/doxbee-sequential) directory. ``` results for 10000 parallel executions, 1 ms per I/O op file time(ms) memory(MB) callbacks-baseline.js 116 33.98 callbacks-suguru03-neo-async-waterfall.js 145 43.81 promises-bluebird-generator.js 183 42.35 promises-bluebird.js 214 43.41 promises-cujojs-when.js 312 64.37 promises-then-promise.js 396 74.33 promises-tildeio-rsvp.js 414 84.80 promises-native-async-await.js 422 104.23 promises-ecmascript6-native.js 424 92.12 generators-tj-co.js 444 90.98 promises-lvivski-davy.js 480 114.46 callbacks-caolan-async-waterfall.js 520 109.01 promises-dfilatov-vow.js 612 134.38 promises-obvious-kew.js 725 208.63 promises-calvinmetcalf-lie.js 730 164.96 streamline-generators.js 809 154.36 promises-medikoo-deferred.js 913 178.51 observables-pozadi-kefir.js 991 194.00 streamline-callbacks.js 1127 196.54 observables-Reactive-Extensions-RxJS.js 1906 268.41 observables-caolan-highland.js 6887 662.08 promises-kriskowal-q.js 8533 435.51 observables-baconjs-bacon.js.js 21282 882.61 Platform info: Linux 4.4.0-79-generic x64 Node.JS 8.6.0 V8 6.0.287.53 Intel(R) Core(TM) i5-6600K CPU @ 3.50GHz × 4 ``` ###2\. Parallel This made-up scenario runs 25 shimmed queries in parallel per each request (N=10000) with fast I/O response times. This is a throughput benchmark. Every implementation runs in a freshly created isolated process which is warmed up to the benchmark code before timing it. The memory column represents the highest snapshotted RSS memory (as reported by `process.memoryUsage().rss`) during processing. Command: `./bench parallel` (needs cloned repository) The implementations for this benchmark are found in [`benchmark/madeup-parallel`](https://github.com/petkaantonov/bluebird/tree/master/benchmark/madeup-parallel) directory. ``` results for 10000 parallel executions, 1 ms per I/O op file time(ms) memory(MB) callbacks-baseline.js 274 75.11 callbacks-suguru03-neo-async-parallel.js 320 88.84 promises-bluebird.js 407 107.25 promises-bluebird-generator.js 432 113.19 callbacks-caolan-async-parallel.js 550 154.27 promises-cujojs-when.js 648 168.65 promises-ecmascript6-native.js 1145 308.87 promises-lvivski-davy.js 1153 257.36 promises-native-async-await.js 1260 323.68 promises-then-promise.js 1372 313.24 promises-tildeio-rsvp.js 1435 398.73 promises-medikoo-deferred.js 1626 306.02 promises-calvinmetcalf-lie.js 1805 351.21 promises-dfilatov-vow.js 2492 558.25 promises-obvious-kew.js 3403 784.61 streamline-generators.js 13068 919.24 streamline-callbacks.js 25509 1141.57 Platform info: Linux 4.4.0-79-generic x64 Node.JS 8.6.0 V8 6.0.287.53 Intel(R) Core(TM) i5-6600K CPU @ 3.50GHz × 4 ``` ###3\. Latency benchmarks For reasonably fast promise implementations latency is going to be fully determined by the scheduler being used and is therefore not interesting to benchmark. [JSPerfs](https://jsperf.com/) that benchmark promises tend to benchmark latency. ================================================ FILE: docs/docs/changelog.md ================================================ --- id: changelog title: Changelog --- ## 3.7.2 (2019-11-28) Bugfixes: - Fixes firefox settimeout not initialized error \([#1623](.)\) ## 3.7.1 (2019-10-15) Features: - feature Bugfixes: - Fix \([#1614](.)\) - Fix \([#1613](.)\) - Fix \([#1616](.)\) ## 3.7.0 (2019-10-01) Features: - Add [Promise.allSettled](.) method \([#1606](.)\) ## 3.6.0 (2019-10-01) Features: - Add support for AsyncResource \([#1403](.)\) Bugfixes: - Fix [.reduce](.) generating unhandled rejection events \([#1501](.)\) - Fix [Promise.reduce](.) generating unhandled rejction events \([#1502](.)\) - Fix [.map](.) and [.filter](.) generating unhandled rejection events \([#1487](.)\) - Fix [Promise.map](.) unhandled rejection events \([#1489](.)\) - Fix cancel skipping upward propagation \([#1459](.)\) - Fix loadTimes deprecation \([#1505](.)\) - Fix [Promise.each](.) maximum stack exceeded error \([#1326](.)\) - Make PromiseRejectionEvent confrom to spec \([#1509](.)\) - Fix false unhandled rejection events \([#1468](.)\) ## 3.5.5 (2019-05-24) Features: - Added Symbol.toStringTag support to Promise \([#1421](.)\) Bugfixes: - Fix error in IE9 \([#1591](.), [#1592](.)\) - Fix error with undefined stack trace \([#1537](.)\) - Fix [.catch](.) throwing an error later rather than immediately when passed non-function handler \([#1517](.)\) ## 3.5.4 (2019-04-03) - Proper version check supporting VSCode\([#1576](.)\) ## 3.5.3 (2018-11-06) Bugfixes: - Update acorn dependency ## 3.5.2 (2018-09-03) Bugfixes: - Fix `PromiseRejectionEvent` to contain `.reason` and `.promise` properties. \([#1509](.), [#1464](.)\) - Fix promise chain retaining memory until the entire chain is resolved \([#1544](.), [#1529](.)\) ## 3.5.1 (2017-10-04) Bugfixes: - Fix false positive unhandled rejection when using async await \([#1404](.)\) - Fix false positive when reporting error as non-error \([#990](.)\) ## 3.5.0 (2017-03-03) Features: - Added new method: [.tapCatch](.) \([#1220](.)\) Bugfixes: - Fixed streamline benchmarks \([#1233](.)\) - Fixed yielding a function calling the function \([#1314](.), [#1315](.)\) - Fixed confusing error message when calling [.catch](.) with non function predicate \([#1350](.)\) - Fixed [.props](.) resolving to empty object when called with empty `Map` \([#1338](.)\) - Fixed confusing error message when invoking `Promise` directly without `new` \([#1320](.)\) - Added dedicated webpack entry point \([#1318](.)\) ## 3.4.7 (2016-12-22) - Promise config returns reference to Bluebird library - Updated logo - Benchmark fix - Don't drop syntaxerror context from stack traces - Fix environment variables sometimes causing long stack traces to be enabled ## 3.4.6 (2016-09-01) Bugfixes: - Fix [Promise.map](.) and [.map](.) not always calling the callback asynchronously \([#1148](.)\) ## 3.4.5 (2016-08-31) Bugfixes: - Fix unhandled error regression introduced in 3.4.3 [#1217](.) ## 3.4.4 (2016-08-30) Bugfixes: - Fix benchmark parallel in node 6 [#1165](.) - Fix memory leak with Promise.each [#1057](.) - Fix thenable passed to .return being evaluated too early [#1210](.) - Fix "unhandledrejection" event not having .detail field when using DOM3 event listener api [#1209](.) - Fix [Promise.join](.) not ensuring asynchronous callback [#1153](.) - Fix domains leaking when synchronous error is thrown while a domain is active [#1125](.) ## 3.4.3 (2016-08-25) Bugfixes: - The "a promise was created in a handler but not returned from it" warning now highlights the file, line and column where the return statement is missing. - The "a promise was created in a handler but not returned from it" warning now adds the bluebird API method used to create the non-returned promise at the top of the warning stack ## 3.4.2 (2016-08-24) Bugfixes: - Add missing link to unhandled warning docs \([#1205](.)\) - Fix [Promise.delay](.) not having a long stack trace \([#1182](.)\) - Fix false unhandled rejection when a rejected promise originating from one copy of bluebird is passed to another copy's [.return](.) or [.catchReturn](.) \([#1186](.)\) - Fix Promise.resolve is not a function error \([#1192](.)\) - Fix global events not being fired through DOM 3 API inside a worker \([#1190](.)\) - Fix .cancel() not immediately marking a promise as being cancelled if it has cancellable parent \([#1187](.)\) - Fix maximum callstack exceeded with [Promise.coroutine](.) \([#1170](.)\) ## 3.4.1 (2016-06-17) Features: - Added [Promise.getNewLibraryCopy](.) ## 3.4.0 (2016-05-17) Features: - Add `Promise.version` which tells the bluebird version as a string e.g. `"3.4.0"` ([#1042](.)). - [.map](.), [Promise.map](.), [.filter](.) and [Promise.filter](.) now return rejected promise when inappropriate options argument is passed ([#1097](.)). Bugfixes: - Fix bug where callback to [.disposer](.) is not called if the resource is `null` ([#1099](.)). - Fix bug where assimilating thenable throws unexpectedly when using hostile host objects as thenables ([#1104](.)). ## 3.3.5 (2016-04-12) Bugfixes: - Fix then sometimes not being called on iOS/Firefox ([#1022](.)). - Fix custom schedulers not being called when using promisified functions ([#1023](.)). - Fix unexpected error being thrown when promisifed function is called with no arguments ([#1063](.)). ## 3.3.4 (2016-03-07) Features: - Warnings about created promises that are not returned are no longer given if the handler promise has not been chained. This should reduce the amount of false positives with this warning. ## 3.3.3 (2016-02-25) Bugfixes: - Fix stack overflow error when a promise returned by promisified function rejects early in a huge array when using [Promise.mapSeries](.) or [Promise.each](.) ## 3.3.2 (2016-02-25) Bugfixes: - Fix missing newline in stack trace reported by [.done()](.) ([#1020](.)). - Detect deep circular resolutions ## 3.3.1 (2016-02-13) Bugfixes: - Fix crash when cancelling a [.tap()](.) handler promise ([#1006](.)). ## 3.3.0 (2016-02-12) Features: - Cancelling Promise returned from [Promise.delay()](.) and [.delay()](.) now calls `clearTimeout` ([#1000](.)) - Add [monitoring and lifecycle hooks](http://bluebirdjs.com/docs/features.html#promise-monitoring) - Add `'warning'` hook for warnings ([#980](.)) Bugfixes: - Fix warnings for "promise was rejected with non-error" being output when promises are rejected with errors from different realm ([#990](.)) ## 3.2.2 (2016-02-05) Bugfixes: - Make build script's output work without TTY ## 3.2.1 (2016-02-01) Bugfixes: - Revert monitoring feature due to crash in browser ## 3.2.0 (2016-02-01) - Broken build ## 3.1.5 (2016-01-26) Dummy release to trigger CDN update. ## 3.1.4 (2016-01-25) Bugfixes: - Fix broken npm prepublish script release ## 3.1.3 (2016-01-25) Bugfixes: - Fix generators crashing in node 0.12 ([#978](.)) - Add minimal build files to build ([#976](.), [#757](.)) ## 3.1.2 (2016-01-23) Features: - [.timeout()](.) now `clearTimeout`s the timer if the resulting promise is cancelled ([#926](.)) - [Promise.coroutine](.) now returns function with same `.length` as the original function ([#927](.), [#933](.)) Bugfixes: - Fix long stack traces not working when promise is created from [Promise.fromCallback](.) ([#971](.)) - Fix [.finally()](.) handlers not being called when promise is cancelled while a domain is active ([#963](.)) - Fix [.timeout()](.) trying to cancel a promise even if cancellation is disabled ([#970](.)) ## 3.1.1 (2015-12-16) Bugfixes: - Disable wForgottenWarning when all warnings are disabled ## 3.1.0 (2015-12-16) Features: - Added ability to configure the [forgotten return statement](http://bluebirdjs.com/docs/warning-explanations.html#warning-a-promise-was-created-in-a-handler-but-none-were-returned-from-it) warning separately \([#920](.)\). Bugfixes: - Fixed the bug where returning a value from [.finally](.) or [.tap](.) handler did not make a warning about a forgotten return go away \([#846](.)\). - Fixed the bug where setTimeout is used in Chrome instead of MutationObserver \([#915](.)\) - Fixed the bug where using [.bind](.) suppressed unhandled rejections \([#841](.)\) ## 3.0.6 (2015-12-01) Bugfixes: - Fix [.timeout()](.) not cancelling parent \([#891](.)\) - Fix long stack traces when using [Promise.resolve()](.) \([#861](.)\) - Fix [Promise.config()](.) not disabling long stack traces when passing `longStackTraces: false` \([#897](.)\) ## 3.0.5 (2015-11-01) Bugfixes: - Added [forgotten return warnings](http://bluebirdjs.com/docs/warning-explanations.html#warning-a-promise-was-created-in-a-handler-but-none-were-returned-from-it) to [Promise.try](.) and [Promise.method](.) ## 3.0.4 (2015-11-01) Bugfixes: - The stack trace for [forgotten return warnings](http://bluebirdjs.com/docs/warning-explanations.html#warning-a-promise-was-created-in-a-handler-but-none-were-returned-from-it) is more useful now. ## 3.0.3 (2015-11-01) Bugfixes: - 3rd party libraries rejecting promises with non-errors no longer causes warnings - When `NODE_ENV` environment variable is `"development"` setting `BLUEBIRD_DEBUG` environment variable to `0` can now be used to disable debug mode ## 3.0.2 (2015-10-29) Bugfixes: - Fix crash when using node.js domains [#829](.) ## 3.0.1 (2015-10-28) See [New in 3.0](new-in-bluebird-3.html). ## 3.0.0 (2015-10-27) See [New in 3.0](new-in-bluebird-3.html). ## 2.11.0 (2016-08-30) Features: - feature Bugfixes: - bugfix ## 2.10.2 (2015-10-01) Features: - [.timeout()](.) now takes a custom error object as second argument ## 2.10.1 (2015-09-21) - Fix error "Cannot promisify an API that has normal methods with 'Async'-suffix" when promisifying certain objects with a custom promisifier ## 2.10.0 (2015-09-08) Features: - `Promise.using` can now take the promises-for-resources as an array ([#733](.)). - Browser builds for minimal core are now hosted on CDN ([#724](.)). Bugfixes: - Disabling debug mode with `BLUEBIRD_DEBUG=0` environment variable now works ([#719](.)). - Fix unhandled rejection reporting when passing rejected promise to `.return()` ([#721](.)). - Fix unbound promise's then handlers being called with wrong `this` value ([#738](.)). ## 2.9.34 (2015-07-15) Bugfixes: - Correct domain for .map, .each, .filter, .reduce callbacks ([#701](.)). - Preserve bound-with-promise promises across the entire chain ([#702](.)). ## 2.9.33 (2015-07-09) Bugfixes: - Methods on `Function.prototype` are no longer promisified ([#680](.)). ## 2.9.32 (2015-07-03) Bugfixes: - Fix `.return(primitiveValue)` returning a wrapped version of the primitive value when a Node.js domain is active ([#689](.)). ## 2.9.31 (2015-07-03) Bugfixes: - Fix Promises/A+ compliance issue regarding circular thenables: the correct behavior is to go into an infinite loop instead of warning with an error (Fixes [#682](.)). - Fix "(node) warning: possible EventEmitter memory leak detected" ([#661](.)). - Fix callbacks sometimes being called with a wrong node.js domain ([#664](.)). - Fix callbacks sometimes not being called at all in iOS 8.1 WebApp mode ([#666](.), [#687](.)). ## 2.9.30 (2015-06-14) Bugfixes: - Fix regression with `promisifyAll` not promisifying certain methods ## 2.9.29 (2015-06-14) Bugfixes: - Improve `promisifyAll` detection of functions that are class constructors. Fixes mongodb 2.x promisification. ## 2.9.28 (2015-06-14) Bugfixes: - Fix handled rejection being reported as unhandled in certain scenarios when using [.all](.) or [Promise.join](.) ([#645](.)) - Fix custom scheduler not being called in Google Chrome when long stack traces are enabled ([#650](.)) ## 2.9.27 (2015-05-30) Bugfixes: - Fix `sinon.useFakeTimers()` breaking scheduler ([#631](.)) Misc: - Add nw testing facilities (`node tools/test --nw`) ## 2.9.26 (2015-05-25) Bugfixes: - Fix crash in NW [#624](.) - Fix [`.return()`](.) not supporting `undefined` as return value [#627](.) ## 2.9.25 (2015-04-28) Bugfixes: - Fix crash in node 0.8 ## 2.9.24 (2015-04-02) Bugfixes: - Fix not being able to load multiple bluebird copies introduced in 2.9.22 ([#559](.), [#561](.), [#560](.)). ## 2.9.23 (2015-04-02) Bugfixes: - Fix node.js domain propagation ([#521](.)). ## 2.9.22 (2015-04-02) - Fix `.promisify` crashing in phantom JS ([#556](.)) ## 2.9.21 (2015-03-30) - Fix error object's `'stack'`' overwriting causing an error when its defined to be a setter that throws an error ([#552](.)). ## 2.9.20 (2015-03-29) Bugfixes: - Fix regression where there is a long delay between calling `.cancel()` and promise actually getting cancelled in Chrome when long stack traces are enabled ## 2.9.19 (2015-03-29) Bugfixes: - Fix crashing in Chrome when long stack traces are disabled ## 2.9.18 (2015-03-29) Bugfixes: - Fix settlePromises using trampoline ## 2.9.17 (2015-03-29) Bugfixes: - Fix Chrome DevTools async stack traceability ([#542](.)). ## 2.9.16 (2015-03-28) Features: - Use setImmediate if available ## 2.9.15 (2015-03-26) Features: - Added `.asCallback` alias for `.nodeify`. Bugfixes: - Don't always use nextTick, but try to pick up setImmediate or setTimeout in NW. Fixes [#534](.), [#525](.) - Make progress a core feature. Fixes [#535](.) Note that progress has been removed in 3.x - this is only a fix necessary for 2.x custom builds. ## 2.9.14 (2015-03-12) Bugfixes: - Always use process.nextTick. Fixes [#525](.) ## 2.9.13 (2015-02-27) Bugfixes: - Fix .each, .filter, .reduce and .map callbacks being called synchornously if the input is immediate. ([#513](.)) ## 2.9.12 (2015-02-19) Bugfixes: - Fix memory leak introduced in 2.9.0 ([#502](.)) ## 2.9.11 (2015-02-19) Bugfixes: - Fix [#503](.) ## 2.9.10 (2015-02-18) Bugfixes: - Fix [#501](.) ## 2.9.9 (2015-02-12) Bugfixes: - Fix `TypeError: Cannot assign to read only property 'length'` when jsdom has declared a read-only length for all objects to inherit. ## 2.9.8 (2015-02-10) Bugfixes: - Fix regression introduced in 2.9.7 where promisify didn't properly dynamically look up methods on `this` ## 2.9.7 (2015-02-08) Bugfixes: - Fix `promisify` not retaining custom properties of the function. This enables promisifying the `"request"` module's export function and its methods at the same time. - Fix `promisifyAll` methods being dependent on `this` when they are not originally dependent on `this`. This enables e.g. passing promisified `fs` functions directly as callbacks without having to bind them to `fs`. - Fix `process.nextTick` being used over `setImmediate` in node. ## 2.9.6 (2015-02-02) Bugfixes: - Node environment detection can no longer be fooled ## 2.9.5 (2015-02-02) Misc: - Warn when [`.then()`](.) is passed non-functions ## 2.9.4 (2015-01-30) Bugfixes: - Fix [.timeout()](.) not calling `clearTimeout` with the proper handle in node causing the process to wait for unneeded timeout. This was a regression introduced in 2.9.1. ## 2.9.3 (2015-01-27) Bugfixes: - Fix node-webkit compatibility issue ([#467](https://github.com/petkaantonov/bluebird/pull/467)) - Fix long stack trace support in recent firefox versions ## 2.9.2 (2015-01-26) Bugfixes: - Fix critical bug regarding to using promisifyAll in browser that was introduced in 2.9.0 ([#466](https://github.com/petkaantonov/bluebird/issues/466)). Misc: - Add `"browser"` entry point to package.json ## 2.9.1 (2015-01-24) Features: - If a bound promise is returned by the callback to [`Promise.method`](.) and [`Promise.try`](.), the returned promise will be bound to the same value ## 2.9.0 (2015-01-24) Features: - Add [`Promise.fromNode`](.) - Add new paramter `value` for [`Promise.bind`](.) Bugfixes: - Fix several issues with [`cancellation`](.) and [`.bind()`](.) interoperation when `thisArg` is a promise or thenable - Fix promises created in [`disposers`](.) not having proper long stack trace context - Fix [`Promise.join`](.) sometimes passing the passed in callback function as the last argument to itself. Misc: - Reduce minified full browser build file size by not including unused code generation functionality. - Major internal refactoring related to testing code and source code file layout ## 2.8.2 (2015-01-20) Features: - [Global rejection events](https://github.com/petkaantonov/bluebird/blob/master/API.md#global-rejection-events) are now fired both as DOM3 events and as legacy events in browsers ## 2.8.1 (2015-01-20) Bugfixes: - Fix long stack trace stiching consistency when rejected from thenables ## 2.8.0 (2015-01-19) Features: - Major debuggability improvements: - Long stack traces have been re-designed. They are now much more readable, succint, relevant and consistent across bluebird features. - Long stack traces are supported now in IE10+ ## 2.7.1 (2015-01-15) Bugfixes: - Fix [#447](.) ## 2.7.0 (2015-01-15) Features: - Added more context to stack traces originating from coroutines ([#421](https://github.com/petkaantonov/bluebird/issues/421)) - Implemented [global rejection events](https://github.com/petkaantonov/bluebird/blob/master/API.md#global-rejection-events) ([#428](https://github.com/petkaantonov/bluebird/issues/428), [#357](https://github.com/petkaantonov/bluebird/issues/357)) - [Custom promisifiers](https://github.com/petkaantonov/bluebird/blob/master/API.md#option-promisifier) are now passed the default promisifier which can be used to add enhancements on top of normal node promisification - [Promisification filters](https://github.com/petkaantonov/bluebird/blob/master/API.md#option-filter) are now passed `passesDefaultFilter` boolean Bugfixes: - Fix `.noConflict()` call signature ([#446]()) - Fix `Promise.method`ified functions being called with `undefined` when they were called with no arguments ## 2.6.4 (2015-01-12) Bugfixes: - `OperationalErrors` thrown by promisified functions retain custom properties, such as `.code` and `.path`. ## 2.6.3 (2015-01-12) Bugfixes: - Fix [#429](https://github.com/petkaantonov/bluebird/issues/429) - Fix [#432](https://github.com/petkaantonov/bluebird/issues/432) - Fix [#433](https://github.com/petkaantonov/bluebird/issues/433) ## 2.6.2 (2015-01-07) Bugfixes: - Fix [#426](https://github.com/petkaantonov/bluebird/issues/426) ## 2.6.1 (2015-01-07) Bugfixes: - Fixed built browser files not being included in the git tag release for bower ## 2.6.0 (2015-01-06) Features: - Significantly improve parallel promise performance and memory usage (+50% faster, -50% less memory) ## 2.5.3 (2014-12-30) ## 2.5.2 (2014-12-29) Bugfixes: - Fix bug where already resolved promise gets attached more handlers while calling its handlers resulting in some handlers not being called - Fix bug where then handlers are not called in the same order as they would run if Promises/A+ 2.3.2 was implemented as adoption - Fix bug where using `Object.create(null)` as a rejection reason would crash bluebird ## 2.5.1 (2014-12-29) Bugfixes: - Fix `.finally` throwing null error when it is derived from a promise that is resolved with a promise that is resolved with a promise ## 2.5.0 (2014-12-28) Features: - [`.get`](.) now supports negative indexing. Bugfixes: - Fix bug with `Promise.method` wrapped function returning a promise that never resolves if the function returns a promise that is resolved with another promise - Fix bug with `Promise.delay` never resolving if the value is a promise that is resolved with another promise ## 2.4.3 (2014-12-28) Bugfixes: - Fix memory leak as described in [this Promises/A+ spec issue](https://github.com/promises-aplus/promises-spec/issues/179). ## 2.4.2 (2014-12-21) Bugfixes: - Fix bug where spread rejected handler is ignored in case of rejection - Fix synchronous scheduler passed to `setScheduler` causing infinite loop ## 2.4.1 (2014-12-20) Features: - Error messages now have links to wiki pages for additional information - Promises now clean up all references (to handlers, child promises etc) as soon as possible. ## 2.4.0 (2014-12-18) Features: - Better filtering of bluebird internal calls in long stack traces, especially when using minified file in browsers - Small performance improvements for all collection methods - Promises now delete references to handlers attached to them as soon as possible - Additional stack traces are now output on stderr/`console.warn` for errors that are thrown in the process/window from rejected `.done()` promises. See [#411](https://github.com/petkaantonov/bluebird/issues/411) ## 2.3.11 (2014-10-31) Bugfixes: - Fix [#371](https://github.com/petkaantonov/bluebird/issues/371), [#373](https://github.com/petkaantonov/bluebird/issues/373) ## 2.3.10 (2014-10-28) Features: - `Promise.method` no longer wraps primitive errors - `Promise.try` no longer wraps primitive errors ## 2.3.7 (2014-10-25) Bugfixes: - Fix [#359](https://github.com/petkaantonov/bluebird/issues/359), [#362](https://github.com/petkaantonov/bluebird/issues/362) and [#364](https://github.com/petkaantonov/bluebird/issues/364) ## 2.3.6 (2014-10-15) Features: - Implement [`.reflect()`](.) ## 2.3.5 (2014-10-06) Bugfixes: - Fix issue when promisifying methods whose names contain the string 'args' ## 2.3.4 (2014-09-27) - `P` alias was not declared inside WebWorkers ## 2.3.3 (2014-09-27) Bugfixes: - Fix [#318](https://github.com/petkaantonov/bluebird/issues/318), [#314](https://github.com/petkaantonov/bluebird/issues/#314) ## 2.3.2 (2014-08-25) Bugfixes: - `P` alias for `Promise` now exists in global scope when using browser builds without a module loader, fixing an issue with firefox extensions ## 2.3.1 (2014-08-23) Features: - `.using` can now be used with disposers created from different bluebird copy ## 2.3.0 (2014-08-13) Features: - [`.bind()`](.) and [`Promise.bind()`](.) now await for the resolution of the `thisArg` if it's a promise or a thenable Bugfixes: - Fix [#276](https://github.com/petkaantonov/bluebird/issues/276) ## 2.2.2 (2014-07-14) - Fix [#259](https://github.com/petkaantonov/bluebird/issues/259) ## 2.2.1 (2014-07-07) - Fix multiline error messages only showing the first line ## 2.2.0 (2014-07-07) Bugfixes: - `.any` and `.some` now consistently reject with RangeError when input array contains too few promises - Fix iteration bug with `.reduce` when input array contains already fulfilled promises ## 2.1.3 (2014-06-18) Bugfixes: - Fix [#235](https://github.com/petkaantonov/bluebird/issues/235) ## 2.1.2 (2014-06-15) Bugfixes: - Fix [#232](https://github.com/petkaantonov/bluebird/issues/232) ## 2.1.1 (2014-06-11) ## 2.1.0 (2014-06-11) Features: - Add [`promisifier`](.) option to `Promise.promisifyAll()` - Improve performance of `.props()` and collection methods when used with immediate values Bugfixes: - Fix a bug where .reduce calls the callback for an already visited item - Fix a bug where stack trace limit is calculated to be too small, which resulted in too short stack traces Add undocumented experimental `yieldHandler` option to `Promise.coroutine` ## 2.0.7 (2014-06-08) ## 2.0.6 (2014-06-07) ## 2.0.5 (2014-06-05) ## 2.0.4 (2014-06-05) ## 2.0.3 (2014-06-05) ## 2.0.2 (2014-06-04) ## 2.0.1 (2014-06-04) ## 2.0.0 (2014-06-04) #What's new in 2.0 - [Resource management](api-reference.html#resource-management) - never leak resources again - [Promisification](api-reference.html#promisification) on steroids - entire modules can now be promisified with one line of code - [`.map()`](.), [`.each()`](.), [`.filter()`](.), [`.reduce()`](.) reimagined from simple sugar to powerful concurrency coordination tools - [API Documentation](api-reference.html) has been reorganized and more elaborate examples added - Deprecated [progression](#progression-migration) and [deferreds](#deferred-migration) - Improved performance and readability Features: - Added [`using()`](.) and [`disposer()`](.) - [`.map()`](.) now calls the handler as soon as items in the input array become fulfilled - Added a concurrency option to [`.map()`](.) - [`.filter()`](.) now calls the handler as soon as items in the input array become fulfilled - Added a concurrency option to [`.filter()`](.) - [`.reduce()`](.) now calls the handler as soon as items in the input array become fulfilled, but in-order - Added [`.each()`](.) - [`Promise.resolve()`](.) behaves like `Promise.cast`. `Promise.cast` deprecated. - [Synchronous inspection](api-reference.html#synchronous-inspection): Removed `.inspect()`, added [`.value()`](.) and [`.reason()`](.) - [`Promise.join()`](.) now takes a function as the last argument - Added [`Promise.setScheduler()`](.) - [`.cancel()`](.) supports a custom cancellation reason - [`.timeout()`](.) now cancels the promise instead of rejecting it - [`.nodeify()`](.) now supports passing multiple success results when mapping promises to nodebacks - Added `suffix` and `filter` options to [`Promise.promisifyAll()`](.) Breaking changes: - Sparse array holes are not skipped by collection methods but treated as existing elements with `undefined` value - `.map()` and `.filter()` do not call the given mapper or filterer function in any specific order - Removed the `.inspect()` method - Yielding an array from a coroutine is not supported by default. You can use [`coroutine.addYieldHandler()`](.) to configure the old behavior (or any behavior you want). - [`.any()`](.) and [`.some()`](.) no longer use an array as the rejection reason. [`AggregateError`](.) is used instead. ## 1.2.4 (2014-04-27) Bugfixes: - Fix promisifyAll causing a syntax error when a method name is not a valid identifier - Fix syntax error when es5.js is used in strict mode ## 1.2.3 (2014-04-17) Bugfixes: - Fix [#179](https://github.com/petkaantonov/bluebird/issues/179) ## 1.2.2 (2014-04-09) Bugfixes: - Promisified methods from promisifyAll no longer call the original method when it is overriden - Nodeify doesn't pass second argument to the callback if the promise is fulfilled with `undefined` ## 1.2.1 (2014-03-31) Bugfixes: - Fix [#168](https://github.com/petkaantonov/bluebird/issues/168) ## 1.2.0 (2014-03-29) Features: - New method: [`.value()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#value---dynamic) - New method: [`.reason()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#reason---dynamic) - New method: [`Promise.onUnhandledRejectionHandled()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#promiseonunhandledrejectionhandledfunction-handler---undefined) - `Promise.map()`, `.map()`, `Promise.filter()` and `.filter()` start calling their callbacks as soon as possible while retaining a correct order. See [`8085922f`](https://github.com/petkaantonov/bluebird/commit/8085922fb95a9987fda0cf2337598ab4a98dc315). Bugfixes: - Fix [#165](https://github.com/petkaantonov/bluebird/issues/165) - Fix [#166](https://github.com/petkaantonov/bluebird/issues/166) ## 1.1.1 (2014-03-18) Bugfixes: - [#138](https://github.com/petkaantonov/bluebird/issues/138) - [#144](https://github.com/petkaantonov/bluebird/issues/144) - [#148](https://github.com/petkaantonov/bluebird/issues/148) - [#151](https://github.com/petkaantonov/bluebird/issues/151) ## 1.1.0 (2014-03-08) Features: - Implement [`Promise.prototype.tap()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#tapfunction-handler---promise) - Implement [`Promise.coroutine.addYieldHandler()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#promisecoroutineaddyieldhandlerfunction-handler---void) - Deprecate `Promise.prototype.spawn` Bugfixes: - Fix already rejected promises being reported as unhandled when handled through collection methods - Fix browserisfy crashing from checking `process.version.indexOf` ## 1.0.8 (2014-03-03) Bugfixes: - Fix active domain being lost across asynchronous boundaries in Node.JS 10.xx ## 1.0.7 (2014-02-25) Bugfixes: - Fix handled errors being reported ## 1.0.6 (2014-02-17) Bugfixes: - Fix bug with unhandled rejections not being reported when using `Promise.try` or `Promise.method` without attaching further handlers ## 1.0.5 (2014-02-15) Features: - Node.js performance: promisified functions try to check amount of passed arguments in most optimal order - Node.js promisified functions will have same `.length` as the original function minus one (for the callback parameter) ## 1.0.4 (2014-02-09) Features: - Possibly unhandled rejection handler will always get a stack trace, even if the rejection or thrown error was not an error - Unhandled rejections are tracked per promise, not per error. So if you create multiple branches from a single ancestor and that ancestor gets rejected, each branch with no error handler with the end will cause a possibly unhandled rejection handler invocation Bugfixes: - Fix unhandled non-writable objects or primitives not reported by possibly unhandled rejection handler ## 1.0.3 (2014-02-05) Bugfixes: - [#93](https://github.com/petkaantonov/bluebird/issues/88) ## 1.0.2 (2014-02-04) Features: - Significantly improve performance of foreign bluebird thenables Bugfixes: - [#88](https://github.com/petkaantonov/bluebird/issues/88) ## 1.0.1 (2014-01-28) Features: - Error objects that have property `.isAsync = true` will now be caught by `.error()` Bugfixes: - Fix TypeError and RangeError shims not working without `new` operator ## 1.0.0 (2014-01-12) Features: - `.filter`, `.map`, and `.reduce` no longer skip sparse array holes. This is a backwards incompatible change. - Like `.map` and `.filter`, `.reduce` now allows returning promises and thenables from the iteration function. Bugfixes: - [#58](https://github.com/petkaantonov/bluebird/issues/58) - [#61](https://github.com/petkaantonov/bluebird/issues/61) - [#64](https://github.com/petkaantonov/bluebird/issues/64) - [#60](https://github.com/petkaantonov/bluebird/issues/60) ## 0.11.6-1 (2013-12-29) ## 0.11.6-0 (2013-12-29) Features: - You may now return promises and thenables from the filterer function used in `Promise.filter` and `Promise.prototype.filter`. - `.error()` now catches additional sources of rejections: - Rejections originating from `Promise.reject` - Rejections originating from thenables using the `reject` callback - Rejections originating from promisified callbacks which use the `errback` argument - Rejections originating from `new Promise` constructor where the `reject` callback is called explicitly - Rejections originating from `PromiseResolver` where `.reject()` method is called explicitly Bugfixes: - Fix `captureStackTrace` being called when it was `null` - Fix `Promise.map` not unwrapping thenables ## 0.11.5-1 (2013-12-15) ## 0.11.5-0 (2013-12-03) Features: - Improve performance of collection methods - Improve performance of promise chains ## 0.11.4-1 (2013-12-02) ## 0.11.4-0 (2013-12-02) Bugfixes: - Fix `Promise.some` behavior with arguments like negative integers, 0... - Fix stack traces of synchronously throwing promisified functions' ## 0.11.3-0 (2013-12-02) Features: - Improve performance of generators Bugfixes: - Fix critical bug with collection methods. ## 0.11.2-0 (2013-12-02) Features: - Improve performance of all collection methods ## 0.11.1-0 (2013-12-02) Features: - Improve overall performance. - Improve performance of promisified functions. - Improve performance of catch filters. - Improve performance of .finally. Bugfixes: - Fix `.finally()` rejecting if passed non-function. It will now ignore non-functions like `.then`. - Fix `.finally()` not converting thenables returned from the handler to promises. - `.spread()` now rejects if the ultimate value given to it is not spreadable. ## 0.11.0-0 (2013-12-02) Features: - Improve overall performance when not using `.bind()` or cancellation. - Promises are now not cancellable by default. This is backwards incompatible change - see [`.cancellable()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#cancellable---promise) - [`Promise.delay`](https://github.com/petkaantonov/bluebird/blob/master/API.md#promisedelaydynamic-value-int-ms---promise) - [`.delay()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#delayint-ms---promise) - [`.timeout()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#timeoutint-ms--string-message---promise) ## 0.10.14-0 (2013-12-01) Bugfixes: - Fix race condition when mixing 3rd party asynchrony. ## 0.10.13-1 (2013-11-30) ## 0.10.13-0 (2013-11-30) Bugfixes: - Fix another bug with progression. ## 0.10.12-0 (2013-11-30) Bugfixes: - Fix bug with progression. ## 0.10.11-4 (2013-11-29) ## 0.10.11-2 (2013-11-29) Bugfixes: - Fix `.race()` not propagating bound values. ## 0.10.11-1 (2013-11-29) Features: - Improve performance of `Promise.race` ## 0.10.11-0 (2013-11-29) Bugfixes: - Fixed `Promise.promisifyAll` invoking property accessors. Only data properties with function values are considered. ## 0.10.10-0 (2013-11-28) Features: - Disable long stack traces in browsers by default. Call `Promise.longStackTraces()` to enable them. ## 0.10.9-1 (2013-11-27) Bugfixes: - Fail early when `new Promise` is constructed incorrectly ## 0.10.9-0 (2013-11-27) Bugfixes: - Promise.props now takes a [thenable-for-collection](https://github.com/petkaantonov/bluebird/blob/f41edac61b7c421608ff439bb5a09b7cffeadcf9/test/mocha/props.js#L197-L217) - All promise collection methods now reject when a promise-or-thenable-for-collection turns out not to give a collection ## 0.10.8-0 (2013-11-25) Features: - All static collection methods take thenable-for-collection ## 0.10.7-0 (2013-11-25) Features: - throw TypeError when thenable resolves with itself - Make .race() and Promise.race() forever pending on empty collections ## 0.10.6-0 (2013-11-25) Bugfixes: - Promise.resolve and PromiseResolver.resolve follow thenables too. ## 0.10.5-0 (2013-11-24) Bugfixes: - Fix infinite loop when thenable resolves with itself ## 0.10.4-1 (2013-11-24) Bugfixes: - Fix a file missing from build. (Critical fix) ## 0.10.4-0 (2013-11-24) Features: - Remove dependency of es5-shim and es5-sham when using ES3. ## 0.10.3-0 (2013-11-24) Features: - Improve performance of `Promise.method` ## 0.10.2-1 (2013-11-24) Features: - Rename PromiseResolver#asCallback to PromiseResolver#callback ## 0.10.2-0 (2013-11-24) Features: - Remove memoization of thenables ## 0.10.1-0 (2013-11-21) Features: - Add methods `Promise.resolve()`, `Promise.reject()`, `Promise.defer()` and `.resolve()`. ## 0.10.0-1 (2013-11-17) ## 0.10.0-0 (2013-11-17) Features: - Implement `Promise.method()` - Implement `.return()` - Implement `.throw()` Bugfixes: - Fix promises being able to use themselves as resolution or follower value ## 0.9.11-1 (2013-11-14) Features: - Implicit `Promise.all()` when yielding an array from generators ## 0.9.11-0 (2013-11-13) Bugfixes: - Fix `.spread` not unwrapping thenables ## 0.9.10-2 (2013-11-13) Features: - Improve performance of promisified functions on V8 Bugfixes: - Report unhandled rejections even when long stack traces are disabled - Fix `.error()` showing up in stack traces ## 0.9.10-1 (2013-11-05) Bugfixes: - Catch filter method calls showing in stack traces ## 0.9.10-0 (2013-11-05) Bugfixes: - Support primitives in catch filters ## 0.9.9-0 (2013-11-05) Features: - Add `Promise.race()` and `.race()` ## 0.9.8-0 (2013-11-01) Bugfixes: - Fix bug with `Promise.try` not unwrapping returned promises and thenables ## 0.9.7-0 (2013-10-29) Bugfixes: - Fix bug with build files containing duplicated code for promise.js ## 0.9.6-0 (2013-10-28) Features: - Improve output of reporting unhandled non-errors - Implement RejectionError wrapping and `.error()` method ## 0.9.5-0 (2013-10-27) Features: - Allow fresh copies of the library to be made ## 0.9.4-1 (2013-10-27) ## 0.9.4-0 (2013-10-27) Bugfixes: - Rollback non-working multiple fresh copies feature ## 0.9.3-0 (2013-10-27) Features: - Allow fresh copies of the library to be made - Add more components to customized builds ## 0.9.2-1 (2013-10-25) ## 0.9.2-0 (2013-10-25) Features: - Allow custom builds ## 0.9.1-1 (2013-10-22) Bugfixes: - Fix unhandled rethrown exceptions not reported ## 0.9.1-0 (2013-10-22) Features: - Improve performance of `Promise.try` - Extend `Promise.try` to accept arguments and ctx to make it more usable in promisification of synchronous functions. ## 0.9.0-0 (2013-10-18) Features: - Implement `.bind` and `Promise.bind` Bugfixes: - Fix `.some()` when argument is a pending promise that later resolves to an array ## 0.8.5-1 (2013-10-17) Features: - Enable process wide long stack traces through BLUEBIRD_DEBUG environment variable ## 0.8.5-0 (2013-10-16) Features: - Improve performance of all collection methods Bugfixes: - Fix .finally passing the value to handlers - Remove kew from benchmarks due to bugs in the library breaking the benchmark - Fix some bluebird library calls potentially appearing in stack traces ## 0.8.4-1 (2013-10-15) Bugfixes: - Fix .pending() call showing in long stack traces ## 0.8.4-0 (2013-10-15) Bugfixes: - Fix PromiseArray and its sub-classes swallowing possibly unhandled rejections ## 0.8.3-3 (2013-10-14) Bugfixes: - Fix AMD-declaration using named module. ## 0.8.3-2 (2013-10-14) Features: - The mortals that can handle it may now release Zalgo by `require("bluebird/zalgo");` ## 0.8.3-1 (2013-10-14) Bugfixes: - Fix memory leak when using the same promise to attach handlers over and over again ## 0.8.3-0 (2013-10-13) Features: - Add `Promise.props()` and `Promise.prototype.props()`. They work like `.all()` for object properties. Bugfixes: - Fix bug with .some returning garbage when sparse arrays have rejections ## 0.8.2-2 (2013-10-13) Features: - Improve performance of `.reduce()` when `initialValue` can be synchronously cast to a value ## 0.8.2-1 (2013-10-12) Bugfixes: - Fix .npmignore having irrelevant files ## 0.8.2-0 (2013-10-12) Features: - Improve performance of `.some()` ## 0.8.1-0 (2013-10-11) Bugfixes: - Remove uses of dynamic evaluation (`new Function`, `eval` etc) when strictly not necessary. Use feature detection to use static evaluation to avoid errors when dynamic evaluation is prohibited. ## 0.8.0-3 (2013-10-10) Features: - Add `.asCallback` property to `PromiseResolver`s ## 0.8.0-2 (2013-10-10) ## 0.8.0-1 (2013-10-09) Features: - Improve overall performance. Be able to sustain infinite recursion when using promises. ## 0.8.0-0 (2013-10-09) Bugfixes: - Fix stackoverflow error when function calls itself "synchronously" from a promise handler ## 0.7.12-2 (2013-10-09) Bugfixes: - Fix safari 6 not using `MutationObserver` as a scheduler - Fix process exceptions interfering with internal queue flushing ## 0.7.12-1 (2013-10-09) Bugfixes: - Don't try to detect if generators are available to allow shims to be used ## 0.7.12-0 (2013-10-08) Features: - Promisification now consider all functions on the object and its prototype chain - Individual promisifcation uses current `this` if no explicit receiver is given - Give better stack traces when promisified callbacks throw or errback primitives such as strings by wrapping them in an `Error` object. Bugfixes: - Fix runtime APIs throwing synchronous errors ## 0.7.11-0 (2013-10-08) Features: - Deprecate `Promise.promisify(Object target)` in favor of `Promise.promisifyAll(Object target)` to avoid confusion with function objects - Coroutines now throw error when a non-promise is `yielded` ## 0.7.10-1 (2013-10-05) Features: - Make tests pass Internet Explorer 8 ## 0.7.10-0 (2013-10-05) Features: - Create browser tests ## 0.7.9-1 (2013-10-03) Bugfixes: - Fix promise cast bug when thenable fulfills using itself as the fulfillment value ## 0.7.9-0 (2013-10-03) Features: - More performance improvements when long stack traces are enabled ## 0.7.8-1 (2013-10-02) Features: - Performance improvements when long stack traces are enabled ## 0.7.8-0 (2013-10-02) Bugfixes: - Fix promisified methods not turning synchronous exceptions into rejections ## 0.7.7-1 (2013-10-02) Features: - feature Bugfixes: - bugfix ## 0.7.7-0 (2013-10-01) Features: - feature Bugfixes: - bugfix ## 0.7.6-0 (2013-09-29) Features: - feature Bugfixes: - bugfix ## 0.7.5-0 (2013-09-28) Features: - feature Bugfixes: - bugfix ## 0.7.4-1 (2013-09-28) Features: - feature Bugfixes: - bugfix ## 0.7.4-0 (2013-09-28) Features: - feature Bugfixes: - bugfix ## 0.7.3-1 (2013-09-28) Features: - feature Bugfixes: - bugfix ## 0.7.3-0 (2013-09-27) Features: - feature Bugfixes: - bugfix ## 0.7.2-0 (2013-09-27) Features: - feature Bugfixes: - bugfix ## 0.7.1-5 (2013-09-26) Features: - feature Bugfixes: - bugfix ## 0.7.1-4 (2013-09-25) Features: - feature Bugfixes: - bugfix ## 0.7.1-3 (2013-09-25) Features: - feature Bugfixes: - bugfix ## 0.7.1-2 (2013-09-24) Features: - feature Bugfixes: - bugfix ## 0.7.1-1 (2013-09-24) Features: - feature Bugfixes: - bugfix ## 0.7.1-0 (2013-09-24) Features: - feature Bugfixes: - bugfix ## 0.7.0-1 (2013-09-23) Features: - feature Bugfixes: - bugfix ## 0.7.0-0 (2013-09-23) Features: - feature Bugfixes: - bugfix ## 0.6.5-2 (2013-09-20) Features: - feature Bugfixes: - bugfix ## 0.6.5-1 (2013-09-18) Features: - feature Bugfixes: - bugfix ## 0.6.5-0 (2013-09-18) Features: - feature Bugfixes: - bugfix ## 0.6.4-1 (2013-09-18) Features: - feature Bugfixes: - bugfix ## 0.6.4-0 (2013-09-18) Features: - feature Bugfixes: - bugfix ## 0.6.3-4 (2013-09-18) Features: - feature Bugfixes: - bugfix ## 0.6.3-3 (2013-09-18) Features: - feature Bugfixes: - bugfix ## 0.6.3-2 (2013-09-16) Features: - feature Bugfixes: - bugfix ## 0.6.3-1 (2013-09-16) Features: - feature Bugfixes: - bugfix ## 0.6.3-0 (2013-09-15) Features: - feature Bugfixes: - bugfix ## 0.6.2-1 (2013-09-14) Features: - feature Bugfixes: - bugfix ## 0.6.2-0 (2013-09-14) Features: - feature Bugfixes: - bugfix ## 0.6.1-0 (2013-09-14) Features: - feature Bugfixes: - bugfix ## 0.6.0-0 (2013-09-13) Features: - feature Bugfixes: - bugfix ## 0.5.9-6 (2013-09-12) Features: - feature Bugfixes: - bugfix ## 0.5.9-5 (2013-09-12) Features: - feature Bugfixes: - bugfix ## 0.5.9-4 (2013-09-12) Features: - feature Bugfixes: - bugfix ## 0.5.9-3 (2013-09-11) Features: - feature Bugfixes: - bugfix ## 0.5.9-2 (2013-09-11) Features: - feature Bugfixes: - bugfix ## 0.5.9-1 (2013-09-11) Features: - feature Bugfixes: - bugfix ## 0.5.9-0 (2013-09-11) Features: - feature Bugfixes: - bugfix ## 0.5.8-1 (2013-09-11) Features: - feature Bugfixes: - bugfix ## 0.5.8-0 (2013-09-11) Features: - feature Bugfixes: - bugfix ## 0.5.7-0 (2013-09-11) Features: - feature Bugfixes: - bugfix ## 0.5.6-1 (2013-09-10) Features: - feature Bugfixes: - bugfix ## 0.5.6-0 (2013-09-10) Features: - feature Bugfixes: - bugfix ## 0.5.5-1 (2013-09-10) Features: - feature Bugfixes: - bugfix ## 0.5.5-0 (2013-09-09) Features: - feature Bugfixes: - bugfix ## 0.5.4-1 (2013-09-08) Features: - feature Bugfixes: - bugfix ## 0.5.4-0 (2013-09-08) Features: - feature Bugfixes: - bugfix ## 0.5.3-0 (2013-09-07) Features: - feature Bugfixes: - bugfix ## 0.5.2-0 (2013-09-07) Features: - feature Bugfixes: - bugfix ## 0.5.1-0 (2013-09-07) Features: - feature Bugfixes: - bugfix ## 0.5.0-0 (2013-09-07) Features: - feature Bugfixes: - bugfix ## 0.4.0-0 (2013-09-06) Features: - feature Bugfixes: - bugfix ## 0.3.0-1 (2013-09-06) Features: - feature Bugfixes: - bugfix ## 0.3.0 (2013-09-06) ================================================ FILE: docs/docs/coming-from-other-languages.md ================================================ --- id: coming-from-other-languages title: Coming from Other Languages --- This page describes parallels of using promises in other languages. Promises as a pattern are very common in other languages and knowing what they map to in other languages might help you with grasping them conceptually - [C#](#c) - [Scala](#scala) - [Python](#python) - [C++](#c) - [Haskell](#haskell) - [Java](#java) - [Android Java](#android-java) - [Objective-C](#objective-c) ## C# A promise is similar to a C# `Task`. They both represent the result of an operation. A promise's `then` method is similar to a Task's `ContinueWith` method in that both allow attaching a continuation to the promise. Bluebird's [Promise.coroutine](.) is analogous to C#'s `async/await` syntax. A `TaskCompletionSource` is analogous to the promise constructor. Although usually promisification is preferred (see the API reference or working with callbacks section). `Task.FromResult` is analogous to [Promise.resolve](.). The difference between a `Task` and a promise are that a task might not be started and might require a `.Start` call where a promise always represents an already started operation. In addition promises are always unwrapped. A promise implicitly has `Task.Unwrap` called on it - that is, promises perform recursive assimilation of promises within them. See [this question on StackOverflow](http://stackoverflow.com/questions/26136389/how-can-i-realize-pattern-promise-deffered) for more differences. ## Scala A bluebird promise is similar to a Scala `Future`. A scala `Promise` is similar to how the promise constructor can be used (previously, to a bluebird Deferred). Just like a future, a promise represents a value over time. The value can resolve to either a fulfilled (ok completion) or rejected (error completion) state. Where blocking on a Future in scala is discouraged, in JavaScript it's downright impossible. In addition promises are always unwrapped. That is, promises perform recursive assimilation of promises within them. You can't have a `Promise>` where a `Future[Future[T]]` is valid in Scala. See [this question on StackOverflow](http://stackoverflow.com/questions/22724883/js-deferred-promise-future-compared-to-functional-languages-like-scala) for more differences. ## Python A promise is similar to a Twisted Deferred object. In fact the first JavaScript implementations of promises were based on it. However, the APIs have diverged since. The mental model is still very similar. A promise is _not_ similar to a Python `concurrent.Future` which does not chain actions. Asyncio coroutines are similar to bluebird coroutines in what they let you do, however bluebird coroutines also enable functional-style chaining. ## C++ A bluebird promise is similar to a `std::future` and the promise constructor is similar to an `std::promise` although it should rarely be used in practice (see the promisification section). However, a bluebird promise is more powerful than the current implementation of `std::future` since while chaining has been discussed it is not yet implemented. Promises can be chained together. Boost futures expose a `.then` method similar to promises and allow this functionality. ## Haskell A promise is a monadic construct with `.then` filling the role of `>>=` (bind). The major difference is that `.then` performs recursive assimilation which acts like a `flatMap` or a map. The type signature of `then` is quote complicated. If we omit the error argument and not throw - it's similar to: ```hs then::Promise a -> (a -> (Either (Promise b) b)) -> Promise b ``` That is, you can return either a promise _or a plain value_ from a `then` without wrapping it. Promises perform a role similar to `IO` in that they allow for easy chaining of asynchronous non-blocking operations. `Promise.coroutine` can be seen as similar to `do` notation although in practice it's not an accurate comparison. ## Java A promise is similar to a guava `Future` with `chain` being similar to `then`. If your'e familiar with Java 8 lambdas, you can think of a promise as a `Future` you can `map` to another future. ## Android Java Several popular Android libraries use promises - for example the Parse Java API returns `Task`s which are similar to JavaScript promises. ## Objective-C If you're familiar with PromiseKit, it is based on a same specification bluebird is based on so the API should feel familiar right away. ================================================ FILE: docs/docs/coming-from-other-libraries.md ================================================ --- id: coming-from-other-libraries title: Coming from Other Libraries --- This page is a reference for migrating to bluebird from other flow control or promise libraries. See [installation](install.html) on how to use bluebird in your environment. - [Coming from native promises](#coming-from-native-promises) - [Coming from jQuery deferreds](#coming-from-jquery-deferreds) - [Coming from `async` module](#coming-from-async-module) - [Coming from Q](#coming-from-q) - [Coming from co/koa](#coming-from-co) - [Coming from highland, RxJS or BaconJS](#coming-from-highland) ##Coming from native promises Bluebird promises are a drop-in replacement for native promises except for subclassing. Additionally you might want to replace usages of the often incorrectly used [Promise.race](.) with bluebird's [Promise.any](.) which does what is usually mistakenly expected from [Promise.race](.). For maximum compatibility, bluebird does provide [Promise.race](.) with ES6 semantics. You can also refactor some looping patterns to a more natural form that would [leak memory when using native promises](https://github.com/promises-aplus/promises-spec/issues/179). ##Coming from jQuery deferreds Bluebird treats jQuery deferreds and promises interchangeably. Wherever you can take a promise or return a promise, you can take or return a jQuery deferred instead and it works the same. For instance, there is no need to write something like this: ```js var firstRequest = new Promise(function(resolve, reject) { $.ajax({...}).done(resolve).fail(reject); }); var secondRequest = new Promise(function(resolve, reject) { $.ajax({...}).done(resolve).fail(reject); }); Promise.all([firstRequest, secondRequest]).then(function() { // ... }); ``` Since [Promise.all](.) takes promises, it must also take jQuery deferreds, so the above can be shortened to: ```js var firstRequest = $.ajax({...}); var secondRequest = $.ajax({...}); Promise.all([firstRequest, secondRequest]).then(function() { // ... }); ``` That said, if you have code written using jQuery deferred methods, such as `.then`, `.done` and so on, you cannot drop-in replace the jQuery deferred with a bluebird promise in that code. Despite having the same names, jQuery deferred methods have different semantics than bluebird promise methods. These differences are due to the completely different goals of the implementations. Bluebird is [an internal DSL](http://en.wikipedia.org/wiki/Domain-specific_language) for the domain of asynchronous control flow while jQuery deferreds are a callback aggregator utility ("glorified event emitters"). If you do have some code using jQuery deferred methods extensively try to see if some of these jQuery deferred patterns and their replacements can be applied: ```js // jQuery $.when.apply($, someArray).then(...) // bluebird Promise.all(someArray).then(...) ``` ```js // jQuery var data = [1,2,3,4]; var processItemsDeferred = []; for(var i = 0; i < data.length; i++) { processItemsDeferred.push(processItem(data[i])); } $.when.apply($, processItemsDeferred).then(everythingDone); // bluebird var data = [1,2,3,4]; Promise.map(data, function(item) { return processItem(item); }).then(everythingDone); ``` ```js // jQuery var d = $.Deferred(); d.resolve("value"); // bluebird var d = Promise.resolve("value"); ``` ```js // jQuery var d = $.Deferred(); d.reject(new Error("error")); // bluebird var d = Promise.reject(new Error("error")); ``` ```js // jQuery var clicked = $.Deferred(); $("body").one("click", function(e) { clicked.resolve(e); }); // bluebird var clicked = new Promise(function(resolve) { $("body").one("click", resolve); }); ``` ```js // jQuery .always(removeSpinner); // bluebird .finally(removeSpinner); ``` ##Coming from `async` module When working with promises the philosophy is basically a complete opposite than when using `async`. Async provides a huge bag of uncomposable helper functions that work at a very low level of abstraction. When using promises you can get the utility otherwise provided by uncountable amount of inflexible helper functions by just combining and composing a few existing functions and concepts. That means when you have a problem there probably isn't an existing function tailored exactly to that problem but instead you can just combine the existing utilities to arrive at a solution. The upside of this is that you don't need to come up with all these different functions to solve problems that are not that different from each other. The most important thing to do when migrating from async to bluebird is this profound shift in philosophy. This section lists the most common async module replacements. ###`async.waterfall` If the waterfall elements are static, you can just replace it with a normal promise chain. For waterfalls with dynamic steps, use [Promise.each](.). Multiple arguments can be ferried in an array. Implementing the example from [async homepage](https://github.com/caolan/async#waterfalltasks-callback) ```js async.waterfall([ function(callback) { callback(null, 'one', 'two'); }, function(arg1, arg2, callback) { // arg1 now equals 'one' and arg2 now equals 'two' callback(null, 'three'); }, function(arg1, callback) { // arg1 now equals 'three' callback(null, 'done'); } ], function (err, result) { // result now equals 'done' }); ``` Since the array passed to waterfall is static (always the same 3 functions) a plain old promise chain is used: ```js Promise.resolve(['one', 'two']).spread(function(arg1, arg2) { // arg1 now equals 'one' and arg2 now equals 'two' return 'three'; }).then(function(arg1) { // arg1 now equals 'three' return 'done'; }).then(function(result) { // result now equals 'done' }); ``` If destructuring parameters are supported, `.spread(function(arg1, arg2) {})` can be replaced with `.then(function([arg1, arg2]){})`. ###`async.series` Using [Promise.mapSeries](.) to implement the example from [async homepage](https://github.com/caolan/async#seriestasks-callback): ```js async.series([ function(callback){ setTimeout(function(){ callback(null, 1); }, 200); }, function(callback){ setTimeout(function(){ callback(null, 2); }, 100); } ], // optional callback function(err, results){ // results is now equal to [1, 2] }); ``` ```js Promise.mapSeries([{timeout: 200, value: 1}, {timeout: 100, value: 2}], function(item) { return Promise.delay(item.timeout, item.value); }).then(function(results) { // results is now equal to [1, 2] }); ``` ###`async.parallel` Using [Promise.all](.) to implement the example from [async homepage](https://github.com/caolan/async#parallel): ```js async.parallel([ function(callback){ setTimeout(function(){ callback(null, 'one'); }, 200); }, function(callback){ setTimeout(function(){ callback(null, 'two'); }, 100); } ], // optional callback function(err, results){ // the results array will equal ['one','two'] even though // the second function had a shorter timeout. }); ``` ```js Promise.all([Promise.delay(200, 'one'), Promise.delay(100, 'two')]).then(function(results) { // the results array will equal ['one','two'] even though // the second function had a shorter timeout. }); ``` ###`async.mapSeries` Using [Promise.each](.) to implement the example from [async homepage](https://github.com/caolan/async#maparr-iterator-callback): ```js var fs = require('fs'); async.mapSeries(['file1','file2','file3'], fs.stat, function(err, results){ // results is now an array of stats for each file }); ``` ```js var fs = Promise.promisifyAll(require('fs')); Promise.each(['file1','file2','file3'], function(fileName, index, length) { return fs.statAsync(fileName); }).then(function(results) { // results is now an array of stats for each file }); ``` ###`async.map` Using [Promise.map](.) to implement the example from [async homepage](https://github.com/caolan/async#maparr-iterator-callback): ```js var fs = require('fs'); async.map(['file1','file2','file3'], fs.stat, function(err, results){ // results is now an array of stats for each file }); ``` ```js var fs = Promise.promisifyAll(require('fs')); Promise.map(['file1','file2','file3'], function(fileName, index, length) { return fs.statAsync(fileName); }).then(function(results) { // results is now an array of stats for each file }); ``` ###`async.whilst` Using recursion to implement the example from [async homepage](https://github.com/caolan/async#whilsttest-fn-callback): ```js var count = 0; async.whilst( function () { return count < 5; }, function (callback) { count++; setTimeout(callback, 1000); }, function (err) { // 5 seconds have passed } ); ``` ```js (function loop() { if (count < 5) { count++; return Promise.delay(1000).then(loop); } return Promise.resolve(); })().then(function() { // 5 seconds have passed }); ``` Be warned that the above example implementations are only superficially equivalent. Callbacks, even with the help of async, require too much boilerplate code to provide the same guarantees as promises. ##Coming from Q Q and bluebird share a lot of common methods that nevertheless have different names: - `Q(...)` -> [Promise.resolve()](.) - `.fail()` -> [.catch()](.) or `.caught()` - `.fin()` -> [.finally()](.) or `.lastly()` - `Q.fcall()` -> [Promise.try](.) or `Promise.attempt()` - `.thenResolve()` -> [.return()](.) or `.thenReturn()` - `.thenReject()` -> [.throw()](.) or `thenThrow()` ##Coming from co/koa In recent versions generator libraries started abandoning old ideas of special tokens passed to callbacks and started using promises for what's being yielded. Bluebird's [Promise.coroutine](.) is a superset of the `co` library, being more extensible as well as supporting cancellation (in environments where [`Generator#return`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return) is implemented). ##Coming from highland, RxJS or BaconJS Stream libraries tend to serve a different purpose than promise libraries. Unlike promise libraries streams can represent multiple values. Check out the benchmarks section for examples of transitioning an API from Bacon/Rx to promises. ================================================ FILE: docs/docs/contribute.md ================================================ --- id: contribute title: Contribute --- For development tasks such as contributing, running benchmarks or testing, you need to clone the repository and install dev-dependencies. Install [node](http://nodejs.org/) git clone git@github.com:petkaantonov/bluebird.git cd bluebird npm install - [Directory structure](#directory-structure) - [Style guide](#style-guide) - [Building](#building) - [Supported options by the build tool](#supported-options-by-the-build-tool) - [Testing](#testing) - [Testing in browsers](#testing-in-browsers) - [Supported options by the test tool](#supported-options-by-the-test-tool) - [Benchmarking](#benchmarking) ## Directory structure - `/benchmark` contains benchmark scripts and stats of benchmarks - `/tools` contains building and testing tools and scripts - `/src` contains the source code - `/test` contains test code - `/test/mocha` contains tests using the mocha testing framework - `/test/browser` a directory that can be statically served using a webserver to run tests in browsers. See [testing in browsers](README.md#testing-in-browsers). ## Style guide Use the same style as is used in the surrounding code. ###Whitespace - No more than 80 columns per line - 4 space indentation - No trailing whitespace - LF at end of files - Curly braces can be left out of single statement `if/else/else if`s when it is obvious there will never be multiple statements such as null check at the top of a function for an early return. - Add an additional new line between logical sections of code. ###Variables - Use multiple `var` statements instead of a single one with comma separator. Do not declare variables until you need them. ###Equality and type checks - Always use `===` except when checking for null or undefined. To check for null or undefined, use `x == null`. - For checks that can be done with `typeof`: do not make helper functions, save results of `typeof` to a variable or make the type string a non-constant. Always write the check in the form `typeof expression === "constant string"` even if it feels like repeating yourself. ##Building ``` node tools/build --debug --release --zalgo --browser --minify ``` ###Supported options by the build tool The value of boolean flags is determined by presence, if you want to pass false value for a boolean flag, use the `no-`-prefix e.g. `--no-debug`. - `--release` - Whether to build the release build. The release build is placed at `js/release` directory. Default `false`. - `--debug` - Whether to build the debug build. The debug build is placed at `js/debug` directory. Default `false`. - `--zalgo` - Whether to build the zalgo build. The zalgo build is placed at `js/zalgo` directory. Default `false`. - `--browser` - Whether to compile the browser build. The browser build file is placed at `js/browser/bluebird.js` Default `false`. - `--minify` - Whether to minify the compiled browser build. The minified browser build file is placed at `js/browser/bluebird.min.js` Default `true`. ##Testing To run all tests, run node --expose-gc tools/test If you need to run generator tests in older versions of NodeJS run the `tool/test.js` script with `--harmony` argument and 0.11+: node-dev --harmony tools/test In recent versions of NodeJS where generators are enabled by default: node tools/test You may specify an individual test file to run with the `--run` script flag: node tools/test --run=cancel.js This enables output from the test and may give a better idea where the test is failing. The paramter to `--run` can be any file name located in `test/mocha` folder. ###Testing in browsers To run the test in a browser instead of node, pass the flag `--browser` to the test tool node tools/test --run=cancel.js --browser This will automatically create a server (default port 9999) and open it in your default browser once the tests have been compiled. Keep the test tab active because some tests are timing-sensitive and will fail if the browser is throttling timeouts. Chrome will do this for example when the tab is not active. ###Supported options by the test tool The value of boolean flags is determined by presence, if you want to pass false value for a boolean flag, use the `no-`-prefix e.g. `--no-browser`. - `--run=String`. Which tests to run (or compile when testing in browser). Default `"all"`. Can also be a glob string (relative to ./test/mocha folder) - `--cover=String`. Create code coverage using the String as istanbul reporter. Coverage is created in the ./coverage folder. No coverage is created by default, default reporter is `"html"` (use `--cover` to use default reporter). - `--browser` - Whether to compile tests for browsers. Default `false`. - `--port=Number` - Whe port where local server is hosted when testing in browser. Default `9999` - `--execute-browser-tests` - Whether to execute the compiled tests for browser when using `--browser`. Default `true`. - `--open-browser` - Whether to open the default browser when executing browser tests. Default `true`. - `--fake-timers` - Whether to use fake timers (`setTimeout` etc) when running tests in node. Default `true`. - `--js-hint` - Whether to run JSHint on source files. Default `true`. - `--saucelabs` Wheter to create a tunnel to sauce labs and run tests in their VMs instead of your browser when compiling tests for browser.Default `false`. ##Benchmarking To run a benchmark, run the given command for a benchmark while on the project root. Requires bash (on windows the mingw32 that comes with git works fine too). Each benchmark must - Have implementations that do the same thing - Run each implementation of a benchmark in a separate freshly created process - Warmup each implementation before timing ###1\. DoxBee sequential Currently the most relevant benchmark is @gorkikosev's benchmark in the article [Analysis of generators and other async patterns in node](http://spion.github.io/posts/analysis-generators-and-other-async-patterns-node.html). The benchmark emulates a situation where n amount of users are making a request in parallel to execute some mixed async/sync action. Command: `bench doxbee` The implementations for this benchmark are found in `benchmark/doxbee-sequential` directory. ###2\. Parallel This made-up scenario runs 25 shimmed queries in parallel. Command: `bench parallel` The implementations for this benchmark are found in `benchmark/madeup-parallel` directory. ================================================ FILE: docs/docs/deprecated-apis.md ================================================ --- id: deprecated-apis title: Deprecated APIs --- [deprecated-apis](unfinished-article) This file contains documentation for APIs that are no longer supported by Bluebird. These APIs still work in Bluebird but will be removed at a future version of the library. For every use case that the methods below solve there exists a better alternative in [the API reference](/docs/api-reference.html). - [Progression](#progression) - [`.progressed(Function handler)`](#progressedfunction-handler---promise) - [`.then([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])`](#thenfunction-fulfilledhandler--function-rejectedhandler---function-progresshandler----promise) - [`.done([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])`](#donefunction-fulfilledhandler--function-rejectedhandler---function-progresshandler----promise) - [Promise resolution](#promise-resolution) - [`.resolve(dynamic value)`](#resolvedynamic-value---undefined) - [`.reject(dynamic reason)`](#rejectdynamic-reason---undefined) - [`.progress(dynamic value)`](#progressdynamic-value---undefined) - [`.callback`](#callback---function) - [Old Promise Cancellation](#old-promise-cancellation) ##Progression The old progression API was meant to be used for tracking the progress of promise resolution. In retrospect, it did not work or compose very well. We understand that problem better now and the use case could be better solved without it. See [Progression Migration](./api/progression-migration.html) for migration assistance and examples of how to convert APIs that use progression to ones that do not. #####`.progressed(Function handler)` -> `Promise` Shorthand for `.then(null, null, handler);`. Attach a progress handler that will be called if this promise is progressed. Returns a new promise chained from this promise.
#####`.then([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])` -> `Promise` The standard [Promises/A+ `.then()`](http://promises-aplus.github.io/promises-spec/) is still supported by Bluebird and support for it will continue indefinitely . However, the variant accepting a third `progressHandler` argument is no longer supported.
#####`.done([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])` -> `void` Like `.then()`, but any unhandled rejection that ends up here will be thrown as an error. Again, only the variant with the progression handler is deprecated here. `.done` is still fully supported.
##Promise resolution A `PromiseResolver` can be used to control the fate of a promise. It is like "Deferred" in jQuery or `$q.defer` in $q. The `PromiseResolver` objects have a `.promise` property which returns a reference to the controlled promise that can be passed to clients. `.promise` of a `PromiseResolver` is not a getter function to match other implementations. The methods of a `PromiseResolver` have no effect if the fate of the underlying promise is already decided (follow, reject, fulfill). **The use of `Promise.defer` and deferred objects is discouraged - it is much more awkward and error-prone than using `new Promise`.**
#####`.resolve(dynamic value)` -> `undefined` Resolve the underlying promise with `value` as the resolution value. If `value` is a thenable or a promise, the underlying promise will assume its state.
#####`.reject(dynamic reason)` -> `undefined` Reject the underlying promise with `reason` as the rejection reason.
#####`.progress(dynamic value)` -> `undefined` Progress the underlying promise with `value` as the progression value. Example ```js function delay(ms) { var resolver = Promise.defer(); var now = Date.now(); setTimeout(function(){ resolver.resolve(Date.now() - now); }, ms); return resolver.promise; } delay(500).then(function(ms){ console.log(ms + " ms passed"); }); ```
##Old Promise Cancellation In 2.x, promise cancellation looked very differently. Promise cancellation received a major overhaul for version 3 in order to create a sound variant of cancellable promises. You can still use 2.x cancellation with bluebird 2.x (which is still supported - but not recommended). See [Cancellation](/cancellation.html) for more details. The 2.x docs are [still accessible under the 2.x branch](https://github.com/petkaantonov/bluebird/blob/2.x/API.md). ================================================ FILE: docs/docs/deprecated_apis.md ================================================ --- id: deprecated_apis title: Deprecated APIs --- This file contains documentation for APIs that are no longer supported by Bluebird. These APIs still work in Bluebird but will be removed at a future version of the library. For every use case that the methods below solve there exists a better alternative in [the API reference](./API.md). - [Progression](#progression) - [`.progressed(Function handler)`](#.progressed) - [`.then([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])`](#.then) - [`.done([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])`](#.done) - [`.fork([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])`](#.fork) - [Promise resolution](#promise-resolution) - [`.resolve(dynamic value)`](#.resolve) - [`.reject(dynamic reason)`](#.reject) - [`.progress(dynamic value)`](#.progress) - [`.callback`](#.callback) ##Progression The old progression API was meant to be used for tracking the progress of promise resolution. In retrospect, it did not work or compose very well. We understand that problem better now and the use case could be better solved without it. See [Progression Migration](./API.md#progression-migration) for migration assistance and examples of how to convert APIs that use progression to ones that do not. #####`.progressed(Function handler)` -> `Promise` Shorthand for [`.then(null, null, handler);`](.). Attach a progress handler that will be called if this promise is progressed. Returns a new promise chained from this promise.
#####`.then([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])` -> `Promise` The standard [Promises/A+ `.then()`](http://promises-aplus.github.io/promises-spec/) is still supported by Bluebird and support for it will continue indefinitely. However, the variant accepting a third `progressHandler` argument is no longer supported.
#####`.done([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])` -> `void` Like [`.then()`](.), but any unhandled rejection that ends up here will be thrown as an error. Again, only the variant with the progression handler is deprecated here. `.done` is still fully supported.
#####`.fork([Function fulfilledHandler] [, Function rejectedHandler ] [, Function progressHandler ])` -> `Promise` Like [`.then()`](.), but cancellation of the returned promise or any of its descendant will not propagate cancellation to this promise or this promise's ancestors. Again, only the variant with the progression handler is deprecated here. `.fork` is still fully supported.
##Promise resolution A `PromiseResolver` can be used to control the fate of a promise. It is like "Deferred" in jQuery or `$q.defer` in $q. The `PromiseResolver` objects have a `.promise` property which is a reference to the controlled promise that can be passed to clients. `.promise` of a `PromiseResolver` is not a getter function to match other implementations. The methods of a `PromiseResolver` have no effect if the fate of the underlying promise is already decided (follow, reject, fulfill). **The use of `Promise.defer` and deferred objects is discouraged - it is much more awkward and error-prone than using `new Promise`.**
#####`.resolve(dynamic value)` -> `undefined` Resolve the underlying promise with `value` as the resolution value. If `value` is a thenable or a promise, the underlying promise will assume its state.
#####`.reject(dynamic reason)` -> `undefined` Reject the underlying promise with `reason` as the rejection reason.
#####`.progress(dynamic value)` -> `undefined` Progress the underlying promise with `value` as the progression value. Example ```js function delay(ms) { var resolver = Promise.defer(); var now = Date.now(); setTimeout(function() { resolver.resolve(Date.now() - now); }, ms); return resolver.promise; } delay(500).then(function(ms) { console.log(ms + " ms passed"); }); ```
================================================ FILE: docs/docs/download-api-reference.md ================================================ --- id: download-api-reference title: Download API Reference --- In order to use the documentation offline (without a stable internet connection). - Go to [the GitHub Pages Branch `gh-pages`](https://github.com/petkaantonov/bluebird/tree/gh-pages). - Click "Clone Or Download". - Click "Download Zip". - Extract the contents of the zip and open the "docs" folder. - Open `api-reference.html` which is the documentation root. ================================================ FILE: docs/docs/error-explanations.md ================================================ --- id: error-explanations title: Error Explanations --- - [Error: Promise.promisify called on an object](#error-promise.promisify-called-on-an-object) - [Error: the promise constructor requires a resolver function](#error-the-promise-constructor-requires-a-resolver-function) - [Error: the promise constructor cannot be invoked directly](#error-the-promise-constructor-cannot-be-invoked-directly) - [Error: expecting an array, a promise or a thenable](#error-expecting-an-array-a-promise-or-a-thenable) - [Error: generatorFunction must be a function](#error-generatorfunction-must-be-a-function) - [Error: fn must be a function](#error-fn-must-be-a-function) - [Error: cannot enable long stack traces after promises have been created](#error-cannot-enable-long-stack-traces-after-promises-have-been-created) - [Error: cannot get fulfillment value of a non-fulfilled promise](#error-cannot-get-fulfillment-value-of-a-non-fulfilled-promise) - [Error: cannot get rejection reason of a non-rejected promise](#error-cannot-get-rejection-reason-of-a-non-rejected-promise) - [Error: the target of promisifyAll must be an object or a function](#error-the-target-of-promisifyall-must-be-an-object-or-a-function) - [Error: circular promise resolution chain](#error-circular-promise-resolution-chain) - [Error: cannot await properties of a non-object](#error-cannot-await-properties-of-a-non-object) - [Error: expecting a positive integer](#error-expecting-a-positive-integer) - [Error: A value was yielded that could not be treated as a promise](#error-a-value-was-yielded-that-could-not-be-treated-as-a-promise) - [Error: cannot await properties of a non object](#error-cannot-await-properties-of-a-non-object) - [Error: Cannot promisify an API that has normal methods](#error-cannot-promisify-an-api-that-has-normal-methods) - [Error: Catch filter must inherit from Error or be a simple predicate function](#error-catch-filter-must-inherit-from-error-or-be-a-simple-predicate-function) - [Error: No async scheduler available](#error-no-async-scheduler-available) ## Error: Promise.promisify called on an object You got this this error because you've used `Promise.promisify` on an object, for example: ```js var fs = Promise.promisify(require("fs")); ``` Instead, use [`Promise.promisifyAll`](.) : ```js var fs = Promise.promisifyAll(require("fs")); ``` ## Error: the promise constructor requires a resolver function You got this error because you used `new Promise()` or `new Promise(something)` without passing a function as the parameter. If you want to wrap an API with a promise manually, the correct syntax is: ```js function wrapWithPromise(parameter) { return new Promise(function (resolve, reject) { doSomethingAsync({ error:reject, success:resolve }); }); } ``` Please consider reading about [new Promise](.) and also consider checking out automatic [promisification](.) as well as [Promise.method](.) ## Error: the promise constructor cannot be invoked directly You can get this error for several reasons: #### 1. You forgot to use `new` when creating a new promise using `new Promise(resolver)` syntax. This can happen when you tried to do something like: return Promise(function(resolve,reject){ //... }) You can correct this by doing: return new Promise(function(resolve,reject){ //... }) Please consider reading about [new Promise](.) and also consider checking out automatic [promisification](.) as well as [Promise.method](.) #### 2. You are trying to subclass `Promise` Bluebird does not support extending promises this way. Instead, see [scoped prototypes](features.html#scoped-prototypes). ## Error: expecting an array, a promise or a thenable The function being called expects a Promise, but is given something different. There are two main reasons why this may occur. **1. Working with collections (like arrays) but pass a single, non-collection element instead** Example: ```js function returnThree(){ return 3;} Promise.resolve(5).map(returnThree).then(function(val){ console.log("Hello Value!",val); }); ``` The `map` operation is expecting an array here (or a promise on one) and instead gets the number `5`. ```js function returnThree(){ return 3;} Promise.resolve([5]).map(returnThree).then(function(val){ console.log("Hello Value!",val); }); ``` ```map``` is given an array with a single element (see ```[5]``` instead of ```5```), so this statement will work (but is bad practice). --- **2.```return``` is forgotten in a 'fat' arrow / anonymous function call ```=>```:** When debugging or performing a one-time operation on a variable before passing it to a function, a return variable is forgotten. Example: ```js function nextFunction(something){ return Promise.resolve(something*3); } myFunction() .then(result => nextFunction(result)); // We are implicitly returning a Promise ``` Debugging, we want to see the value of result, so we add a ```console.log()``` line: ```js function nextFunction(something){ return Promise.resolve(something*3); } myFunction().then(result => { console.log("Debug:", result); nextFunction(result)); // The chain is broken! We don't return anything to the .then() call }); ``` As this is an anonymous function call, we need to **return** something, which is not currently happening. To fix, simply remember to add ```return``` in front of your promise-complying function: ```js function nextFunction(something){ return Promise.resolve(something*3); } myFunction().then(result => { console.log("Debug:", result); return nextFunction(result)); // The anonymous function returns the function which returns the promise .then() needs }); ``` ## Error: generatorFunction must be a function You are getting this error when trying to use [Promise.coroutine](.) and not passing it a generator function as a parameter. For example: ```js Promise.coroutine(function* () { // Note the * var data = yield $.get("http://www.example.com"); var moreUrls = data.split("\n"); var contents = []; for( var i = 0, len = moreUrls.length; i < len; ++i ) { contents.push(yield $.get(moreUrls[i])); } return contents; }); ``` Please refer to the relevant section in the documentation about [Generators](.) in order to get usage instructions: **Note**: Bluebird used to eagerly check for generators which caused problems with transpilers. Because of this, you might get an error similar to `TypeError: Cannot read property 'next' of undefined` if you pass a function instead of a generator function to Bluebird. [Promise.coroutine](.) is built to work with generators to form C# like `async/await` ## Error: fn must be a function You passed a non-function where a function was expected. ## Error: cannot enable long stack traces after promises have been created You are getting this error because you are enabling long stack traces after a promise has already been created. When using `longStackTraces` the first line in your code after requiring Bluebird should be: ```js Promise.config({ longStackTraces: true }); ``` See the API page about [Promise.longStackTraces](.) ## Error: cannot get fulfillment value of a non-fulfilled promise You can get this error when you're trying to call `.value` or `.error` when inspecting a promise where the promise has not been fulfilled or rejected yet. For example: ```js var p = Promise.delay(1000); p.inspect().value(); ``` Consider using [.isPending()](.) [.isFulfilled()](.) and [.isRejected()](.) in order to inspect the promise for status. Please consider reading more about [synchronous inspection](.) ## Error: cannot get rejection reason of a non-rejected promise You can get this error when you're trying to call `.value` or `.error` when inspecting a promise where the promise has not been fulfilled or rejected yet. For example: ```js var p = Promise.delay(1000); p.inspect().value(); ``` Consider using [.isPending()](.) [.isFulfilled()](.) and [.isRejected()](.) in order to inspect the promise for status. Please consider reading more about [synchronous inspection](.) ##Error: the target of promisifyAll must be an object or a function This can happen when you are calling [Promise.promisifyAll](.) on a function and invoking it instead of passing it. In general, the usage of [Promise.promisifyAll](.) is along the lines of `var fs = Promise.promisifyAll(require("fs"))`. Consider reading the section about [promisification](.) ## Error: circular promise resolution chain This usually happens when you have a promise that resolves or rejects with itself. For example: `var p = Promise.delay(100).then(function(){ return p});` . In this case, the promise resolves with itself which was is not intended. This also happens when implementing live-updating models with a `.then` method that indicates when the model is "ready". A promise is a process, it starts and it ends. Promises do not aim to solve such live updating problems directly. One option would be to use an intermediate promise - for example a `.loaded` property on the model that fulfills with nothing. resolving it with itself tells it "it is done when it is done" ## Error: cannot await properties of a non-object The `.props` method expects to receive an object. For example: ```js Promise.props({ pictures: getPictures(), comments: getComments(), tweets: getTweets() }).then(function(result){ console.log(result.tweets, result.pictures, result.comments); }); ``` This happens when a non object value or a promise that resolves with something that is not an object is being passed instead. ## Error: expecting a positive integer This happens when you call `.some` passing it a negative value or a non-integer. One possible cause is using `.indexOf` which returns `-1` when it doesn't find the value being searched for. Please consider reading the API docs for [`.some`](.) ## Error: A value was yielded that could not be treated as a promise You are getting this error because you have tried to `yield` something in a coroutine without a yield handler, for example: ```js var coroutine = Promise.coroutine(function*(){ var bar = yield "Foo"; console.log(bar); }); ``` The solution is to either convert it to a promise by calling `Promise.resolve` on it or `Promise.promisify` if it's a callback: ```js var coroutine = Promise.coroutine(function*(){ var bar = yield Promise.resolve("Foo"); console.log(bar); }); ``` Or to use [Promise.coroutine.addYieldHandler`](.) to teach [Promise.coroutine](.) to accept these sort of values. ## Error: cannot await properties of a non object The `.props` method expects to receive an object. For example: ```js Promise.props({ pictures: getPictures(), comments: getComments(), tweets: getTweets() }).then(function(result){ console.log(result.tweets, result.pictures, result.comments); }); ``` This happens when a non object value or a promise that resolves with something that is not an object is being passed instead. ## Error: Cannot promisify an API that has normal methods This error indicates you have tried to call [Promise.promisifyAll](.) on an object that already has a property with the `Async` suffix: ```js var myApi = { foo: function(cb){ ... }, fooAsync(cb) { ... } ``` This is because Bluebird adds the `Async` suffix to distinguish the original method from the promisified one, so `fooAsync` would have been overridden. In order to avoid this - either rename `fooAsync` before promisifying the API, or call [Promise.promisify](.) manually on select properties. You may also use the custom suffix option to choose another suffix that doesn't result in conflicts. If you find this issue in a common library please [open an issue](https://github.com/petkaantonov/bluebird/issues/new). ## Error: Catch filter must inherit from Error or be a simple predicate function Bluebird supports typed and predicate [.catch()](.) calls]. However in order to use the typed/predicate catch syntax for error handling you must do one of two things. Pass it a constructor that inherits from `Error`: }).catch(ReferenceError, function(e) { // this is fine }).catch(Array, function(e) { // arrays don't capture stack traces This is to enable better stack trace support and to have more consistent and logical code. Alternatively, if you provide it a predicate be sure it's a simple function: }).catch(function(e){ return false; }, function(e) { // this catches nothing }).catch(function(e){ return e.someProp = 5; }, function(e) { // this is fine Please see the API docs of [.catch()](.) on how to use predicate catches. ## Error: No async scheduler available Async scheduler is a function that takes a callback function and calls the callback function as soon as possible, but asynchronously. For example `setTimeout`. By default bluebird only tries a few common async schedulers, such as `setTimeout`, `process.nextTick` and `MutationObserver`. However if your JavaScript runtime environment doesn't expose any of these, you will see this error. You may use [Promise.setScheduler](.) to pass a custom scheduler that your environment supports. For example in DukTape: ```js Promise.setScheduler(function(fn){ // fn is what to execute var timer = uv.new_timer.call({}); uv.timer_start(timer, 0, 0, fn); // add the function as a callback to the timer }); ``` ================================================ FILE: docs/docs/features.md ================================================ --- id: features title: Features --- [features](unfinished-article) - [Synchronous inspection](#synchronous-inspection) - [Concurrency coordination](#concurrency-coordination) - [Promisification on steroids](#promisification-on-steroids) - [Debuggability and error handling](#debuggability-and-error-handling) - [Resource management](#resource-management) - [Cancellation and timeouts](#cancellation-and-timeouts) - [Scoped prototypes](#scoped-prototypes) - [Promise monitoring](#promise-monitoring) - [Async/Await](#async-await) ##Synchronous inspection Synchronous inspection allows you to retrieve the fulfillment value of an already fulfilled promise or the rejection reason of an already rejected promise synchronously. Often it is known in certain code paths that a promise is guaranteed to be fulfilled at that point - it would then be extremely inconvenient to use [`.then`](.) to get at the promise's value as the callback is always called asynchronously. See the API on [synchronous inspection](.) for more information. ##Concurrency coordination Through the use of [.each](.) and [.map](.) doing things just at the right concurrency level becomes a breeze. ##Promisification on steroids Promisification means converting an existing promise-unaware API to a promise-returning API. The usual way to use promises in node is to [Promise.promisifyAll](.) some API and start exclusively calling promise returning versions of the APIs methods. E.g. ```js var fs = require("fs"); Promise.promisifyAll(fs); // Now you can use fs as if it was designed to use bluebird promises from the beginning fs.readFileAsync("file.js", "utf8").then(...) ``` Note that the above is an exceptional case because `fs` is a singleton instance. Most libraries can be promisified by requiring the library's classes (constructor functions) and calling promisifyAll on the `.prototype`. This only needs to be done once in the entire application's lifetime and after that you may use the library's methods exactly as they are documented, except by appending the `"Async"`-suffix to method calls and using the promise interface instead of the callback interface. As a notable exception in `fs`, `fs.existsAsync` doesn't work as expected, because Node's `fs.exists` doesn't call back with error as first argument. More at [#418](.). One possible workaround is using `fs.statAsync`. Some examples of the above practice applied to some popular libraries: ```js // The most popular redis module var Promise = require("bluebird"); Promise.promisifyAll(require("redis")); ``` ```js // The most popular mongodb module var Promise = require("bluebird"); Promise.promisifyAll(require("mongodb")); ``` ```js // The most popular mysql module var Promise = require("bluebird"); // Note that the library's classes are not properties of the main export // so we require and promisifyAll them manually Promise.promisifyAll(require("mysql/lib/Connection").prototype); Promise.promisifyAll(require("mysql/lib/Pool").prototype); ``` ```js // Mongoose var Promise = require("bluebird"); Promise.promisifyAll(require("mongoose")); ``` ```js // Request var Promise = require("bluebird"); Promise.promisifyAll(require("request")); // Use request.getAsync(...) not request(..), it will not return a promise ``` ```js // mkdir var Promise = require("bluebird"); Promise.promisifyAll(require("mkdirp")); // Use mkdirp.mkdirpAsync not mkdirp(..), it will not return a promise ``` ```js // winston var Promise = require("bluebird"); Promise.promisifyAll(require("winston")); ``` ```js // rimraf var Promise = require("bluebird"); // The module isn't promisified but the function returned is var rimrafAsync = Promise.promisify(require("rimraf")); ``` ```js // xml2js var Promise = require("bluebird"); Promise.promisifyAll(require("xml2js")); ``` ```js // jsdom var Promise = require("bluebird"); Promise.promisifyAll(require("jsdom")); ``` ```js // fs-extra var Promise = require("bluebird"); Promise.promisifyAll(require("fs-extra")); ``` ```js // prompt var Promise = require("bluebird"); Promise.promisifyAll(require("prompt")); ``` ```js // Nodemailer var Promise = require("bluebird"); Promise.promisifyAll(require("nodemailer")); ``` ```js // ncp var Promise = require("bluebird"); Promise.promisifyAll(require("ncp")); ``` ```js // pg var Promise = require("bluebird"); Promise.promisifyAll(require("pg")); ``` In all of the above cases the library made its classes available in one way or another. If this is not the case, you can still promisify by creating a throwaway instance: ```js var ParanoidLib = require("..."); var throwAwayInstance = ParanoidLib.createInstance(); Promise.promisifyAll(Object.getPrototypeOf(throwAwayInstance)); // Like before, from this point on, all new instances + even the throwAwayInstance suddenly support promises ``` See also [`Promise.promisifyAll`](.). ##Debuggability and error handling - [Surfacing unhandled errors](#surfacing-unhandled-errors) - [Long stack traces](#long-stack-traces) - [Error pattern matching](#error-pattern-matching) - [Warnings](#warnings) ###Surfacing unhandled errors The default approach of bluebird is to immediately log the stack trace when there is an unhandled rejection. This is similar to how uncaught exceptions cause the stack trace to be logged so that you have something to work with when something is not working as expected. However because it is possible to handle a rejected promise at any time in the indeterminate future, some programming patterns will result in false positives. Because such programming patterns are not necessary and can always be refactored to never cause false positives, we recommend doing that to keep debugging as easy as possible . You may however feel differently so bluebird provides hooks to implement more complex failure policies. Such policies could include: - Logging after the promise became GCd (requires a native node.js module) - Showing a live list of rejected promises - Using no hooks and using [`.done`](.) to manually to mark end points where rejections will not be handled - Swallowing all errors (challenge your debugging skills) - ... See [global rejection events](http://bluebirdjs.com/docs/api/error-management-configuration.html#global-rejection-events) to learn more about the hooks. ###Long stack traces Normally stack traces don't go beyond asynchronous boundaries so their utility is greatly reduced in asynchronous code: ```js setTimeout(function() { setTimeout(function() { setTimeout(function() { a.b.c; }, 1); }, 1) }, 1) ``` ``` ReferenceError: a is not defined at null._onTimeout file.js:4:13 at Timer.listOnTimeout (timers.js:90:15) ``` Of course you could use hacks like monkey patching or domains but these break down when something can't be monkey patched or new apis are introduced. Since in bluebird [promisification](.) is made trivial, you can get long stack traces all the time: ```js var Promise = require("bluebird"); Promise.delay(1) .delay(1) .delay(1).then(function() { a.b.c; }); ``` ``` Unhandled rejection ReferenceError: a is not defined at file.js:6:9 at processImmediate [as _immediateCallback] (timers.js:321:17) From previous event: at Object. (file.js:5:15) at Module._compile (module.js:446:26) at Object.Module._extensions..js (module.js:464:10) at Module.load (module.js:341:32) at Function.Module._load (module.js:296:12) at Function.Module.runMain (module.js:487:10) at startup (node.js:111:16) at node.js:799:3 ``` And there is more. Bluebird's long stack traces additionally eliminate cycles, don't leak memory, are not limited to a certain amount of asynchronous boundaries and are fast enough for most applications to be used in production. All these are non-trivial problems that haunt straight-forward long stack trace implementations. See [installation](install.html) on how to enable long stack traces in your environment. ###Error pattern matching Perhaps the greatest thing about promises is that it unifies all error handling into one mechanism where errors propagate automatically and have to be explicitly ignored. ###Warnings Promises can have a steep learning curve and it doesn't help that promise standards go out of their way to make it even harder. Bluebird works around the limitations by providing warnings where the standards disallow throwing errors when incorrect usage is detected. See [Warning Explanations](warning-explanations.html) for the possible warnings that bluebird covers. See [installation](install.html) on how to enable warnings in your environment. Note - in order to get full stack traces with warnings in Node 6.x+ you need to enable to `--trace-warnings` flag which will give you a full stack trace of where the warning is coming from. ###Promise monitoring This feature enables subscription to promise lifecycle events via standard global events mechanisms in browsers and Node.js. The following lifecycle events are available: - `"promiseCreated"` - Fired when a promise is created through the constructor. - `"promiseChained"` - Fired when a promise is created through chaining (e.g. [.then](.)). - `"promiseFulfilled"` - Fired when a promise is fulfilled. - `"promiseRejected"` - Fired when a promise is rejected. - `"promiseResolved"` - Fired when a promise adopts another's state. - `"promiseCancelled"` - Fired when a promise is cancelled. This feature has to be explicitly enabled by calling [Promise.config](.) with `monitoring: true`. The actual subscription API depends on the environment. 1\. In Node.js, use `process.on`: ```js // Note the event name is in camelCase, as per Node.js convention. process.on("promiseChained", function(promise, child) { // promise - The parent promise the child was chained from // child - The created child promise. }); ``` 2\. In modern browsers use `window.addEventListener` (window context) or `self.addEventListener()` (web worker or window context) method: ```js // Note the event names are in mashedtogetherlowercase, as per DOM convention. self.addEventListener("promisechained", function(event) { // event.details.promise - The parent promise the child was chained from // event.details.child - The created child promise. }); ``` 3\. In legacy browsers use `window.oneventname = handlerFunction;`. ```js // Note the event names are in mashedtogetherlowercase, as per legacy convention. window.onpromisechained = function(promise, child) { // event.details.promise - The parent promise the child was chained from // event.details.child - The created child promise. }; ``` ##Resource management ##Cancellation and timeouts See [`Cancellation`](.) for how to use cancellation. ```js // Enable cancellation Promise.config({cancellation: true}); var fs = Promise.promisifyAll(require("fs")); // In 2000ms or less, load & parse a file 'config.json' var p = Promise.resolve('./config.json') .timeout(2000) .catch(console.error.bind(console, 'Failed to load config!')) .then(fs.readFileAsync) .then(JSON.parse); // Listen for exception event to trigger promise cancellation process.on('unhandledException', function(event) { // cancel config loading p.cancel(); }); ``` ##Scoped prototypes Building a library that depends on bluebird? You should know about the "scoped prototype" feature. If your library needs to do something obtrusive like adding or modifying methods on the `Promise` prototype, uses long stack traces or uses a custom unhandled rejection handler then... that's totally ok as long as you don't use `require("bluebird")`. Instead you should create a file that creates an isolated copy. For example, creating a file called `bluebird-extended.js` that contains: ```js //NOTE the function call right after module.exports = require("bluebird/js/main/promise")(); ``` Your library can then use `var Promise = require("bluebird-extended");` and do whatever it wants with it. Then if the application or other library uses their own bluebird promises they will all play well together because of Promises/A+ thenable assimilation magic. ##Async/Await ================================================ FILE: docs/docs/getting-started.md ================================================ --- id: getting-started title: Getting Started redirect_from: "/index.html" redirect_from: "/docs/index.html" --- [getting-started](unfinished-article) ## Node.js npm install bluebird Then: ```js var Promise = require("bluebird"); ``` Alternatively in ES6 ```js import * as Promise from "bluebird"; ``` If that ES6 import [doesn't work](https://github.com/petkaantonov/bluebird/pull/1594) ```js import {Promise} from "bluebird"; ``` ## Browsers (See also [Installation](install.html).) There are many ways to use bluebird in browsers: - Direct downloads - Full build [bluebird.js](https://cdn.jsdelivr.net/bluebird/latest/bluebird.js) - Full build minified [bluebird.min.js](https://cdn.jsdelivr.net/bluebird/latest/bluebird.min.js) - Core build [bluebird.core.js](https://cdn.jsdelivr.net/bluebird/latest/bluebird.core.js) - Core build minified [bluebird.core.min.js](https://cdn.jsdelivr.net/bluebird/latest/bluebird.core.min.js) - You may use browserify on the main export - You may use the [bower](http://bower.io) package. When using script tags the global variables `Promise` and `P` (alias for `Promise`) become available. Bluebird runs on a wide variety of browsers including older versions. We'd like to thank BrowserStack for giving us a free account which helps us test that. ================================================ FILE: docs/docs/install.md ================================================ --- id: install title: Installation --- - [Browser installation](#browser-installation) - [Node installation](#node-installation) - [Supported platforms](#supported-platforms) ##Browser installation Download bluebird {{ site.version }} (development) Unminified source file meant to be used in development. Warnings and long stack traces are enabled which are taxing on performance. ```html ``` Download bluebird {{ site.version }} (production) Minified source file meant to be used in production. Warnings and long straces are disabled. The gzipped size is 17.76KB. ```html ``` Unless an AMD loader is installed, the script tag installation exposes the library in the `Promise` and `P` namespaces. If you want to restore the `Promise` namespace, use `var Bluebird = Promise.noConflict()`. ###Bower ``` $ bower install --save bluebird ``` ###Browserify and Webpack ``` $ npm install bluebird ``` Using webpack for development/debugging: ```js var Promise = require("bluebird"); // Configure webpack and browserify for development/debugging Promise.config({ longStackTraces: true, warnings: true // note, run node with --trace-warnings to see full stack traces for warnings }) ``` Using webpack for production/performance: ```js var Promise = require("bluebird"); // Configure webpack and browserify for production/performance Promise.config({ longStackTraces: false, warnings: false }) ``` ##Node installation ``` $ npm install bluebird ``` ```js var Promise = require("bluebird"); ``` To enable long stack traces and warnings in node development: ``` $ NODE_ENV=development node server.js ``` To enable long stack traces and warnings in node production: ``` $ BLUEBIRD_DEBUG=1 node server.js ``` See [Environment Variables](.). ##Supported platforms Bluebird officially supports and is tested on node.js, iojs and browsers starting from IE7. Unofficial platforms are supported with best effort only. IE7 and IE8 do not support using keywords as property names, so if supporting these browsers is required you need to use the compatibility aliases: - [`Promise.try()`](.) -> `Promise.attempt()` - [`.catch()`](.) -> `.caught()` - [`.finally()`](.) -> `.lastly()` - [`.return()`](.) -> `.thenReturn()` - [`.throw()`](.) -> `.thenThrow()` Long stack traces are only supported in Chrome, recent Firefoxes and Internet Explorer 10+ [![Selenium Test Status](https://saucelabs.com/browser-matrix/petka_antonov.svg)](https://saucelabs.com/u/petka_antonov) ================================================ FILE: docs/docs/new-in-bluebird-3.md ================================================ --- id: new-in-bluebird-3 title: New in bluebird 3.0 --- ##Cancellation overhaul Cancellation has been redesigned for bluebird 3.0. Any code that relies on 2.x cancellation semantics won't work in 3.0 or later. See [Cancellation](.) for more information. ##Promisification API changes Both promisification \([Promise.promisify](.) and [Promise.promisifyAll](.)\) methods and [Promise.fromCallback](.) now by default ignore multiple arguments passed to the callback adapter and instead only the first argument is used to resolve the promise. The behavior in 2.x is to construct an array of the arguments and resolve the promise with it when more than one argument is passed to the callback adapter. The problems with this approach and reasons for the change are discussed in [#307](.). [Promise.promisify](.)'s second argument is now an options object, so any code using the second argument needs to change: ```js // 2.x Promise.promisify(fn, ctx); // 3.0 Promise.promisify(fn, {context: ctx}); ``` Both promisification \([Promise.promisify](.) and [Promise.promisifyAll](.)\) methods and [Promise.fromCallback](.) all take a new boolean option `multiArgs` which defaults to `false`. Enabling this option will make the adapter callback *always* construct an array of the passed arguments regardless of amount of arguments. This can be used to reliably get access to all arguments rather than just the first one. ##Collection method changes All collection methods now support objects that implement [ES6's *iterable*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) protocol along with regular arrays. [Promise.props](.) and [.props](.) now support [ES6 `Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) objects along with normal objects. Actual `Map` objects are only considered for their entries in the map instead of both entries and properties. ##Warnings Warnings have been added to report usages which are very likely to be programmer errors. See [Promise.config](.) for how to enable warnings. See [Warning Explanations](warning-explanations.html) for list of the warnings and their explanations. ##Feature additions - [.catch\(\)](.) now supports an object predicate as a filter: `.catch({code: 'ENOENT'}, e => ...)`. - Added [.suppressUnhandledRejections\(\)](.). - Added [.catchThrow\(\)](.). - Added [.catchReturn\(\)](.). - Added [Promise.mapSeries\(\)](.) and [.mapSeries\(\)](.) ##Deprecations - `Promise.settle` has been deprecated. Use [.reflect](.) instead. - `Promise.spawn` has been deprecated. Use [Promise.coroutine](.) instead. - [Promise.try](.)'s `ctx` and `arg` arguments have been deprecated. - `.nodeify` is now better known as [.asCallback](.) - `.fromNode` is now better known as [Promise.fromCallback](.) ##Summary of breaking changes - Promisifier APIs. - Cancellation redesign. - Promise progression has been completely removed. - [.spread](.)'s second argument has been removed. - [.done](.) causes an irrecoverable fatal error in Node.js environments now. See [#471](.) for rationale. - Errors created with [Promise.reject](.) or `reject` callback of [new Promise](.) are no longer marked as [OperationalError](.)s. ##3.0.1 update Note that the 3.0.1 update is strictly speaking backward-incompatible with 3.0.0. Version 3.0.0 changed the previous behavior of the `.each` method and made it work the same as the new `.mapSeries` - 3.0.1 unrolls this change by reverting to the `.tap`-like behavior found in 2.x However, this would only affect users who updated to 3.0.0 during the short time that it wasn't deprecated and started relying on the new `.each` behavior. This seems unlikely, and therefore the major version was not changed. ================================================ FILE: docs/docs/support.md ================================================ --- id: support title: Support --- Depending on the nature of your problem there are a few possible ways of getting help with your problem. ###Stack Overflow Stack Overflow has an active community answering questions on both the [bluebird](http://stackoverflow.com/questions/tagged/bluebird) and [promise](http://stackoverflow.com/questions/tagged/promise) tags. If you are unfamiliar with Stack Overflow, do review [the asking guidelines](http://stackoverflow.com/help/asking) before asking a new question there. ###Google Groups mailing list The [bluebird-js@googlegroups.com](https://groups.google.com/forum/#!forum/bluebird-js) mailing list is the best place to ask any question that cannot be easily fit in the stack overflow question format. ###IRC You can usually get fast answers on the freenode.net channels [#bluebird](irc://chat.freenode.net/bluebird) and [#promises](irc://chat.freenode.net/promises). ###Gitter If you are not comfortable using IRC you can also join the [bluebird gitter chat](https://gitter.im/petkaantonov/bluebird) ###Github issue tracker If you feel that your problem is caused by a bug or a missing feature that should be added, you should use the [Github issue tracker](https://github.com/petkaantonov/bluebird/issues/) to report it. However in any other case the issue tracker is not the appropriate channel. ================================================ FILE: docs/docs/warning-explanations.md ================================================ --- id: warning-explanations title: Warning Explanations --- [warning-explanations](unfinished-article) - [Warning: .then() only accepts functions](#warning-then-only-accepts-functions) - [Warning: a promise was rejected with a non-error](#warning-a-promise-was-rejected-with-a-non-error) - [Warning: a promise was created in a handler but was not returned from it](#warning-a-promise-was-created-in-a-handler-but-was-not-returned-from-it) Note - in order to get full stack traces with warnings in Node 6.x+ you need to enable to `--trace-warnings` flag which will give you a full stack trace of where the warning is coming from. ## Warning: .then() only accepts functions If you see this warning your code is probably not doing what you expect it to, the most common reason is passing the *result* of calling a function to [.then()](.) instead of the function *itself*: ```js function processImage(image) { // Code that processes image } getImage().then(processImage()); ``` The above calls the function `processImage()` *immediately* and passes the result to [.then()](.) (which is most likely `undefined` - the default return value when a function doesn't return anything). To fix it, simply pass the function reference to [.then()](.) as is: ```js getImage().then(processImage) ``` *If you are wondering why this is a warning and not a simple TypeError it is because the due to historic reasons Promises/A+ specification requires that incorrect usage is silently ignored.* ## Warning: a promise was rejected with a non-error Due to a historic mistake in JavaScript, the `throw` statement is allowed to be used with any value, not just errors, and Promises/A+ choosing to inherit this mistake, it is possible to reject a promise with a value that is not an error. An error is an object that is a `instanceof Error`. It will at minimum have the properties `.stack` and `.message`, which are an absolute *must* have for any value that is being used in an automatic propagation mechanism, such as exceptions and rejections. This is because errors are usually handled many levels above where they actually originate - the error object must have sufficient metadata about it so that its ultimate handler (possibly many levels above) will have all the information needed for creating a useful high level error report. Since all objects support having properties you might still wonder why exactly does it have to be an error object and not just any object. In addition to supporting properties, an equally important feature necessary for values that are automatically propagated is the stack trace property (`.stack`). A stack trace allows you easily find where an error originated from as it gives the code's call stack - along with line numbers for reference in code files. You should heed this warning because rejecting a promise with a non-error makes debugging extremely hard and costly. Additionally, if you reject with simple primitives such as `undefined` (commonly caused by simply calling `reject()`) you cannot handle errors at all because it's impossible to tell from `undefined` what exactly went wrong. All you can tell the user is that "something went wrong" and lose them forever. ## Warning: a promise was created in a handler but was not returned from it This usually means that you simply forgot a `return` statement somewhere, which will cause a runaway promise that is not connected to any promise chain. For example: ```js getUser().then(function(user) { getUserData(user); }).then(function(userData) { // userData is undefined }); ``` Because the result of `getUserData()` is not returned from the first then handler, it becomes a runaway promise that is not awaited for by the second then. The second [.then()](.) simply gets immediately called with `undefined` (because `undefined` is the default return value when you don't return anything). To fix it, you need to `return` the promise: ```js getUser().then(function(user) { return getUserData(user); }).then(function(userData) { // userData is the user's data }); ```
If you know what you're doing and don't want to silence all warnings, you can create runaway promises without causing this warning by returning e.g. `null`: ```js getUser().then(function(user) { // Perform this in the "background" and don't care about its result at all saveAnalytics(user); // return a non-undefined value to signal that we didn't forget to return return null; }); ``` ================================================ FILE: docs/docs/what-about-generators.md ================================================ --- id: what-about-generators title: What About Generators? --- There is an [excellent article](https://www.promisejs.org/generators/) on promisejs.org detailing how to combine promises with generators to achieve much cleaner code. Instead of the `async` function the article proposes, you can use [Promise.coroutine](.). [what-about-generators](unfinished-article) ================================================ FILE: docs/docs/why-bluebird.md ================================================ --- id: why-bluebird title: Why bluebird? --- There are many third party promise libraries available for JavaScript and even the standard library contains a promise implementation in newer versions of browsers and node/io.js. This page will explore why one might use bluebird promises over other third party or the standard library implementations. For reasons to use promises in general, see the [Why Promises?](why-promises.html) article. ###Bluebird design principles Bluebird is built with the following design principles in mind: - **Pragmatic and not theoretical** - Bluebird will always pick the pragmatic route vs the theoretically elegant one when there is a conflict. The library's API was created based on real-life use cases and after a lot of consideration. - **Fully featured without bloat** - Bluebird provides all the tools and utilities needed to realize a highly expressive and fluent DSL for asynchronous JavaScript without suffering from bloat by avoiding incorporating features that are solely motivated by theoretical arguments, have extremely narrow applicability, or have limited synergy and composability with existing features. - **Easy to debug** - A major consequence of choosing pragmatism over theoretical elegance, a property that is unique to bluebird among promise libraries taken to this extent. - Bluebird ships with the best cross-platform long stack traces out there and a warning system. This helps you recognize common and devastating promise usage mistakes early before they lead to hard to debug code later. - Unhandled errors are not silently swallowed by default but reported along with helpful stack traces where applicable. All of this is of course configurable. - **Zero overhead abstraction** - In server-side applications the performance of a promise implementation matters. Bluebird's server-side performance is measured with highly relevant and realistic end-to-end macro [benchmarks](benchmarks.html), and consistently comes out on top. We understand that if bluebird is as close to a zero cost abstraction as possible, developers won't be tempted to short-circuit and absorb complexity themselves. - **Runs everywhere** - Bluebird runs on pretty much every platform. This makes bluebird ideal for projects who care about providing consistent cross-platform and cross-version experience. It runs on old IE, it has even been known to run on Netscape 7. - **Spec compatible** - Bluebird can work as a drop-in replacement for native promises for an instant performance boost. It passes the Promises/A+ test suite and is fully spec compliant. ================================================ FILE: docs/docs/why-performance.md ================================================ --- id: why-performance title: Why Performance? --- [why-performance](unfinished-article) ================================================ FILE: docs/docs/why-promises.md ================================================ --- id: why-promises title: Why Promises? --- Promises are a concurrency primitive with a proven track record and language integration in most modern programming languages. They have been extensively studied since the 80s and will make your life much easier. You should use promises to turn this: ```js fs.readFile("file.json", function (err, val) { if (err) { console.error("unable to read file"); } else { try { val = JSON.parse(val); console.log(val.success); } catch (e) { console.error("invalid json in file"); } } }); ``` Into this: ```js fs.readFileAsync("file.json").then(JSON.parse).then(function (val) { console.log(val.success); }) .catch(SyntaxError, function (e) { console.error("invalid json in file"); }) .catch(function (e) { console.error("unable to read file"); }); ``` *If you're thinking, "There's no `readFileAsync` method on `fs` that returns a promise!" see [promisification](api/promisification.html)* You might notice that the promise approach looks very similar to using synchronous I/O: ```js try { var val = JSON.parse(fs.readFileSync("file.json")); console.log(val.success); } // Gecko-only syntax; used for illustrative purposes catch (e if e instanceof SyntaxError) { console.error("invalid json in file"); } catch (e) { console.error("unable to read file"); } ``` This is the point—to have something that works like `return` and `throw` in synchronous code. You can also use promises to improve code that was written with callbacks: ```js //Copyright Plato http://stackoverflow.com/a/19385911/995876 //CC BY-SA 2.5 mapSeries(URLs, function (URL, done) { var options = {}; needle.get(URL, options, function (error, response, body) { if (error) { return done(error); } try { var ret = JSON.parse(body); return done(null, ret); } catch (e) { done(e); } }); }, function (err, results) { if (err) { console.log(err); } else { console.log('All Needle requests successful'); // results is a 1 to 1 mapping in order of URLs > needle.body processAndSaveAllInDB(results, function (err) { if (err) { return done(err); } console.log('All Needle requests saved'); done(null); }); } }); ``` This is far more readable when done with promises: ```js Promise.promisifyAll(needle); var options = {}; var current = Promise.resolve(); Promise.map(URLs, function (URL) { current = current.then(function () { return needle.getAsync(URL, options); }); return current; }).map(function (responseAndBody) { return JSON.parse(responseAndBody[1]); }).then(function (results) { return processAndSaveAllInDB(results); }).then(function () { console.log('All Needle requests saved'); }).catch(function (e) { console.log(e); }); ``` Also, promises don't just give you correspondences for synchronous features; they can also be used as limited event emitters or callback aggregators. More reading: - [Promise nuggets](https://promise-nuggets.github.io/) - [Why I am switching to promises](http://spion.github.io/posts/why-i-am-switching-to-promises.html) - [What is the the point of promises](http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/#toc_1) - [Aren't Promises Just Callbacks?](http://stackoverflow.com/questions/22539815/arent-promises-just-callbacks) ================================================ FILE: docs/docs/working-with-callbacks.md ================================================ --- id: working-with-callbacks title: Working with Callbacks --- This page explains how to interface your code with existing callback APIs and libraries you're using. We'll see that making bluebird work with callback APIs is not only easy - it's also fast. We'll cover several subjects. If you want to get the tl;dr what you need is likely the [Working with callback APIs using the Node convention](#working-with-callback-apis-using-the-node-convention) section. First to make sure we're on the same page: Promises have state, they start as pending and can settle to: - __fulfilled__ meaning that the computation completed successfully. - __rejected__ meaning that the computation failed. Promise returning functions _should never throw_, they should always successfully return a promise which is rejected in the case of an error. Throwing from a promise returning function will force you to use both a `} catch { ` _and_ a `.catch`. People using promisified APIs do not expect promises to throw. If you're not sure how async APIs work in JS - please [see this answer](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call/16825593#16825593) first. * [Automatic vs. Manual conversion](#automatic-vs.-manual-conversion) * [Working with callback APIs using the Node convention](#working-with-callback-apis-using-the-node-convention) * [Working with one time events.](#working-with-one-time-events) * [Working with delays](#working-with-delays/setTimeout) * [Working with browser APIs](#working-with-browser-apis) * [Working with databases](#working-with-databases) * [More Common Examples](#more-common-examples) * [Working with any other APIs](#working-with-any-other-apis) There is also [this more general StackOverflow question](http://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises) about conversion of callback APIs to promises. If you find anything missing in this guide however, please do open an issue or pull request. ###Automatic vs. Manual conversion There are two primary methods of converting callback based APIs into promise based ones. You can either manually map the API calls to promise returning functions or you can let the bluebird do it for you. We **strongly** recommend the latter. Promises provide a lot of really cool and powerful guarantees like throw safety which are hard to provide when manually converting APIs to use promises. Thus, whenever it is possible to use the `Promise.promisify` and `Promise.promisifyAll` methods - we recommend you use them. Not only are they the safest form of conversion - they also use techniques of dynamic recompilation to introduce very little overhead. ###Working with callback APIs using the Node convention In Node/io.js most APIs follow a convention of ['error-first, single-parameter'](https://gist.github.com/CrabDude/10907185) as such: ```js function getStuff(data, callback) { ... } getStuff("dataParam", function(err, data) { if (!err) { } }); ``` This APIs are what most core modules in Node/io use and bluebird comes with a fast and efficient way to convert them to promise based APIs through the `Promise.promisify` and `Promise.promisifyAll` function calls. - [Promise.promisify](.) - converts a _single_ callback taking function into a promise returning function. It does not alter the original function and returns the modified version. - [Promise.promisifyAll](.) - takes an _object_ full of functions and _converts each function_ into the new one with the `Async` suffix (by default). It does not change the original functions but instead adds new ones. > **Note** - please check the linked docs for more parameters and usage examples. Here's an example of `fs.readFile` with or without promises: ```js // callbacks var fs = require("fs"); fs.readFile("name", "utf8", function(err, data) { }); ``` Promises: ```js var fs = Promise.promisifyAll(require("fs")); fs.readFileAsync("name", "utf8").then(function(data) { }); ``` Note the new method is suffixed with `Async`, as in `fs.readFileAsync`. It did not replace the `fs.readFile` function. Single functions can also be promisified for example: ```js var request = Promise.promisify(require("request")); request("foo.bar").then(function(result) { }); ``` > **Note** `Promise.promisify` and `Promise.promisifyAll` use dynamic recompilation for really fast wrappers and thus calling them should be done only once. [Promise.fromCallback](.) exists for cases where this is not possible. ###Working with one time events Sometimes, we want to find out when a single one time event has finished. For example - a stream is done. For this we can use [new Promise](.). Note that this option should be considered only if [automatic conversion](#working-with-callback-apis-using-the-node-convention) isn't possible. Note that promises model a _single value through time_, they only resolve _once_ - so while they're a good fit for a single event, they are not recommended for multiple event APIs. For example, let's say you have a window `onload` event you want to bind to. We can use the promise construction and resolve when the window has loaded as such: ```js // onload example, the promise constructor takes a // 'resolver' function that tells the promise when // to resolve and fire off its `then` handlers. var loaded = new Promise(function(resolve, reject) { window.addEventListener("load", resolve); }); loaded.then(function() { // window is loaded here }); ``` Here is another example with an API that lets us know when a connection is ready. The attempt here is imperfect and we'll describe why soon: ```js function connect() { var connection = myConnector.getConnection(); // Synchronous. return new Promise(function(resolve, reject) { connection.on("ready", function() { // When a connection has been established // mark the promise as fulfilled. resolve(connection); }); connection.on("error", function(e) { // If it failed connecting, mark it // as rejected. reject(e); // e is preferably an `Error`. }); }); } ``` The problem with the above is that `getConnection` itself might throw for some reason and if it does we'll get a synchronous rejection. An asynchronous operation should always be asynchronous to prevent double guarding and race conditions so it's best to always put the sync parts inside the promise constructor as such: ```js function connect() { return new Promise(function(resolve, reject) { // If getConnection throws here instead of getting // an exception we're getting a rejection thus // producing a much more consistent API. var connection = myConnector.getConnection(); connection.on("ready", function() { // When a connection has been established // mark the promise as fulfilled. resolve(connection); }); connection.on("error", function(e) { // If it failed connecting, mark it // as rejected. reject(e); // e is preferably an `Error` }); }); } ``` ###Working with delays/setTimeout There is no need to convert timeouts/delays to a bluebird API, bluebird already ships with the [Promise.delay](.) function for this use case. Please consult the [timers](.) section of the docs on usage and examples. ###Working with browser APIs Often browser APIs are nonstandard and automatic promisification will fail for them. If you're running into an API that you can't promisify with [promisify](.) and [promisifyAll](.) - please consult the [working with other APIs section](#working-with-any-other-apis) ###Working with databases For resource management in general and databases in particular, bluebird includes the powerful [Promise.using](.) and disposers system. This is similar to `with` in Python, `using` in C#, try/resource in Java or RAII in C++ in that it lets you handle resource management in an automatic way. Several examples of databases follow. > **Note** for more examples please see the [Promise.using](.) section. ####Mongoose/MongoDB Mongoose works with persistent connections and the driver takes care of reconnections/disposals. For this reason using `using` with it isn't required - instead connect on server startup and use promisification to expose promises. Note that Mongoose already ships with promise support but the promises it offers are significantly slower and don't report unhandled rejections so it is recommended to use automatic promisification with it anyway: ```js var Mongoose = Promise.promisifyAll(require("mongoose")); ``` ####Sequelize Sequelize already uses Bluebird promises internally and has promise returning APIs. Use those. ####RethinkDB Rethink already uses Bluebird promises internally and has promise returning APIs. Use those. ####Bookshelf Bookshelf already uses Bluebird promises internally and has promise returning APIs. Use those. ####PostgreSQL Here is how to create a disposer for the PostgreSQL driver: ```js var pg = require("pg"); // Uncomment if pg has not been properly promisified yet. //var Promise = require("bluebird"); //Promise.promisifyAll(pg, { // filter: function(methodName) { // return methodName === "connect" // }, // multiArgs: true //}); // Promisify rest of pg normally. //Promise.promisifyAll(pg); function getSqlConnection(connectionString) { var close; return pg.connectAsync(connectionString).spread(function(client, done) { close = done; return client; }).disposer(function() { if (close) close(); }); } module.exports = getSqlConnection; ``` Which would allow you to use: ```js var using = Promise.using; using(getSqlConnection(), function(conn) { // use connection here and _return the promise_ }).then(function(result) { // connection already disposed here }); ``` It's also possible to use a disposer pattern (but not actual disposers) for transaction management: ```js function withTransaction(fn) { return Promise.using(pool.acquireConnection(), function(connection) { var tx = connection.beginTransaction() return Promise .try(fn, tx) .then(function(res) { return connection.commit().thenReturn(res) }, function(err) { return connection.rollback() .catch(function(e) {/* maybe add the rollback error to err */}) .thenThrow(err); }); }); } exports.withTransaction = withTransaction; ``` Which would let you do: ```js withTransaction(tx => { return tx.queryAsync(...).then(function() { return tx.queryAsync(...) }).then(function() { return tx.queryAsync(...) }); }); ``` ####MySQL Here is how to create a disposer for the MySQL driver: ```js var mysql = require("mysql"); // Uncomment if mysql has not been properly promisified yet // var Promise = require("bluebird"); // Promise.promisifyAll(mysql); // Promise.promisifyAll(require("mysql/lib/Connection").prototype); // Promise.promisifyAll(require("mysql/lib/Pool").prototype); var pool = mysql.createPool({ connectionLimit: 10, host: 'example.org', user: 'bob', password: 'secret' }); function getSqlConnection() { return pool.getConnectionAsync().disposer(function(connection) { connection.release(); }); } module.exports = getSqlConnection; ``` The usage pattern is similar to the PostgreSQL example above. You can also use a disposer pattern (but not an actual .disposer). See the PostgreSQL example above for instructions. ###More common examples Some examples of the above practice applied to some popular libraries: ```js // The most popular redis module var Promise = require("bluebird"); Promise.promisifyAll(require("redis")); ``` ```js // The most popular mongodb module var Promise = require("bluebird"); Promise.promisifyAll(require("mongodb")); ``` ```js // The most popular mysql module var Promise = require("bluebird"); // Note that the library's classes are not properties of the main export // so we require and promisifyAll them manually Promise.promisifyAll(require("mysql/lib/Connection").prototype); Promise.promisifyAll(require("mysql/lib/Pool").prototype); ``` ```js // Mongoose var Promise = require("bluebird"); Promise.promisifyAll(require("mongoose")); ``` ```js // Request var Promise = require("bluebird"); Promise.promisifyAll(require("request")); // Use request.getAsync(...) not request(..), it will not return a promise ``` ```js // mkdir var Promise = require("bluebird"); Promise.promisifyAll(require("mkdirp")); // Use mkdirp.mkdirpAsync not mkdirp(..), it will not return a promise ``` ```js // winston var Promise = require("bluebird"); Promise.promisifyAll(require("winston")); ``` ```js // rimraf var Promise = require("bluebird"); // The module isn't promisified but the function returned is var rimrafAsync = Promise.promisify(require("rimraf")); ``` ```js // xml2js var Promise = require("bluebird"); Promise.promisifyAll(require("xml2js")); ``` ```js // jsdom var Promise = require("bluebird"); Promise.promisifyAll(require("jsdom")); ``` ```js // fs-extra var Promise = require("bluebird"); Promise.promisifyAll(require("fs-extra")); ``` ```js // prompt var Promise = require("bluebird"); Promise.promisifyAll(require("prompt")); ``` ```js // Nodemailer var Promise = require("bluebird"); Promise.promisifyAll(require("nodemailer")); ``` ```js // ncp var Promise = require("bluebird"); Promise.promisifyAll(require("ncp")); ``` ```js // pg var Promise = require("bluebird"); Promise.promisifyAll(require("pg")); ``` In all of the above cases the library made its classes available in one way or another. If this is not the case, you can still promisify by creating a throwaway instance: ```js var ParanoidLib = require("..."); var throwAwayInstance = ParanoidLib.createInstance(); Promise.promisifyAll(Object.getPrototypeOf(throwAwayInstance)); // Like before, from this point on, all new instances + even the throwAwayInstance suddenly support promises ``` ###Working with any other APIs Sometimes you have to work with APIs that are inconsistent and do not follow a common convention. > **Note** Promise returning function should never throw For example, something like: ```js function getUserData(userId, onLoad, onFail) { ... ``` We can use the promise constructor to convert it to a promise returning function: ```js function getUserDataAsync(userId) { return new Promise(function(resolve, reject) { // Put all your code here, this section is throw-safe. getUserData(userId, resolve, reject); }); } ``` ================================================ FILE: docs/helpers.rb ================================================ require "sanitize" class Helpers def self.clean(input) return Sanitize.fragment(input) .downcase .gsub(/\s+/, "-") .gsub(/^([A-Za-z0-9\-_.:]+).*?$/, "\\1") .gsub(/^\./, "") .gsub(/\.$/, "") .gsub(/[^A-Za-z0-9\-_.]/, "") .sub(/^-+/, "") .sub(/-+$/, "") end end ================================================ FILE: docs/img/README.txt ================================================ SVG available upon request. ================================================ FILE: docs/index.html ================================================ Redirecting...

Redirecting...

Click here if you are not redirected. ================================================ FILE: issue_template.md ================================================ (This issue tracker is only for bug reports or feature requests, if this is neither, please choose appropriate channel from http://bluebirdjs.com/docs/support.html) Please answer the questions the best you can: 1) What version of bluebird is the issue happening on? 2) What platform and version? (For example Node.js 0.12 or Google Chrome 32) 3) Did this issue happen with earlier version of bluebird? (Write description of your issue here, stack traces from errors and code that reproduces the issue are helpful) ================================================ FILE: package.json ================================================ { "name": "bluebird", "description": "Full featured Promises/A+ implementation with exceptionally good performance", "version": "3.7.2", "keywords": [ "promise", "performance", "promises", "promises-a", "promises-aplus", "async", "await", "deferred", "deferreds", "future", "flow control", "dsl", "fluent interface" ], "scripts": { "lint": "node scripts/jshint.js", "test": "node --expose-gc tools/test.js", "istanbul": "istanbul", "prepublish": "npm run generate-browser-core && npm run generate-browser-full", "generate-browser-full": "node tools/build.js --no-clean --no-debug --release --browser --minify", "generate-browser-core": "node tools/build.js --features=core --no-debug --release --zalgo --browser --minify && mv js/browser/bluebird.js js/browser/bluebird.core.js && mv js/browser/bluebird.min.js js/browser/bluebird.core.min.js" }, "homepage": "https://github.com/petkaantonov/bluebird", "repository": { "type": "git", "url": "git://github.com/petkaantonov/bluebird.git" }, "bugs": { "url": "http://github.com/petkaantonov/bluebird/issues" }, "license": "MIT", "author": { "name": "Petka Antonov", "email": "petka_antonov@hotmail.com", "url": "http://github.com/petkaantonov/" }, "devDependencies": { "acorn": "^6.0.2", "acorn-walk": "^6.1.0", "baconjs": "^0.7.43", "bluebird": "^2.9.2", "body-parser": "^1.10.2", "browserify": "^8.1.1", "cli-table": "~0.3.1", "co": "^4.2.0", "cross-spawn": "^0.2.3", "glob": "^4.3.2", "grunt-saucelabs": "~8.6.3", "highland": "^2.3.0", "istanbul": "^0.3.5", "jshint": "^2.6.0", "jshint-stylish": "~0.2.0", "kefir": "^2.4.1", "mkdirp": "~0.5.0", "mocha": "~10.2", "open": "~0.0.5", "optimist": "~0.6.1", "rimraf": "~2.2.6", "rx": "^2.3.25", "serve-static": "^1.7.1", "sinon": "~1.7.3", "uglify-js": "~2.4.16" }, "readmeFilename": "README.md", "main": "./js/release/bluebird.js", "webpack": "./js/release/bluebird.js", "browser": "./js/browser/bluebird.js", "files": [ "js/browser", "js/release", "LICENSE" ] } ================================================ FILE: src/any.js ================================================ "use strict"; module.exports = function(Promise) { var SomePromiseArray = Promise._SomePromiseArray; var ASSERT = require("./assert"); function any(promises) { var ret = new SomePromiseArray(promises); var promise = ret.promise(); ASSERT(promise.isPending()); ASSERT(ret instanceof SomePromiseArray); ret.setHowMany(1); ret.setUnwrap(); ret.init(); return promise; } Promise.any = function (promises) { return any(promises); }; Promise.prototype.any = function () { return any(this); }; }; ================================================ FILE: src/assert.js ================================================ "use strict"; module.exports = (function(){ var AssertionError = (function() { function AssertionError(a) { this.constructor$(a); this.message = a; this.name = "AssertionError"; } AssertionError.prototype = new Error(); AssertionError.prototype.constructor = AssertionError; AssertionError.prototype.constructor$ = Error; return AssertionError; })(); function getParams(args) { var params = []; for (var i = 0; i < args.length; ++i) params.push("arg" + i); return params; } function nativeAssert(callName, args, expect) { try { var params = getParams(args); var constructorArgs = params; constructorArgs.push("return " + callName + "("+ params.join(",") + ");"); var fn = Function.apply(null, constructorArgs); return fn.apply(null, args); } catch (e) { if (!(e instanceof SyntaxError)) { throw e; } else { return expect; } } } return function assert(boolExpr, message) { if (boolExpr === true) return; if (typeof boolExpr === "string" && boolExpr.charAt(0) === "%") { var nativeCallName = boolExpr; INLINE_SLICE(args, arguments, 2); if (nativeAssert(nativeCallName, args, message) === message) return; message = (nativeCallName + " !== " + message); } var ret = new AssertionError(message); if (Error.captureStackTrace) { Error.captureStackTrace(ret, assert); } throw ret; }; })(); ================================================ FILE: src/async.js ================================================ "use strict"; var firstLineError; try {throw new Error(); } catch (e) {firstLineError = e;} var ASSERT = require("./assert"); var schedule = require("./schedule"); var Queue = require("./queue"); function Async() { this._customScheduler = false; this._isTickUsed = false; this._lateQueue = new Queue(LATE_QUEUE_CAPACITY); this._normalQueue = new Queue(NORMAL_QUEUE_CAPACITY); this._haveDrainedQueues = false; var self = this; this.drainQueues = function () { self._drainQueues(); }; this._schedule = schedule; } Async.prototype.setScheduler = function(fn) { var prev = this._schedule; this._schedule = fn; this._customScheduler = true; return prev; }; Async.prototype.hasCustomScheduler = function() { return this._customScheduler; }; Async.prototype.haveItemsQueued = function () { return this._isTickUsed || this._haveDrainedQueues; }; Async.prototype.fatalError = function(e, isNode) { if (isNode) { process.stderr.write("Fatal " + (e instanceof Error ? e.stack : e) + "\n"); process.exit(2); } else { this.throwLater(e); } }; // Must be used if fn can throw Async.prototype.throwLater = function(fn, arg) { if (arguments.length === 1) { arg = fn; fn = function () { throw arg; }; } if (typeof setTimeout !== "undefined") { setTimeout(function() { fn(arg); }, 0); } else try { this._schedule(function() { fn(arg); }); } catch (e) { throw new Error(NO_ASYNC_SCHEDULER); } }; //When the fn absolutely needs to be called after //the queue has been completely flushed function AsyncInvokeLater(fn, receiver, arg) { ASSERT(arguments.length === 3); this._lateQueue.push(fn, receiver, arg); this._queueTick(); } function AsyncInvoke(fn, receiver, arg) { ASSERT(arguments.length === 3); this._normalQueue.push(fn, receiver, arg); this._queueTick(); } function AsyncSettlePromises(promise) { this._normalQueue._pushOne(promise); this._queueTick(); } Async.prototype.invokeLater = AsyncInvokeLater; Async.prototype.invoke = AsyncInvoke; Async.prototype.settlePromises = AsyncSettlePromises; function _drainQueue(queue) { while (queue.length() > 0) { _drainQueueStep(queue); } } // Shift the queue in a separate function to allow // garbage collection after each step function _drainQueueStep(queue) { var fn = queue.shift(); if (typeof fn !== "function") { fn._settlePromises(); } else { var receiver = queue.shift(); var arg = queue.shift(); fn.call(receiver, arg); } } Async.prototype._drainQueues = function () { ASSERT(this._isTickUsed); _drainQueue(this._normalQueue); this._reset(); this._haveDrainedQueues = true; _drainQueue(this._lateQueue); }; Async.prototype._queueTick = function () { if (!this._isTickUsed) { this._isTickUsed = true; this._schedule(this.drainQueues); } }; Async.prototype._reset = function () { this._isTickUsed = false; }; module.exports = Async; module.exports.firstLineError = firstLineError; ================================================ FILE: src/bind.js ================================================ "use strict"; module.exports = function(Promise, INTERNAL, tryConvertToPromise, debug) { var calledBind = false; var rejectThis = function(_, e) { this._reject(e); }; var targetRejected = function(e, context) { context.promiseRejectionQueued = true; context.bindingPromise._then(rejectThis, rejectThis, null, this, e); }; var bindingResolved = function(thisArg, context) { if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG, this._bitField)) { this._resolveCallback(context.target); } }; var bindingRejected = function(e, context) { if (!context.promiseRejectionQueued) this._reject(e); }; Promise.prototype.bind = function (thisArg) { if (!calledBind) { calledBind = true; Promise.prototype._propagateFrom = debug.propagateFromFunction(); Promise.prototype._boundValue = debug.boundValueFunction(); } var maybePromise = tryConvertToPromise(thisArg); var ret = new Promise(INTERNAL); ret._propagateFrom(this, PROPAGATE_CANCEL); var target = this._target(); ret._setBoundTo(maybePromise); if (maybePromise instanceof Promise) { var context = { promiseRejectionQueued: false, promise: ret, target: target, bindingPromise: maybePromise }; target._then(INTERNAL, targetRejected, undefined, ret, context); maybePromise._then( bindingResolved, bindingRejected, undefined, ret, context); ret._setOnCancel(maybePromise); } else { ret._resolveCallback(target); } return ret; }; Promise.prototype._setBoundTo = function (obj) { if (obj !== undefined) { this._bitField = this._bitField | IS_BOUND; this._boundTo = obj; } else { this._bitField = this._bitField & (~IS_BOUND); } }; Promise.prototype._isBound = function () { return (this._bitField & IS_BOUND) === IS_BOUND; }; Promise.bind = function (thisArg, value) { return Promise.resolve(value).bind(thisArg); }; }; ================================================ FILE: src/bluebird.js ================================================ "use strict"; var old; if (typeof Promise !== "undefined") old = Promise; function noConflict() { try { if (Promise === bluebird) Promise = old; } catch (e) {} return bluebird; } var bluebird = require("./promise")(); bluebird.noConflict = noConflict; module.exports = bluebird; ================================================ FILE: src/call_get.js ================================================ "use strict"; var cr = Object.create; if (cr) { var callerCache = cr(null); var getterCache = cr(null); callerCache[" size"] = getterCache[" size"] = 0; } module.exports = function(Promise) { var util = require("./util"); var canEvaluate = util.canEvaluate; var isIdentifier = util.isIdentifier; var getMethodCaller; var getGetter; if (!__BROWSER__) { var makeMethodCaller = function (methodName) { return new Function("ensureMethod", " \n\ return function(obj) { \n\ 'use strict' \n\ var len = this.length; \n\ ensureMethod(obj, 'methodName'); \n\ switch(len) { \n\ case 1: return obj.methodName(this[0]); \n\ case 2: return obj.methodName(this[0], this[1]); \n\ case 3: return obj.methodName(this[0], this[1], this[2]); \n\ case 0: return obj.methodName(); \n\ default: \n\ return obj.methodName.apply(obj, this); \n\ } \n\ }; \n\ ".replace(/methodName/g, methodName))(ensureMethod); }; var makeGetter = function (propertyName) { return new Function("obj", " \n\ 'use strict'; \n\ return obj.propertyName; \n\ ".replace("propertyName", propertyName)); }; var getCompiled = function(name, compiler, cache) { var ret = cache[name]; if (typeof ret !== "function") { if (!isIdentifier(name)) { return null; } ret = compiler(name); cache[name] = ret; cache[" size"]++; if (cache[" size"] > 512) { var keys = Object.keys(cache); for (var i = 0; i < 256; ++i) delete cache[keys[i]]; cache[" size"] = keys.length - 256; } } return ret; }; getMethodCaller = function(name) { return getCompiled(name, makeMethodCaller, callerCache); }; getGetter = function(name) { return getCompiled(name, makeGetter, getterCache); }; } function ensureMethod(obj, methodName) { var fn; if (obj != null) fn = obj[methodName]; if (typeof fn !== "function") { var message = "Object " + util.classString(obj) + " has no method '" + util.toString(methodName) + "'"; throw new Promise.TypeError(message); } return fn; } function caller(obj) { var methodName = this.pop(); var fn = ensureMethod(obj, methodName); return fn.apply(obj, this); } Promise.prototype.call = function (methodName) { INLINE_SLICE(args, arguments, 1); if (!__BROWSER__) { if (canEvaluate) { var maybeCaller = getMethodCaller(methodName); if (maybeCaller !== null) { return this._then( maybeCaller, undefined, undefined, args, undefined); } } } args.push(methodName); return this._then(caller, undefined, undefined, args, undefined); }; function namedGetter(obj) { return obj[this]; } function indexedGetter(obj) { var index = +this; if (index < 0) index = Math.max(0, index + obj.length); return obj[index]; } Promise.prototype.get = function (propertyName) { var isIndex = (typeof propertyName === "number"); var getter; if (!isIndex) { if (canEvaluate) { var maybeGetter = getGetter(propertyName); getter = maybeGetter !== null ? maybeGetter : namedGetter; } else { getter = namedGetter; } } else { getter = indexedGetter; } return this._then(getter, undefined, undefined, propertyName, undefined); }; }; ================================================ FILE: src/cancel.js ================================================ "use strict"; module.exports = function(Promise, PromiseArray, apiRejection, debug) { var ASSERT = require("./assert"); var util = require("./util"); var tryCatch = util.tryCatch; var errorObj = util.errorObj; var async = Promise._async; Promise.prototype["break"] = Promise.prototype.cancel = function() { if (!debug.cancellation()) return this._warn("cancellation is disabled"); var promise = this; var child = promise; while (promise._isCancellable()) { if (!promise._cancelBy(child)) { if (child._isFollowing()) { child._followee().cancel(); } else { child._cancelBranched(); } break; } var parent = promise._cancellationParent; if (parent == null || !parent._isCancellable()) { if (promise._isFollowing()) { promise._followee().cancel(); } else { promise._cancelBranched(); } break; } else { if (promise._isFollowing()) promise._followee().cancel(); promise._setWillBeCancelled(); child = promise; promise = parent; } } }; Promise.prototype._branchHasCancelled = function() { ASSERT(typeof this._branchesRemainingToCancel === "number"); this._branchesRemainingToCancel--; }; Promise.prototype._enoughBranchesHaveCancelled = function() { return this._branchesRemainingToCancel === undefined || this._branchesRemainingToCancel <= 0; }; Promise.prototype._cancelBy = function(canceller) { if (canceller === this) { this._branchesRemainingToCancel = 0; this._invokeOnCancel(); return true; } else { ASSERT(canceller._cancellationParent === this); this._branchHasCancelled(); if (this._enoughBranchesHaveCancelled()) { this._invokeOnCancel(); return true; } } return false; }; Promise.prototype._cancelBranched = function() { if (this._enoughBranchesHaveCancelled()) { this._cancel(); } }; Promise.prototype._cancel = function() { if (!this._isCancellable()) return; ASSERT(!this._isFollowing()); this._setCancelled(); async.invoke(this._cancelPromises, this, undefined); }; Promise.prototype._cancelPromises = function() { if (this._length() > 0) this._settlePromises(); }; Promise.prototype._unsetOnCancel = function() { ASSERT(this._isCancellable() || this._isCancelled()); this._onCancelField = undefined; }; Promise.prototype._isCancellable = function() { return this.isPending() && !this._isCancelled(); }; Promise.prototype.isCancellable = function() { return this.isPending() && !this.isCancelled(); }; Promise.prototype._doInvokeOnCancel = function(onCancelCallback, internalOnly) { if (util.isArray(onCancelCallback)) { for (var i = 0; i < onCancelCallback.length; ++i) { this._doInvokeOnCancel(onCancelCallback[i], internalOnly); } } else if (onCancelCallback !== undefined) { if (typeof onCancelCallback === "function") { if (!internalOnly) { var e = tryCatch(onCancelCallback).call(this._boundValue()); if (e === errorObj) { this._attachExtraTrace(e.e); async.throwLater(e.e); } } } else { onCancelCallback._resultCancelled(this); } } }; Promise.prototype._invokeOnCancel = function() { var onCancelCallback = this._onCancel(); // The existence of onCancel handler on a promise signals that the handler // has not been queued for invocation yet. this._unsetOnCancel(); async.invoke(this._doInvokeOnCancel, this, onCancelCallback); }; Promise.prototype._invokeInternalOnCancel = function() { if (this._isCancellable()) { this._doInvokeOnCancel(this._onCancel(), true); this._unsetOnCancel(); } }; Promise.prototype._resultCancelled = function() { this.cancel(); }; }; ================================================ FILE: src/catch_filter.js ================================================ "use strict"; module.exports = function(NEXT_FILTER) { var util = require("./util"); var getKeys = require("./es5").keys; var tryCatch = util.tryCatch; var errorObj = util.errorObj; function catchFilter(instances, cb, promise) { return function(e) { var boundTo = promise._boundValue(); predicateLoop: for (var i = 0; i < instances.length; ++i) { var item = instances[i]; if (item === Error || (item != null && item.prototype instanceof Error)) { if (e instanceof item) { return tryCatch(cb).call(boundTo, e); } } else if (typeof item === "function") { var matchesPredicate = tryCatch(item).call(boundTo, e); if (matchesPredicate === errorObj) { return matchesPredicate; } else if (matchesPredicate) { return tryCatch(cb).call(boundTo, e); } } else if (util.isObject(e)) { var keys = getKeys(item); for (var j = 0; j < keys.length; ++j) { var key = keys[j]; if (item[key] != e[key]) { continue predicateLoop; } } return tryCatch(cb).call(boundTo, e); } } return NEXT_FILTER; }; } return catchFilter; }; ================================================ FILE: src/constants.js ================================================ //This is pretty lame but what you gonna do //async.js CONSTANT(LATE_QUEUE_CAPACITY, 16); CONSTANT(NORMAL_QUEUE_CAPACITY, 16); //errors.js CONSTANT(ERROR_HANDLED_KEY, "__promiseHandled__"); CONSTANT(OPERATIONAL_ERROR_KEY, "isOperational"); CONSTANT(DEFAULT_STATE, 0); CONSTANT(STACK_ATTACHED, 1); CONSTANT(ERROR_HANDLED, 2); //join.js CONSTANT(GENERATED_CLASS_COUNT, 8); //promise.js CONSTANT(USE_BOUND, true); CONSTANT(DONT_USE_BOUND, false); CONSTANT(PROPAGATE_CANCEL, 1); CONSTANT(PROPAGATE_BIND, 2); CONSTANT(PROPAGATE_ALL, PROPAGATE_CANCEL | PROPAGATE_BIND); CONSTANT(CALLBACK_FULFILL_OFFSET, 0); CONSTANT(CALLBACK_REJECT_OFFSET, 1); CONSTANT(CALLBACK_PROMISE_OFFSET, 2); CONSTANT(CALLBACK_RECEIVER_OFFSET, 3); CONSTANT(CALLBACK_SIZE, 4); //Layout for ._bitField //[RR]XO GWFN CTBH IUDE LLLL LLLL LLLL LLLL //[RR] = [Reserved] (Both bits are either on or off to represent // 1 bit due to 31-bit integers in 32-bit v8) //R = [Reserved] //X = noAsyncGuarantee //O = returnedNonUndefined //G = isAsyncGuaranteed //W = isFollowing (The promise that is being followed is not stored explicitly) //F = isFulfilled //N = isRejected //C = willBeCancelled //T = isFinal (used for .done() implementation) //B = isBound //I = isRejectionIgnored //H = isRejectionUnhandled //U = isUnhanldedRejectionNotified //D = isDisposable //E = isCancelled //L = Length, 16 bit unsigned CONSTANT(ASYNC_GUARANTEE_SHIFT, 2) CONSTANT(NO_STATE, 0x0|0); CONSTANT(NO_ASYNC_GUARANTEE, 0x20000000|0); CONSTANT(RETURNED_NON_UNDEFINED, 0x10000000|0); CONSTANT(IS_ASYNC_GUARANTEED, 0x8000000|0); CONSTANT(IS_FOLLOWING, 0x4000000|0); CONSTANT(IS_FULFILLED, 0x2000000|0); CONSTANT(IS_REJECTED, 0x1000000|0); CONSTANT(WILL_BE_CANCELLED, 0x800000|0); CONSTANT(IS_FINAL, 0x400000|0); CONSTANT(IS_BOUND, 0x200000|0); CONSTANT(IS_REJECTION_UNHANDLED, 0x100000|0); CONSTANT(IS_REJECTION_IGNORED, 0x80000|0); CONSTANT(IS_UNHANDLED_REJECTION_NOTIFIED, 0x40000|0); CONSTANT(IS_DISPOSABLE, 0x20000|0); CONSTANT(IS_CANCELLED, 0x10000|0); CONSTANT(IS_CANCELLED_OR_WILL_BE_CANCELLED, IS_CANCELLED | WILL_BE_CANCELLED) CONSTANT(LENGTH_MASK, 0xFFFF|0); CONSTANT(LENGTH_CLEAR_MASK, ~LENGTH_MASK); CONSTANT(MAX_LENGTH, LENGTH_MASK); CONSTANT(IS_REJECTED_OR_CANCELLED, IS_REJECTED | IS_CANCELLED); CONSTANT(IS_REJECTED_OR_FULFILLED, IS_REJECTED | IS_FULFILLED); CONSTANT(IS_REJECTED_OR_FULFILLED_OR_CANCELLED, IS_REJECTED | IS_FULFILLED| IS_CANCELLED); CONSTANT(IS_PENDING_AND_WAITING_NEG, IS_REJECTED_OR_FULFILLED_OR_CANCELLED); CONSTANT(IS_FATE_SEALED, IS_REJECTED | IS_FULFILLED | IS_FOLLOWING | IS_CANCELLED); CONSTANT(AFTER_PROMISIFIED_SUFFIX, "Async"); CONSTANT(UNHANDLED_REJECTION_EVENT, "unhandledRejection"); CONSTANT(REJECTION_HANDLED_EVENT, "rejectionHandled"); //promise_array.js //MUST BE NEGATIVE NUMBERS CONSTANT(RESOLVE_UNDEFINED, -1); CONSTANT(RESOLVE_ARRAY, -2); CONSTANT(RESOLVE_OBJECT, -3); CONSTANT(RESOLVE_FOREVER_PENDING, -4); CONSTANT(RESOLVE_CALL_METHOD, -5); CONSTANT(RESOLVE_MAP, -6); //queue.js CONSTANT(QUEUE_MAX_CAPACITY, (1 << 30) | 0); CONSTANT(QUEUE_MIN_CAPACITY, 16); //captured_trace.js CONSTANT(FROM_PREVIOUS_EVENT, "From previous event:"); CONSTANT(NO_STACK_TRACE, " (No stack trace)"); CONSTANT(ADDITIONAL_STACK_TRACE, "^--- With additional stack trace: "); CONSTANT(UNHANDLED_REJECTION_HEADER, "Unhandled rejection "); //finally.js CONSTANT(FINALLY_TYPE, 0); CONSTANT(TAP_TYPE, 1); //direct_resolve.js CONSTANT(THROW, 1); CONSTANT(RETURN, 2); //promisify.js CONSTANT(MAX_PARAM_COUNT, 1023); CONSTANT(PARAM_COUNTS_TO_TRY, 3); //error.js CONSTANT(BLUEBIRD_ERRORS, "__BluebirdErrorTypes__"); //deprecated CONSTANT(OBJECT_PROMISIFY_DEPRECATED, "Promise.promisify for promisifying entire objects is deprecated. Use Promise.promisifyAll instead.\n\n\ See http://goo.gl/MqrFmX"); CONSTANT(SPAWN_DEPRECATED, "Promise.spawn is deprecated. Use Promise.coroutine instead."); //errors CONSTANT(LATE_CANCELLATION_OBSERVER, "late cancellation observer"); CONSTANT(TIMEOUT_ERROR, "operation timed out"); CONSTANT(COLLECTION_ERROR, "expecting an array or an iterable object but got "); CONSTANT(OBJECT_ERROR, "expecting an object but got "); CONSTANT(FUNCTION_ERROR, "expecting a function but got "); CONSTANT(CONSTRUCT_ERROR_INVOCATION, "the promise constructor cannot be invoked directly\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(NOT_GENERATOR_ERROR, "generatorFunction must be a function\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(LONG_STACK_TRACES_ERROR, "cannot enable long stack traces after promises have been created\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(INSPECTION_VALUE_ERROR, "cannot get fulfillment value of a non-fulfilled promise\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(INSPECTION_REASON_ERROR, "cannot get rejection reason of a non-rejected promise\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(PROMISIFY_TYPE_ERROR, "the target of promisifyAll must be an object or a function\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(CIRCULAR_RESOLUTION_ERROR, "circular promise resolution chain\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(PROPS_TYPE_ERROR, "cannot await properties of a non-object\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(POSITIVE_INTEGER_ERROR, "expecting a positive integer\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(YIELDED_NON_PROMISE_ERROR, "A value %s was yielded that could not be treated as a promise\n\n\ See http://goo.gl/MqrFmX\n\n"); CONSTANT(FROM_COROUTINE_CREATED_AT, "From coroutine:\n"); CONSTANT(UNBOUND_RESOLVER_INVOCATION, "Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(PROMISIFICATION_NORMAL_METHODS_ERROR, "Cannot promisify an API that has normal methods with '%s'-suffix\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(SUFFIX_NOT_IDENTIFIER, "suffix must be a valid identifier\n\n\ See http://goo.gl/MqrFmX\n"); CONSTANT(NO_ASYNC_SCHEDULER, "No async scheduler available\n\n\ See http://goo.gl/MqrFmX\n"); ================================================ FILE: src/context.js ================================================ "use strict"; module.exports = function(Promise) { var longStackTraces = false; var contextStack = []; Promise.prototype._promiseCreated = function() {}; Promise.prototype._pushContext = function() {}; Promise.prototype._popContext = function() {return null;}; Promise._peekContext = Promise.prototype._peekContext = function() {}; function Context() { this._trace = new Context.CapturedTrace(peekContext()); } Context.prototype._pushContext = function () { if (this._trace !== undefined) { this._trace._promiseCreated = null; contextStack.push(this._trace); } }; Context.prototype._popContext = function () { if (this._trace !== undefined) { var trace = contextStack.pop(); var ret = trace._promiseCreated; trace._promiseCreated = null; return ret; } return null; }; function createContext() { if (longStackTraces) return new Context(); } function peekContext() { var lastIndex = contextStack.length - 1; if (lastIndex >= 0) { return contextStack[lastIndex]; } return undefined; } Context.CapturedTrace = null; Context.create = createContext; Context.deactivateLongStackTraces = function() {}; Context.activateLongStackTraces = function() { var Promise_pushContext = Promise.prototype._pushContext; var Promise_popContext = Promise.prototype._popContext; var Promise_PeekContext = Promise._peekContext; var Promise_peekContext = Promise.prototype._peekContext; var Promise_promiseCreated = Promise.prototype._promiseCreated; Context.deactivateLongStackTraces = function() { Promise.prototype._pushContext = Promise_pushContext; Promise.prototype._popContext = Promise_popContext; Promise._peekContext = Promise_PeekContext; Promise.prototype._peekContext = Promise_peekContext; Promise.prototype._promiseCreated = Promise_promiseCreated; longStackTraces = false; }; longStackTraces = true; Promise.prototype._pushContext = Context.prototype._pushContext; Promise.prototype._popContext = Context.prototype._popContext; Promise._peekContext = Promise.prototype._peekContext = peekContext; Promise.prototype._promiseCreated = function() { var ctx = this._peekContext(); if (ctx && ctx._promiseCreated == null) ctx._promiseCreated = this; }; }; return Context; }; ================================================ FILE: src/debuggability.js ================================================ "use strict"; module.exports = function(Promise, Context, enableAsyncHooks, disableAsyncHooks) { var async = Promise._async; var Warning = require("./errors").Warning; var util = require("./util"); var es5 = require("./es5"); var ASSERT = require("./assert"); var canAttachTrace = util.canAttachTrace; var unhandledRejectionHandled; var possiblyUnhandledRejection; var bluebirdFramePattern = /[\\\/]bluebird[\\\/]js[\\\/](release|debug|instrumented)/; var nodeFramePattern = /\((?:timers\.js):\d+:\d+\)/; var parseLinePattern = /[\/<\(](.+?):(\d+):(\d+)\)?\s*$/; var stackFramePattern = null; var formatStack = null; var indentStackFrames = false; var printWarning; var debugging = !!(util.env("BLUEBIRD_DEBUG") != 0 && (__DEBUG__ || util.env("BLUEBIRD_DEBUG") || util.env("NODE_ENV") === "development")); var warnings = !!(util.env("BLUEBIRD_WARNINGS") != 0 && (debugging || util.env("BLUEBIRD_WARNINGS"))); var longStackTraces = !!(util.env("BLUEBIRD_LONG_STACK_TRACES") != 0 && (debugging || util.env("BLUEBIRD_LONG_STACK_TRACES"))); var wForgottenReturn = util.env("BLUEBIRD_W_FORGOTTEN_RETURN") != 0 && (warnings || !!util.env("BLUEBIRD_W_FORGOTTEN_RETURN")); var deferUnhandledRejectionCheck; (function() { var promises = []; function unhandledRejectionCheck() { for (var i = 0; i < promises.length; ++i) { promises[i]._notifyUnhandledRejection(); } unhandledRejectionClear(); } function unhandledRejectionClear() { promises.length = 0; } deferUnhandledRejectionCheck = function(promise) { promises.push(promise); setTimeout(unhandledRejectionCheck, 1); }; es5.defineProperty(Promise, "_unhandledRejectionCheck", { value: unhandledRejectionCheck }); es5.defineProperty(Promise, "_unhandledRejectionClear", { value: unhandledRejectionClear }); })(); Promise.prototype.suppressUnhandledRejections = function() { var target = this._target(); target._bitField = ((target._bitField & (~IS_REJECTION_UNHANDLED)) | IS_REJECTION_IGNORED); }; Promise.prototype._ensurePossibleRejectionHandled = function () { if ((this._bitField & IS_REJECTION_IGNORED) !== 0) return; this._setRejectionIsUnhandled(); deferUnhandledRejectionCheck(this); }; Promise.prototype._notifyUnhandledRejectionIsHandled = function () { fireRejectionEvent(REJECTION_HANDLED_EVENT, unhandledRejectionHandled, undefined, this); }; Promise.prototype._setReturnedNonUndefined = function() { this._bitField = this._bitField | RETURNED_NON_UNDEFINED; }; Promise.prototype._returnedNonUndefined = function() { return (this._bitField & RETURNED_NON_UNDEFINED) !== 0; }; Promise.prototype._notifyUnhandledRejection = function () { if (this._isRejectionUnhandled()) { var reason = this._settledValue(); this._setUnhandledRejectionIsNotified(); fireRejectionEvent(UNHANDLED_REJECTION_EVENT, possiblyUnhandledRejection, reason, this); } }; Promise.prototype._setUnhandledRejectionIsNotified = function () { this._bitField = this._bitField | IS_UNHANDLED_REJECTION_NOTIFIED; }; Promise.prototype._unsetUnhandledRejectionIsNotified = function () { this._bitField = this._bitField & (~IS_UNHANDLED_REJECTION_NOTIFIED); }; Promise.prototype._isUnhandledRejectionNotified = function () { return (this._bitField & IS_UNHANDLED_REJECTION_NOTIFIED) > 0; }; Promise.prototype._setRejectionIsUnhandled = function () { ASSERT(!this._isFollowing()); this._bitField = this._bitField | IS_REJECTION_UNHANDLED; }; Promise.prototype._unsetRejectionIsUnhandled = function () { ASSERT(!this._isFollowing()); this._bitField = this._bitField & (~IS_REJECTION_UNHANDLED); if (this._isUnhandledRejectionNotified()) { this._unsetUnhandledRejectionIsNotified(); this._notifyUnhandledRejectionIsHandled(); } }; Promise.prototype._isRejectionUnhandled = function () { ASSERT(!this._isFollowing()); return (this._bitField & IS_REJECTION_UNHANDLED) > 0; }; Promise.prototype._warn = function(message, shouldUseOwnTrace, promise) { return warn(message, shouldUseOwnTrace, promise || this); }; Promise.onPossiblyUnhandledRejection = function (fn) { var context = Promise._getContext(); possiblyUnhandledRejection = util.contextBind(context, fn); }; Promise.onUnhandledRejectionHandled = function (fn) { var context = Promise._getContext(); unhandledRejectionHandled = util.contextBind(context, fn); }; var disableLongStackTraces = function() {}; Promise.longStackTraces = function () { if (async.haveItemsQueued() && !config.longStackTraces) { throw new Error(LONG_STACK_TRACES_ERROR); } if (!config.longStackTraces && longStackTracesIsSupported()) { var Promise_captureStackTrace = Promise.prototype._captureStackTrace; var Promise_attachExtraTrace = Promise.prototype._attachExtraTrace; var Promise_dereferenceTrace = Promise.prototype._dereferenceTrace; config.longStackTraces = true; disableLongStackTraces = function() { if (async.haveItemsQueued() && !config.longStackTraces) { throw new Error(LONG_STACK_TRACES_ERROR); } Promise.prototype._captureStackTrace = Promise_captureStackTrace; Promise.prototype._attachExtraTrace = Promise_attachExtraTrace; Promise.prototype._dereferenceTrace = Promise_dereferenceTrace; Context.deactivateLongStackTraces(); config.longStackTraces = false; }; Promise.prototype._captureStackTrace = longStackTracesCaptureStackTrace; Promise.prototype._attachExtraTrace = longStackTracesAttachExtraTrace; Promise.prototype._dereferenceTrace = longStackTracesDereferenceTrace; Context.activateLongStackTraces(); } }; Promise.hasLongStackTraces = function () { return config.longStackTraces && longStackTracesIsSupported(); }; var legacyHandlers = { unhandledrejection: { before: function() { var ret = util.global.onunhandledrejection; util.global.onunhandledrejection = null; return ret; }, after: function(fn) { util.global.onunhandledrejection = fn; } }, rejectionhandled: { before: function() { var ret = util.global.onrejectionhandled; util.global.onrejectionhandled = null; return ret; }, after: function(fn) { util.global.onrejectionhandled = fn; } } }; var fireDomEvent = (function() { var dispatch = function(legacy, e) { if (legacy) { var fn; try { fn = legacy.before(); return !util.global.dispatchEvent(e); } finally { legacy.after(fn); } } else { return !util.global.dispatchEvent(e); } }; try { if (typeof CustomEvent === "function") { var event = new CustomEvent("CustomEvent"); util.global.dispatchEvent(event); return function(name, event) { name = name.toLowerCase(); var eventData = { detail: event, cancelable: true }; var domEvent = new CustomEvent(name, eventData); es5.defineProperty( domEvent, "promise", {value: event.promise}); es5.defineProperty( domEvent, "reason", {value: event.reason}); return dispatch(legacyHandlers[name], domEvent); }; // In Firefox < 48 CustomEvent is not available in workers but // Event is. } else if (typeof Event === "function") { var event = new Event("CustomEvent"); util.global.dispatchEvent(event); return function(name, event) { name = name.toLowerCase(); var domEvent = new Event(name, { cancelable: true }); domEvent.detail = event; es5.defineProperty(domEvent, "promise", {value: event.promise}); es5.defineProperty(domEvent, "reason", {value: event.reason}); return dispatch(legacyHandlers[name], domEvent); }; } else { var event = document.createEvent("CustomEvent"); event.initCustomEvent("testingtheevent", false, true, {}); util.global.dispatchEvent(event); return function(name, event) { name = name.toLowerCase(); var domEvent = document.createEvent("CustomEvent"); domEvent.initCustomEvent(name, false, true, event); return dispatch(legacyHandlers[name], domEvent); }; } } catch (e) {} return function() { return false; }; })(); var fireGlobalEvent = (function() { if (util.isNode) { return function() { return process.emit.apply(process, arguments); }; } else { if (!util.global) { return function() { return false; }; } return function(name) { var methodName = "on" + name.toLowerCase(); var method = util.global[methodName]; if (!method) return false; method.apply(util.global, [].slice.call(arguments, 1)); return true; }; } })(); function generatePromiseLifecycleEventObject(name, promise) { return {promise: promise}; } var eventToObjectGenerator = { promiseCreated: generatePromiseLifecycleEventObject, promiseFulfilled: generatePromiseLifecycleEventObject, promiseRejected: generatePromiseLifecycleEventObject, promiseResolved: generatePromiseLifecycleEventObject, promiseCancelled: generatePromiseLifecycleEventObject, promiseChained: function(name, promise, child) { return {promise: promise, child: child}; }, warning: function(name, warning) { return {warning: warning}; }, unhandledRejection: function (name, reason, promise) { return {reason: reason, promise: promise}; }, rejectionHandled: generatePromiseLifecycleEventObject }; var activeFireEvent = function (name) { var globalEventFired = false; try { globalEventFired = fireGlobalEvent.apply(null, arguments); } catch (e) { async.throwLater(e); globalEventFired = true; } var domEventFired = false; try { domEventFired = fireDomEvent(name, eventToObjectGenerator[name].apply(null, arguments)); } catch (e) { async.throwLater(e); domEventFired = true; } return domEventFired || globalEventFired; }; Promise.config = function(opts) { opts = Object(opts); if ("longStackTraces" in opts) { if (opts.longStackTraces) { Promise.longStackTraces(); } else if (!opts.longStackTraces && Promise.hasLongStackTraces()) { disableLongStackTraces(); } } if ("warnings" in opts) { var warningsOption = opts.warnings; config.warnings = !!warningsOption; wForgottenReturn = config.warnings; if (util.isObject(warningsOption)) { if ("wForgottenReturn" in warningsOption) { wForgottenReturn = !!warningsOption.wForgottenReturn; } } } if ("cancellation" in opts && opts.cancellation && !config.cancellation) { if (async.haveItemsQueued()) { throw new Error( "cannot enable cancellation after promises are in use"); } Promise.prototype._clearCancellationData = cancellationClearCancellationData; Promise.prototype._propagateFrom = cancellationPropagateFrom; Promise.prototype._onCancel = cancellationOnCancel; Promise.prototype._setOnCancel = cancellationSetOnCancel; Promise.prototype._attachCancellationCallback = cancellationAttachCancellationCallback; Promise.prototype._execute = cancellationExecute; propagateFromFunction = cancellationPropagateFrom; config.cancellation = true; } if ("monitoring" in opts) { if (opts.monitoring && !config.monitoring) { config.monitoring = true; Promise.prototype._fireEvent = activeFireEvent; } else if (!opts.monitoring && config.monitoring) { config.monitoring = false; Promise.prototype._fireEvent = defaultFireEvent; } } if ("asyncHooks" in opts && util.nodeSupportsAsyncResource) { var prev = config.asyncHooks; var cur = !!opts.asyncHooks; if (prev !== cur) { config.asyncHooks = cur; if (cur) { enableAsyncHooks(); } else { disableAsyncHooks(); } } } return Promise; }; function defaultFireEvent() { return false; } Promise.prototype._fireEvent = defaultFireEvent; Promise.prototype._execute = function(executor, resolve, reject) { try { executor(resolve, reject); } catch (e) { return e; } }; Promise.prototype._onCancel = function () {}; Promise.prototype._setOnCancel = function (handler) { USE(handler); }; Promise.prototype._attachCancellationCallback = function(onCancel) { USE(onCancel); }; Promise.prototype._captureStackTrace = function () {}; Promise.prototype._attachExtraTrace = function () {}; Promise.prototype._dereferenceTrace = function () {}; Promise.prototype._clearCancellationData = function() {}; Promise.prototype._propagateFrom = function (parent, flags) { USE(parent); USE(flags); }; function cancellationExecute(executor, resolve, reject) { var promise = this; try { executor(resolve, reject, function(onCancel) { if (typeof onCancel !== "function") { throw new TypeError("onCancel must be a function, got: " + util.toString(onCancel)); } promise._attachCancellationCallback(onCancel); }); } catch (e) { return e; } } function cancellationAttachCancellationCallback(onCancel) { if (!this._isCancellable()) return this; var previousOnCancel = this._onCancel(); if (previousOnCancel !== undefined) { if (util.isArray(previousOnCancel)) { previousOnCancel.push(onCancel); } else { this._setOnCancel([previousOnCancel, onCancel]); } } else { this._setOnCancel(onCancel); } } function cancellationOnCancel() { ASSERT(this._isCancellable()); return this._onCancelField; } function cancellationSetOnCancel(onCancel) { ASSERT(this._isCancellable()); this._onCancelField = onCancel; } function cancellationClearCancellationData() { this._cancellationParent = undefined; this._onCancelField = undefined; } function cancellationPropagateFrom(parent, flags) { ASSERT(flags !== 0); if ((flags & PROPAGATE_CANCEL) !== 0) { this._cancellationParent = parent; var branchesRemainingToCancel = parent._branchesRemainingToCancel; if (branchesRemainingToCancel === undefined) { branchesRemainingToCancel = 0; } parent._branchesRemainingToCancel = branchesRemainingToCancel + 1; } if ((flags & PROPAGATE_BIND) !== 0 && parent._isBound()) { this._setBoundTo(parent._boundTo); } } function bindingPropagateFrom(parent, flags) { ASSERT(flags !== 0); if ((flags & PROPAGATE_BIND) !== 0 && parent._isBound()) { this._setBoundTo(parent._boundTo); } } var propagateFromFunction = bindingPropagateFrom; function boundValueFunction() { var ret = this._boundTo; if (ret !== undefined) { if (ret instanceof Promise) { if (ret.isFulfilled()) { return ret.value(); } else { return undefined; } } } return ret; } function longStackTracesCaptureStackTrace() { ASSERT(this._trace == null); this._trace = new CapturedTrace(this._peekContext()); } function longStackTracesAttachExtraTrace(error, ignoreSelf) { if (canAttachTrace(error)) { var trace = this._trace; if (trace !== undefined) { if (ignoreSelf) trace = trace._parent; } if (trace !== undefined) { trace.attachExtraTrace(error); } else if (!error.__stackCleaned__) { var parsed = parseStackAndMessage(error); util.notEnumerableProp(error, "stack", parsed.message + "\n" + parsed.stack.join("\n")); util.notEnumerableProp(error, "__stackCleaned__", true); } } } function longStackTracesDereferenceTrace() { this._trace = undefined; } function checkForgottenReturns(returnValue, promiseCreated, name, promise, parent) { if (returnValue === undefined && promiseCreated !== null && wForgottenReturn) { if (parent !== undefined && parent._returnedNonUndefined()) return; if (BIT_FIELD_READ(LENGTH_MASK, promise._bitField) === 0) return; if (name) name = name + " "; var handlerLine = ""; var creatorLine = ""; if (promiseCreated._trace) { var traceLines = promiseCreated._trace.stack.split("\n"); var stack = cleanStack(traceLines); for (var i = stack.length - 1; i >= 0; --i) { var line = stack[i]; if (!nodeFramePattern.test(line)) { var lineMatches = line.match(parseLinePattern); if (lineMatches) { handlerLine = "at " + lineMatches[1] + ":" + lineMatches[2] + ":" + lineMatches[3] + " "; } break; } } if (stack.length > 0) { var firstUserLine = stack[0]; for (var i = 0; i < traceLines.length; ++i) { if (traceLines[i] === firstUserLine) { if (i > 0) { creatorLine = "\n" + traceLines[i - 1]; } break; } } } } var msg = "a promise was created in a " + name + "handler " + handlerLine + "but was not returned from it, " + "see http://goo.gl/rRqMUw" + creatorLine; promise._warn(msg, true, promiseCreated); } } function deprecated(name, replacement) { var message = name + " is deprecated and will be removed in a future version."; if (replacement) message += " Use " + replacement + " instead."; return warn(message); } function warn(message, shouldUseOwnTrace, promise) { if (!config.warnings) return; var warning = new Warning(message); var ctx; if (shouldUseOwnTrace) { promise._attachExtraTrace(warning); } else if (config.longStackTraces && (ctx = Promise._peekContext())) { ctx.attachExtraTrace(warning); } else { var parsed = parseStackAndMessage(warning); warning.stack = parsed.message + "\n" + parsed.stack.join("\n"); } if (!activeFireEvent("warning", warning)) { formatAndLogError(warning, "", true); } } function reconstructStack(message, stacks) { for (var i = 0; i < stacks.length - 1; ++i) { stacks[i].push(FROM_PREVIOUS_EVENT); stacks[i] = stacks[i].join("\n"); } if (i < stacks.length) { stacks[i] = stacks[i].join("\n"); } return message + "\n" + stacks.join("\n"); } function removeDuplicateOrEmptyJumps(stacks) { for (var i = 0; i < stacks.length; ++i) { if (stacks[i].length === 0 || ((i + 1 < stacks.length) && stacks[i][0] === stacks[i+1][0])) { stacks.splice(i, 1); i--; } } } function removeCommonRoots(stacks) { var current = stacks[0]; for (var i = 1; i < stacks.length; ++i) { var prev = stacks[i]; var currentLastIndex = current.length - 1; var currentLastLine = current[currentLastIndex]; var commonRootMeetPoint = -1; for (var j = prev.length - 1; j >= 0; --j) { if (prev[j] === currentLastLine) { commonRootMeetPoint = j; break; } } for (var j = commonRootMeetPoint; j >= 0; --j) { var line = prev[j]; if (current[currentLastIndex] === line) { current.pop(); currentLastIndex--; } else { break; } } current = prev; } } function cleanStack(stack) { var ret = []; for (var i = 0; i < stack.length; ++i) { var line = stack[i]; var isTraceLine = NO_STACK_TRACE === line || stackFramePattern.test(line); var isInternalFrame = isTraceLine && shouldIgnore(line); if (isTraceLine && !isInternalFrame) { if (indentStackFrames && line.charAt(0) !== " ") { // Make Firefox stack traces readable...it is almost // impossible to see the event boundaries without // indentation. line = " " + line; } ret.push(line); } } return ret; } function stackFramesAsArray(error) { var stack = error.stack.replace(/\s+$/g, "").split("\n"); for (var i = 0; i < stack.length; ++i) { var line = stack[i]; if (NO_STACK_TRACE === line || stackFramePattern.test(line)) { break; } } // Chrome and IE include the error message in the stack if (i > 0 && error.name != "SyntaxError") { stack = stack.slice(i); } return stack; } function parseStackAndMessage(error) { var stack = error.stack; var message = error.toString(); stack = typeof stack === "string" && stack.length > 0 ? stackFramesAsArray(error) : [NO_STACK_TRACE]; return { message: message, stack: error.name == "SyntaxError" ? stack : cleanStack(stack) }; } function formatAndLogError(error, title, isSoft) { if (typeof console !== "undefined") { var message; if (util.isObject(error)) { var stack = error.stack; message = title + formatStack(stack, error); } else { message = title + String(error); } if (typeof printWarning === "function") { printWarning(message, isSoft); } else if (typeof console.log === "function" || typeof console.log === "object") { console.log(message); } } } function fireRejectionEvent(name, localHandler, reason, promise) { var localEventFired = false; try { if (typeof localHandler === "function") { localEventFired = true; if (name === REJECTION_HANDLED_EVENT) { localHandler(promise); } else { localHandler(reason, promise); } } } catch (e) { async.throwLater(e); } if (name === UNHANDLED_REJECTION_EVENT) { if (!activeFireEvent(name, reason, promise) && !localEventFired) { formatAndLogError(reason, UNHANDLED_REJECTION_HEADER); } } else { activeFireEvent(name, promise); } } function formatNonError(obj) { var str; if (typeof obj === "function") { str = "[function " + (obj.name || "anonymous") + "]"; } else { str = obj && typeof obj.toString === "function" ? obj.toString() : util.toString(obj); var ruselessToString = /\[object [a-zA-Z0-9$_]+\]/; if (ruselessToString.test(str)) { try { var newStr = JSON.stringify(obj); str = newStr; } catch(e) { } } if (str.length === 0) { str = "(empty array)"; } } return ("(<" + snip(str) + ">, no stack trace)"); } function snip(str) { var maxChars = 41; if (str.length < maxChars) { return str; } return str.substr(0, maxChars - 3) + "..."; } function longStackTracesIsSupported() { return typeof captureStackTrace === "function"; } // For filtering out internal calls from stack traces var shouldIgnore = function() { return false; }; var parseLineInfoRegex = /[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/; function parseLineInfo(line) { var matches = line.match(parseLineInfoRegex); if (matches) { return { fileName: matches[1], line: parseInt(matches[2], 10) }; } } function setBounds(firstLineError, lastLineError) { if (!longStackTracesIsSupported()) return; var firstStackLines = (firstLineError.stack || "").split("\n"); var lastStackLines = (lastLineError.stack || "").split("\n"); var firstIndex = -1; var lastIndex = -1; var firstFileName; var lastFileName; for (var i = 0; i < firstStackLines.length; ++i) { var result = parseLineInfo(firstStackLines[i]); if (result) { firstFileName = result.fileName; firstIndex = result.line; break; } } for (var i = 0; i < lastStackLines.length; ++i) { var result = parseLineInfo(lastStackLines[i]); if (result) { lastFileName = result.fileName; lastIndex = result.line; break; } } if (firstIndex < 0 || lastIndex < 0 || !firstFileName || !lastFileName || firstFileName !== lastFileName || firstIndex >= lastIndex) { return; } shouldIgnore = function(line) { if (bluebirdFramePattern.test(line)) return true; var info = parseLineInfo(line); if (info) { if (info.fileName === firstFileName && (firstIndex <= info.line && info.line <= lastIndex)) { return true; } } return false; }; } function CapturedTrace(parent) { ASSERT(parent === undefined || parent instanceof CapturedTrace); this._parent = parent; this._promisesCreated = 0; var length = this._length = 1 + (parent === undefined ? 0 : parent._length); captureStackTrace(this, CapturedTrace); // Unless the user manually nested > 32 indentation levels, // there must be cycles if (length > 32) this.uncycle(); } util.inherits(CapturedTrace, Error); Context.CapturedTrace = CapturedTrace; CapturedTrace.prototype.uncycle = function() { var length = this._length; if (length < 2) return; var nodes = []; var stackToIndex = {}; for (var i = 0, node = this; node !== undefined; ++i) { nodes.push(node); node = node._parent; } // the node length is only used as heuristic to decide when to decycle, as // there may be multiple linked lists that share members and decycling one // will fail to update lenghts in the other. This is the correct length. length = this._length = i; ASSERT(nodes[0] === this); ASSERT(nodes[nodes.length - 1] instanceof CapturedTrace); for (var i = length - 1; i >= 0; --i) { var stack = nodes[i].stack; if (stackToIndex[stack] === undefined) { stackToIndex[stack] = i; } } for (var i = 0; i < length; ++i) { var currentStack = nodes[i].stack; var index = stackToIndex[currentStack]; ASSERT(currentStack === nodes[index].stack); if (index !== undefined && index !== i) { if (index > 0) { ASSERT(nodes[index - 1]._parent === nodes[index]); nodes[index - 1]._parent = undefined; nodes[index - 1]._length = 1; } nodes[i]._parent = undefined; nodes[i]._length = 1; var cycleEdgeNode = i > 0 ? nodes[i - 1] : this; if (index < length - 1) { cycleEdgeNode._parent = nodes[index + 1]; cycleEdgeNode._parent.uncycle(); cycleEdgeNode._length = cycleEdgeNode._parent._length + 1; } else { cycleEdgeNode._parent = undefined; cycleEdgeNode._length = 1; } var currentChildLength = cycleEdgeNode._length + 1; for (var j = i - 2; j >= 0; --j) { nodes[j]._length = currentChildLength; currentChildLength++; } return; } } }; CapturedTrace.prototype.attachExtraTrace = function(error) { if (error.__stackCleaned__) return; this.uncycle(); var parsed = parseStackAndMessage(error); var message = parsed.message; var stacks = [parsed.stack]; var trace = this; while (trace !== undefined) { stacks.push(cleanStack(trace.stack.split("\n"))); trace = trace._parent; } removeCommonRoots(stacks); removeDuplicateOrEmptyJumps(stacks); util.notEnumerableProp(error, "stack", reconstructStack(message, stacks)); util.notEnumerableProp(error, "__stackCleaned__", true); }; var captureStackTrace = (function stackDetection() { var v8stackFramePattern = /^\s*at\s*/; var v8stackFormatter = function(stack, error) { ASSERT(error !== null); if (typeof stack === "string") return stack; if (error.name !== undefined && error.message !== undefined) { return error.toString(); } return formatNonError(error); }; //V8 if (typeof Error.stackTraceLimit === "number" && typeof Error.captureStackTrace === "function") { Error.stackTraceLimit += 6; stackFramePattern = v8stackFramePattern; formatStack = v8stackFormatter; var captureStackTrace = Error.captureStackTrace; // For node shouldIgnore = function(line) { return bluebirdFramePattern.test(line); }; return function(receiver, ignoreUntil) { Error.stackTraceLimit += 6; captureStackTrace(receiver, ignoreUntil); Error.stackTraceLimit -= 6; }; } var err = new Error(); //SpiderMonkey if (typeof err.stack === "string" && err.stack.split("\n")[0].indexOf("stackDetection@") >= 0) { stackFramePattern = /@/; formatStack = v8stackFormatter; indentStackFrames = true; return function captureStackTrace(o) { o.stack = new Error().stack; }; } var hasStackAfterThrow; try { throw new Error(); } catch(e) { hasStackAfterThrow = ("stack" in e); } // IE 10+ if (!("stack" in err) && hasStackAfterThrow && typeof Error.stackTraceLimit === "number") { stackFramePattern = v8stackFramePattern; formatStack = v8stackFormatter; return function captureStackTrace(o) { Error.stackTraceLimit += 6; try { throw new Error(); } catch(e) { o.stack = e.stack; } Error.stackTraceLimit -= 6; }; } formatStack = function(stack, error) { if (typeof stack === "string") return stack; if ((typeof error === "object" || typeof error === "function") && error.name !== undefined && error.message !== undefined) { return error.toString(); } return formatNonError(error); }; return null; })([]); if (typeof console !== "undefined" && typeof console.warn !== "undefined") { printWarning = function (message) { console.warn(message); }; if (util.isNode && process.stderr.isTTY) { printWarning = function(message, isSoft) { var color = isSoft ? "\u001b[33m" : "\u001b[31m"; console.warn(color + message + "\u001b[0m\n"); }; } else if (!util.isNode && typeof (new Error().stack) === "string") { printWarning = function(message, isSoft) { console.warn("%c" + message, isSoft ? "color: darkorange" : "color: red"); }; } } var config = { warnings: warnings, longStackTraces: false, cancellation: false, monitoring: false, asyncHooks: false }; if (longStackTraces) Promise.longStackTraces(); return { asyncHooks: function() { return config.asyncHooks; }, longStackTraces: function() { return config.longStackTraces; }, warnings: function() { return config.warnings; }, cancellation: function() { return config.cancellation; }, monitoring: function() { return config.monitoring; }, propagateFromFunction: function() { return propagateFromFunction; }, boundValueFunction: function() { return boundValueFunction; }, checkForgottenReturns: checkForgottenReturns, setBounds: setBounds, warn: warn, deprecated: deprecated, CapturedTrace: CapturedTrace, fireDomEvent: fireDomEvent, fireGlobalEvent: fireGlobalEvent }; }; ================================================ FILE: src/direct_resolve.js ================================================ "use strict"; module.exports = function(Promise) { function returner() { return this.value; } function thrower() { throw this.reason; } Promise.prototype["return"] = Promise.prototype.thenReturn = function (value) { if (value instanceof Promise) value.suppressUnhandledRejections(); return this._then( returner, undefined, undefined, {value: value}, undefined); }; Promise.prototype["throw"] = Promise.prototype.thenThrow = function (reason) { return this._then( thrower, undefined, undefined, {reason: reason}, undefined); }; Promise.prototype.catchThrow = function (reason) { if (arguments.length <= 1) { return this._then( undefined, thrower, undefined, {reason: reason}, undefined); } else { var _reason = arguments[1]; var handler = function() {throw _reason;}; return this.caught(reason, handler); } }; Promise.prototype.catchReturn = function (value) { if (arguments.length <= 1) { if (value instanceof Promise) value.suppressUnhandledRejections(); return this._then( undefined, returner, undefined, {value: value}, undefined); } else { var _value = arguments[1]; if (_value instanceof Promise) _value.suppressUnhandledRejections(); var handler = function() {return _value;}; return this.caught(value, handler); } }; }; ================================================ FILE: src/each.js ================================================ "use strict"; module.exports = function(Promise, INTERNAL) { var PromiseReduce = Promise.reduce; var PromiseAll = Promise.all; function promiseAllThis() { return PromiseAll(this); } function PromiseMapSeries(promises, fn) { return PromiseReduce(promises, fn, INTERNAL, INTERNAL); } Promise.prototype.each = function (fn) { return PromiseReduce(this, fn, INTERNAL, 0) ._then(promiseAllThis, undefined, undefined, this, undefined); }; Promise.prototype.mapSeries = function (fn) { return PromiseReduce(this, fn, INTERNAL, INTERNAL); }; Promise.each = function (promises, fn) { return PromiseReduce(promises, fn, INTERNAL, 0) ._then(promiseAllThis, undefined, undefined, promises, undefined); }; Promise.mapSeries = PromiseMapSeries; }; ================================================ FILE: src/errors.js ================================================ "use strict"; var es5 = require("./es5"); var Objectfreeze = es5.freeze; var util = require("./util"); var inherits = util.inherits; var notEnumerableProp = util.notEnumerableProp; function subError(nameProperty, defaultMessage) { function SubError(message) { if (!(this instanceof SubError)) return new SubError(message); notEnumerableProp(this, "message", typeof message === "string" ? message : defaultMessage); notEnumerableProp(this, "name", nameProperty); if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } else { Error.call(this); } } inherits(SubError, Error); return SubError; } var _TypeError, _RangeError; var Warning = subError("Warning", "warning"); var CancellationError = subError("CancellationError", "cancellation error"); var TimeoutError = subError("TimeoutError", "timeout error"); var AggregateError = subError("AggregateError", "aggregate error"); try { _TypeError = TypeError; _RangeError = RangeError; } catch(e) { _TypeError = subError("TypeError", "type error"); _RangeError = subError("RangeError", "range error"); } var methods = ("join pop push shift unshift slice filter forEach some " + "every map indexOf lastIndexOf reduce reduceRight sort reverse").split(" "); for (var i = 0; i < methods.length; ++i) { if (typeof Array.prototype[methods[i]] === "function") { AggregateError.prototype[methods[i]] = Array.prototype[methods[i]]; } } es5.defineProperty(AggregateError.prototype, "length", { value: 0, configurable: false, writable: true, enumerable: true }); AggregateError.prototype[OPERATIONAL_ERROR_KEY] = true; var level = 0; AggregateError.prototype.toString = function() { var indent = Array(level * 4 + 1).join(" "); var ret = "\n" + indent + "AggregateError of:" + "\n"; level++; indent = Array(level * 4 + 1).join(" "); for (var i = 0; i < this.length; ++i) { var str = this[i] === this ? "[Circular AggregateError]" : this[i] + ""; var lines = str.split("\n"); for (var j = 0; j < lines.length; ++j) { lines[j] = indent + lines[j]; } str = lines.join("\n"); ret += str + "\n"; } level--; return ret; }; function OperationalError(message) { if (!(this instanceof OperationalError)) return new OperationalError(message); notEnumerableProp(this, "name", "OperationalError"); notEnumerableProp(this, "message", message); this.cause = message; this[OPERATIONAL_ERROR_KEY] = true; if (message instanceof Error) { notEnumerableProp(this, "message", message.message); notEnumerableProp(this, "stack", message.stack); } else if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } } inherits(OperationalError, Error); //Ensure all copies of the library throw the same error types var errorTypes = Error[BLUEBIRD_ERRORS]; if (!errorTypes) { errorTypes = Objectfreeze({ CancellationError: CancellationError, TimeoutError: TimeoutError, OperationalError: OperationalError, RejectionError: OperationalError, AggregateError: AggregateError }); es5.defineProperty(Error, BLUEBIRD_ERRORS, { value: errorTypes, writable: false, enumerable: false, configurable: false }); } module.exports = { Error: Error, TypeError: _TypeError, RangeError: _RangeError, CancellationError: errorTypes.CancellationError, OperationalError: errorTypes.OperationalError, TimeoutError: errorTypes.TimeoutError, AggregateError: errorTypes.AggregateError, Warning: Warning }; ================================================ FILE: src/es5.js ================================================ var isES5 = (function(){ "use strict"; return this === undefined; })(); if (isES5) { module.exports = { freeze: Object.freeze, defineProperty: Object.defineProperty, getDescriptor: Object.getOwnPropertyDescriptor, keys: Object.keys, names: Object.getOwnPropertyNames, getPrototypeOf: Object.getPrototypeOf, isArray: Array.isArray, isES5: isES5, propertyIsWritable: function(obj, prop) { var descriptor = Object.getOwnPropertyDescriptor(obj, prop); return !!(!descriptor || descriptor.writable || descriptor.set); } }; } else { var has = {}.hasOwnProperty; var str = {}.toString; var proto = {}.constructor.prototype; var ObjectKeys = function (o) { var ret = []; for (var key in o) { if (has.call(o, key)) { ret.push(key); } } return ret; }; var ObjectGetDescriptor = function(o, key) { return {value: o[key]}; }; var ObjectDefineProperty = function (o, key, desc) { o[key] = desc.value; return o; }; var ObjectFreeze = function (obj) { return obj; }; var ObjectGetPrototypeOf = function (obj) { try { return Object(obj).constructor.prototype; } catch (e) { return proto; } }; var ArrayIsArray = function (obj) { try { return str.call(obj) === "[object Array]"; } catch(e) { return false; } }; module.exports = { isArray: ArrayIsArray, keys: ObjectKeys, names: ObjectKeys, defineProperty: ObjectDefineProperty, getDescriptor: ObjectGetDescriptor, freeze: ObjectFreeze, getPrototypeOf: ObjectGetPrototypeOf, isES5: isES5, propertyIsWritable: function() { return true; } }; } ================================================ FILE: src/filter.js ================================================ "use strict"; module.exports = function(Promise, INTERNAL) { var PromiseMap = Promise.map; Promise.prototype.filter = function (fn, options) { return PromiseMap(this, fn, options, INTERNAL); }; Promise.filter = function (promises, fn, options) { return PromiseMap(promises, fn, options, INTERNAL); }; }; ================================================ FILE: src/finally.js ================================================ "use strict"; module.exports = function(Promise, tryConvertToPromise, NEXT_FILTER) { var util = require("./util"); var CancellationError = Promise.CancellationError; var errorObj = util.errorObj; var catchFilter = require("./catch_filter")(NEXT_FILTER); function PassThroughHandlerContext(promise, type, handler) { this.promise = promise; this.type = type; this.handler = handler; this.called = false; this.cancelPromise = null; } PassThroughHandlerContext.prototype.isFinallyHandler = function() { return this.type === FINALLY_TYPE; }; function FinallyHandlerCancelReaction(finallyHandler) { this.finallyHandler = finallyHandler; } FinallyHandlerCancelReaction.prototype._resultCancelled = function() { checkCancel(this.finallyHandler); }; function checkCancel(ctx, reason) { if (ctx.cancelPromise != null) { if (arguments.length > 1) { ctx.cancelPromise._reject(reason); } else { ctx.cancelPromise._cancel(); } ctx.cancelPromise = null; return true; } return false; } function succeed() { return finallyHandler.call(this, this.promise._target()._settledValue()); } function fail(reason) { if (checkCancel(this, reason)) return; errorObj.e = reason; return errorObj; } function finallyHandler(reasonOrValue) { var promise = this.promise; var handler = this.handler; if (!this.called) { this.called = true; var ret = this.isFinallyHandler() ? handler.call(promise._boundValue()) : handler.call(promise._boundValue(), reasonOrValue); if (ret === NEXT_FILTER) { return ret; } else if (ret !== undefined) { promise._setReturnedNonUndefined(); var maybePromise = tryConvertToPromise(ret, promise); if (maybePromise instanceof Promise) { if (this.cancelPromise != null) { if (maybePromise._isCancelled()) { var reason = new CancellationError(LATE_CANCELLATION_OBSERVER); promise._attachExtraTrace(reason); errorObj.e = reason; return errorObj; } else if (maybePromise.isPending()) { maybePromise._attachCancellationCallback( new FinallyHandlerCancelReaction(this)); } } return maybePromise._then( succeed, fail, undefined, this, undefined); } } } if (promise.isRejected()) { checkCancel(this); errorObj.e = reasonOrValue; return errorObj; } else { checkCancel(this); return reasonOrValue; } } Promise.prototype._passThrough = function(handler, type, success, fail) { if (typeof handler !== "function") return this.then(); return this._then(success, fail, undefined, new PassThroughHandlerContext(this, type, handler), undefined); }; Promise.prototype.lastly = Promise.prototype["finally"] = function (handler) { return this._passThrough(handler, FINALLY_TYPE, finallyHandler, finallyHandler); }; Promise.prototype.tap = function (handler) { return this._passThrough(handler, TAP_TYPE, finallyHandler); }; Promise.prototype.tapCatch = function (handlerOrPredicate) { var len = arguments.length; if(len === 1) { return this._passThrough(handlerOrPredicate, TAP_TYPE, undefined, finallyHandler); } else { var catchInstances = new Array(len - 1), j = 0, i; for (i = 0; i < len - 1; ++i) { var item = arguments[i]; if (util.isObject(item)) { catchInstances[j++] = item; } else { return Promise.reject(new TypeError( "tapCatch statement predicate: " + OBJECT_ERROR + util.classString(item) )); } } catchInstances.length = j; var handler = arguments[i]; return this._passThrough(catchFilter(catchInstances, handler, this), TAP_TYPE, undefined, finallyHandler); } }; return PassThroughHandlerContext; }; ================================================ FILE: src/generators.js ================================================ "use strict"; module.exports = function(Promise, apiRejection, INTERNAL, tryConvertToPromise, Proxyable, debug) { var errors = require("./errors"); var TypeError = errors.TypeError; var ASSERT = require("./assert"); var util = require("./util"); var errorObj = util.errorObj; var tryCatch = util.tryCatch; var yieldHandlers = []; function promiseFromYieldHandler(value, yieldHandlers, traceParent) { for (var i = 0; i < yieldHandlers.length; ++i) { traceParent._pushContext(); var result = tryCatch(yieldHandlers[i])(value); traceParent._popContext(); if (result === errorObj) { traceParent._pushContext(); var ret = Promise.reject(errorObj.e); traceParent._popContext(); return ret; } var maybePromise = tryConvertToPromise(result, traceParent); if (maybePromise instanceof Promise) return maybePromise; } return null; } function PromiseSpawn(generatorFunction, receiver, yieldHandler, stack) { if (debug.cancellation()) { var internal = new Promise(INTERNAL); var _finallyPromise = this._finallyPromise = new Promise(INTERNAL); this._promise = internal.lastly(function() { return _finallyPromise; }); internal._captureStackTrace(); internal._setOnCancel(this); } else { var promise = this._promise = new Promise(INTERNAL); promise._captureStackTrace(); } this._stack = stack; this._generatorFunction = generatorFunction; this._receiver = receiver; this._generator = undefined; this._yieldHandlers = typeof yieldHandler === "function" ? [yieldHandler].concat(yieldHandlers) : yieldHandlers; this._yieldedPromise = null; this._cancellationPhase = false; } util.inherits(PromiseSpawn, Proxyable); PromiseSpawn.prototype._isResolved = function() { return this._promise === null; }; PromiseSpawn.prototype._cleanup = function() { this._promise = this._generator = null; if (debug.cancellation() && this._finallyPromise !== null) { this._finallyPromise._fulfill(); this._finallyPromise = null; } }; PromiseSpawn.prototype._promiseCancelled = function() { if (this._isResolved()) return; var implementsReturn = typeof this._generator["return"] !== "undefined"; var result; if (!implementsReturn) { var reason = new Promise.CancellationError( "generator .return() sentinel"); Promise.coroutine.returnSentinel = reason; this._promise._attachExtraTrace(reason); this._promise._pushContext(); result = tryCatch(this._generator["throw"]).call(this._generator, reason); this._promise._popContext(); } else { this._promise._pushContext(); result = tryCatch(this._generator["return"]).call(this._generator, undefined); this._promise._popContext(); } this._cancellationPhase = true; this._yieldedPromise = null; this._continue(result); }; PromiseSpawn.prototype._promiseFulfilled = function(value) { this._yieldedPromise = null; this._promise._pushContext(); var result = tryCatch(this._generator.next).call(this._generator, value); this._promise._popContext(); this._continue(result); }; PromiseSpawn.prototype._promiseRejected = function(reason) { this._yieldedPromise = null; this._promise._attachExtraTrace(reason); this._promise._pushContext(); var result = tryCatch(this._generator["throw"]) .call(this._generator, reason); this._promise._popContext(); this._continue(result); }; PromiseSpawn.prototype._resultCancelled = function() { if (this._yieldedPromise instanceof Promise) { var promise = this._yieldedPromise; this._yieldedPromise = null; promise.cancel(); } }; PromiseSpawn.prototype.promise = function () { return this._promise; }; PromiseSpawn.prototype._run = function () { this._generator = this._generatorFunction.call(this._receiver); this._receiver = this._generatorFunction = undefined; this._promiseFulfilled(undefined); }; PromiseSpawn.prototype._continue = function (result) { ASSERT(this._yieldedPromise == null); var promise = this._promise; if (result === errorObj) { this._cleanup(); if (this._cancellationPhase) { return promise.cancel(); } else { return promise._rejectCallback(result.e, false); } } var value = result.value; if (result.done === true) { this._cleanup(); if (this._cancellationPhase) { return promise.cancel(); } else { return promise._resolveCallback(value); } } else { var maybePromise = tryConvertToPromise(value, this._promise); if (!(maybePromise instanceof Promise)) { maybePromise = promiseFromYieldHandler(maybePromise, this._yieldHandlers, this._promise); ASSERT(maybePromise === null || maybePromise instanceof Promise); if (maybePromise === null) { this._promiseRejected( new TypeError( YIELDED_NON_PROMISE_ERROR.replace("%s", String(value)) + FROM_COROUTINE_CREATED_AT + this._stack.split("\n").slice(1, -7).join("\n") ) ); return; } } maybePromise = maybePromise._target(); var bitField = maybePromise._bitField; USE(bitField); if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) { this._yieldedPromise = maybePromise; maybePromise._proxy(this, null); } else if (BIT_FIELD_CHECK(IS_FULFILLED)) { Promise._async.invoke( this._promiseFulfilled, this, maybePromise._value() ); } else if (BIT_FIELD_CHECK(IS_REJECTED)) { Promise._async.invoke( this._promiseRejected, this, maybePromise._reason() ); } else { this._promiseCancelled(); } } }; Promise.coroutine = function (generatorFunction, options) { //Throw synchronously because Promise.coroutine is semantically //something you call at "compile time" to annotate static functions if (typeof generatorFunction !== "function") { throw new TypeError(NOT_GENERATOR_ERROR); } var yieldHandler = Object(options).yieldHandler; var PromiseSpawn$ = PromiseSpawn; var stack = new Error().stack; return function () { var generator = generatorFunction.apply(this, arguments); var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler, stack); var ret = spawn.promise(); spawn._generator = generator; spawn._promiseFulfilled(undefined); return ret; }; }; Promise.coroutine.addYieldHandler = function(fn) { if (typeof fn !== "function") { throw new TypeError(FUNCTION_ERROR + util.classString(fn)); } yieldHandlers.push(fn); }; Promise.spawn = function (generatorFunction) { debug.deprecated("Promise.spawn()", "Promise.coroutine()"); //Return rejected promise because Promise.spawn is semantically //something that will be called at runtime with possibly dynamic values if (typeof generatorFunction !== "function") { return apiRejection(NOT_GENERATOR_ERROR); } var spawn = new PromiseSpawn(generatorFunction, this); var ret = spawn.promise(); spawn._run(Promise.spawn); return ret; }; }; ================================================ FILE: src/join.js ================================================ "use strict"; module.exports = function(Promise, PromiseArray, tryConvertToPromise, INTERNAL, async) { var util = require("./util"); var canEvaluate = util.canEvaluate; var tryCatch = util.tryCatch; var errorObj = util.errorObj; var reject; if (!__BROWSER__) { if (canEvaluate) { var thenCallback = function(i) { return new Function("value", "holder", " \n\ 'use strict'; \n\ holder.pIndex = value; \n\ holder.checkFulfillment(this); \n\ ".replace(/Index/g, i)); }; var promiseSetter = function(i) { return new Function("promise", "holder", " \n\ 'use strict'; \n\ holder.pIndex = promise; \n\ ".replace(/Index/g, i)); }; var generateHolderClass = function(total) { var props = new Array(total); for (var i = 0; i < props.length; ++i) { props[i] = "this.p" + (i+1); } var assignment = props.join(" = ") + " = null;"; var cancellationCode= "var promise;\n" + props.map(function(prop) { return " \n\ promise = " + prop + "; \n\ if (promise instanceof Promise) { \n\ promise.cancel(); \n\ } \n\ "; }).join("\n"); var passedArguments = props.join(", "); var name = "Holder$" + total; var code = "return function(tryCatch, errorObj, Promise, async) { \n\ 'use strict'; \n\ function [TheName](fn) { \n\ [TheProperties] \n\ this.fn = fn; \n\ this.asyncNeeded = true; \n\ this.now = 0; \n\ } \n\ \n\ [TheName].prototype._callFunction = function(promise) { \n\ promise._pushContext(); \n\ var ret = tryCatch(this.fn)([ThePassedArguments]); \n\ promise._popContext(); \n\ if (ret === errorObj) { \n\ promise._rejectCallback(ret.e, false); \n\ } else { \n\ promise._resolveCallback(ret); \n\ } \n\ }; \n\ \n\ [TheName].prototype.checkFulfillment = function(promise) { \n\ var now = ++this.now; \n\ if (now === [TheTotal]) { \n\ if (this.asyncNeeded) { \n\ async.invoke(this._callFunction, this, promise); \n\ } else { \n\ this._callFunction(promise); \n\ } \n\ \n\ } \n\ }; \n\ \n\ [TheName].prototype._resultCancelled = function() { \n\ [CancellationCode] \n\ }; \n\ \n\ return [TheName]; \n\ }(tryCatch, errorObj, Promise, async); \n\ "; code = code.replace(/\[TheName\]/g, name) .replace(/\[TheTotal\]/g, total) .replace(/\[ThePassedArguments\]/g, passedArguments) .replace(/\[TheProperties\]/g, assignment) .replace(/\[CancellationCode\]/g, cancellationCode); return new Function("tryCatch", "errorObj", "Promise", "async", code) (tryCatch, errorObj, Promise, async); }; var holderClasses = []; var thenCallbacks = []; var promiseSetters = []; for (var i = 0; i < GENERATED_CLASS_COUNT; ++i) { holderClasses.push(generateHolderClass(i + 1)); thenCallbacks.push(thenCallback(i + 1)); promiseSetters.push(promiseSetter(i + 1)); } reject = function (reason) { this._reject(reason); }; }} Promise.join = function () { var last = arguments.length - 1; var fn; if (last > 0 && typeof arguments[last] === "function") { fn = arguments[last]; if (!__BROWSER__) { if (last <= GENERATED_CLASS_COUNT && canEvaluate) { var ret = new Promise(INTERNAL); ret._captureStackTrace(); var HolderClass = holderClasses[last - 1]; var holder = new HolderClass(fn); var callbacks = thenCallbacks; for (var i = 0; i < last; ++i) { var maybePromise = tryConvertToPromise(arguments[i], ret); if (maybePromise instanceof Promise) { maybePromise = maybePromise._target(); var bitField = maybePromise._bitField; USE(bitField); if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) { maybePromise._then(callbacks[i], reject, undefined, ret, holder); promiseSetters[i](maybePromise, holder); holder.asyncNeeded = false; } else if (BIT_FIELD_CHECK(IS_FULFILLED)) { callbacks[i].call(ret, maybePromise._value(), holder); } else if (BIT_FIELD_CHECK(IS_REJECTED)) { ret._reject(maybePromise._reason()); } else { ret._cancel(); } } else { callbacks[i].call(ret, maybePromise, holder); } } if (!ret._isFateSealed()) { if (holder.asyncNeeded) { var context = Promise._getContext(); holder.fn = util.contextBind(context, holder.fn); } ret._setAsyncGuaranteed(); ret._setOnCancel(holder); } return ret; } } } INLINE_SLICE(args, arguments); if (fn) args.pop(); var ret = new PromiseArray(args).promise(); return fn !== undefined ? ret.spread(fn) : ret; }; }; ================================================ FILE: src/map.js ================================================ "use strict"; module.exports = function(Promise, PromiseArray, apiRejection, tryConvertToPromise, INTERNAL, debug) { var ASSERT = require("./assert"); var util = require("./util"); var tryCatch = util.tryCatch; var errorObj = util.errorObj; var async = Promise._async; function MappingPromiseArray(promises, fn, limit, _filter) { this.constructor$(promises); this._promise._captureStackTrace(); var context = Promise._getContext(); this._callback = util.contextBind(context, fn); this._preservedValues = _filter === INTERNAL ? new Array(this.length()) : null; this._limit = limit; this._inFlight = 0; this._queue = []; async.invoke(this._asyncInit, this, undefined); if (util.isArray(promises)) { for (var i = 0; i < promises.length; ++i) { var maybePromise = promises[i]; if (maybePromise instanceof Promise) { maybePromise.suppressUnhandledRejections(); } } } } util.inherits(MappingPromiseArray, PromiseArray); MappingPromiseArray.prototype._asyncInit = function() { this._init$(undefined, RESOLVE_ARRAY); }; // The following hack is required because the super constructor // might call promiseFulfilled before this.callback = fn is set // // The super constructor call must always be first so that fields // are initialized in the same order so that the sub-class instances // will share same memory layout as the super class instances // Override MappingPromiseArray.prototype._init = function () {}; // Override MappingPromiseArray.prototype._promiseFulfilled = function (value, index) { ASSERT(!this._isResolved()); var values = this._values; var length = this.length(); var preservedValues = this._preservedValues; var limit = this._limit; // Callback has been called for this index if it's negative if (index < 0) { // Restore the actual index value index = (index * -1) - 1; values[index] = value; if (limit >= 1) { this._inFlight--; this._drainQueue(); if (this._isResolved()) return true; } } else { if (limit >= 1 && this._inFlight >= limit) { values[index] = value; this._queue.push(index); return false; } if (preservedValues !== null) preservedValues[index] = value; var promise = this._promise; var callback = this._callback; var receiver = promise._boundValue(); promise._pushContext(); var ret = tryCatch(callback).call(receiver, value, index, length); var promiseCreated = promise._popContext(); debug.checkForgottenReturns( ret, promiseCreated, preservedValues !== null ? "Promise.filter" : "Promise.map", promise ); if (ret === errorObj) { this._reject(ret.e); return true; } // If the mapper function returned a promise we simply reuse // The MappingPromiseArray as a PromiseArray for round 2. // To mark an index as "round 2" its inverted by adding +1 and // multiplying by -1 var maybePromise = tryConvertToPromise(ret, this._promise); if (maybePromise instanceof Promise) { maybePromise = maybePromise._target(); var bitField = maybePromise._bitField; USE(bitField); if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) { if (limit >= 1) this._inFlight++; values[index] = maybePromise; maybePromise._proxy(this, (index + 1) * -1); return false; } else if (BIT_FIELD_CHECK(IS_FULFILLED)) { ret = maybePromise._value(); } else if (BIT_FIELD_CHECK(IS_REJECTED)) { this._reject(maybePromise._reason()); return true; } else { this._cancel(); return true; } } values[index] = ret; } var totalResolved = ++this._totalResolved; if (totalResolved >= length) { if (preservedValues !== null) { this._filter(values, preservedValues); } else { this._resolve(values); } return true; } return false; }; MappingPromiseArray.prototype._drainQueue = function () { var queue = this._queue; var limit = this._limit; var values = this._values; while (queue.length > 0 && this._inFlight < limit) { if (this._isResolved()) return; var index = queue.pop(); this._promiseFulfilled(values[index], index); } }; MappingPromiseArray.prototype._filter = function (booleans, values) { var len = values.length; var ret = new Array(len); var j = 0; for (var i = 0; i < len; ++i) { if (booleans[i]) ret[j++] = values[i]; } ret.length = j; this._resolve(ret); }; MappingPromiseArray.prototype.preservedValues = function () { return this._preservedValues; }; function map(promises, fn, options, _filter) { if (typeof fn !== "function") { return apiRejection(FUNCTION_ERROR + util.classString(fn)); } var limit = 0; if (options !== undefined) { if (typeof options === "object" && options !== null) { if (typeof options.concurrency !== "number") { return Promise.reject( new TypeError("'concurrency' must be a number but it is " + util.classString(options.concurrency))); } limit = options.concurrency; } else { return Promise.reject(new TypeError( "options argument must be an object but it is " + util.classString(options))); } } limit = typeof limit === "number" && isFinite(limit) && limit >= 1 ? limit : 0; return new MappingPromiseArray(promises, fn, limit, _filter).promise(); } Promise.prototype.map = function (fn, options) { return map(this, fn, options, null); }; Promise.map = function (promises, fn, options, _filter) { return map(promises, fn, options, _filter); }; }; ================================================ FILE: src/method.js ================================================ "use strict"; module.exports = function(Promise, INTERNAL, tryConvertToPromise, apiRejection, debug) { var util = require("./util"); var ASSERT = require("./assert"); var tryCatch = util.tryCatch; Promise.method = function (fn) { if (typeof fn !== "function") { throw new Promise.TypeError(FUNCTION_ERROR + util.classString(fn)); } return function () { var ret = new Promise(INTERNAL); ret._captureStackTrace(); ret._pushContext(); var value = tryCatch(fn).apply(this, arguments); var promiseCreated = ret._popContext(); debug.checkForgottenReturns( value, promiseCreated, "Promise.method", ret); ret._resolveFromSyncValue(value); return ret; }; }; Promise.attempt = Promise["try"] = function (fn) { if (typeof fn !== "function") { return apiRejection(FUNCTION_ERROR + util.classString(fn)); } var ret = new Promise(INTERNAL); ret._captureStackTrace(); ret._pushContext(); var value; if (arguments.length > 1) { debug.deprecated("calling Promise.try with more than 1 argument"); var arg = arguments[1]; var ctx = arguments[2]; value = util.isArray(arg) ? tryCatch(fn).apply(ctx, arg) : tryCatch(fn).call(ctx, arg); } else { value = tryCatch(fn)(); } var promiseCreated = ret._popContext(); debug.checkForgottenReturns( value, promiseCreated, "Promise.try", ret); ret._resolveFromSyncValue(value); return ret; }; Promise.prototype._resolveFromSyncValue = function (value) { ASSERT(!this._isFollowing()); if (value === util.errorObj) { this._rejectCallback(value.e, false); } else { this._resolveCallback(value, true); } }; }; ================================================ FILE: src/nodeback.js ================================================ "use strict"; var util = require("./util"); var maybeWrapAsError = util.maybeWrapAsError; var errors = require("./errors"); var OperationalError = errors.OperationalError; var es5 = require("./es5"); function isUntypedError(obj) { return obj instanceof Error && es5.getPrototypeOf(obj) === Error.prototype; } var rErrorKey = /^(?:name|message|stack|cause)$/; function wrapAsOperationalError(obj) { var ret; if (isUntypedError(obj)) { ret = new OperationalError(obj); ret.name = obj.name; ret.message = obj.message; ret.stack = obj.stack; var keys = es5.keys(obj); for (var i = 0; i < keys.length; ++i) { var key = keys[i]; if (!rErrorKey.test(key)) { ret[key] = obj[key]; } } return ret; } util.markAsOriginatingFromRejection(obj); return obj; } function nodebackForPromise(promise, multiArgs) { return function(err, value) { if (promise === null) return; if (err) { var wrapped = wrapAsOperationalError(maybeWrapAsError(err)); promise._attachExtraTrace(wrapped); promise._reject(wrapped); } else if (!multiArgs) { promise._fulfill(value); } else { INLINE_SLICE(args, arguments, 1); promise._fulfill(args); } promise = null; }; } module.exports = nodebackForPromise; ================================================ FILE: src/nodeify.js ================================================ "use strict"; module.exports = function(Promise) { var util = require("./util"); var async = Promise._async; var ASSERT = require("./assert"); var tryCatch = util.tryCatch; var errorObj = util.errorObj; function spreadAdapter(val, nodeback) { var promise = this; if (!util.isArray(val)) return successAdapter.call(promise, val, nodeback); var ret = tryCatch(nodeback).apply(promise._boundValue(), [null].concat(val)); if (ret === errorObj) { async.throwLater(ret.e); } } function successAdapter(val, nodeback) { var promise = this; var receiver = promise._boundValue(); ASSERT(typeof nodeback == "function"); var ret = val === undefined ? tryCatch(nodeback).call(receiver, null) : tryCatch(nodeback).call(receiver, null, val); if (ret === errorObj) { async.throwLater(ret.e); } } function errorAdapter(reason, nodeback) { var promise = this; if (!reason) { var newReason = new Error(reason + ""); newReason.cause = reason; reason = newReason; ASSERT(!!reason); } ASSERT(typeof nodeback == "function"); var ret = tryCatch(nodeback).call(promise._boundValue(), reason); if (ret === errorObj) { async.throwLater(ret.e); } } Promise.prototype.asCallback = Promise.prototype.nodeify = function (nodeback, options) { if (typeof nodeback == "function") { var adapter = successAdapter; if (options !== undefined && Object(options).spread) { adapter = spreadAdapter; } this._then( adapter, errorAdapter, undefined, this, nodeback ); } return this; }; }; ================================================ FILE: src/promise.js ================================================ "use strict"; module.exports = function() { var makeSelfResolutionError = function () { return new TypeError(CIRCULAR_RESOLUTION_ERROR); }; var reflectHandler = function() { return new Promise.PromiseInspection(this._target()); }; var apiRejection = function(msg) { return Promise.reject(new TypeError(msg)); }; function Proxyable() {} var UNDEFINED_BINDING = {}; var ASSERT = require("./assert"); var util = require("./util"); util.setReflectHandler(reflectHandler); var getDomain = function() { var domain = process.domain; if (domain === undefined) { return null; } return domain; }; var getContextDefault = function() { return null; }; var getContextDomain = function() { return { domain: getDomain(), async: null }; }; var AsyncResource = util.isNode && util.nodeSupportsAsyncResource ? require("async_hooks").AsyncResource : null; var getContextAsyncHooks = function() { return { domain: getDomain(), async: new AsyncResource("Bluebird::Promise") }; }; var getContext = util.isNode ? getContextDomain : getContextDefault; util.notEnumerableProp(Promise, "_getContext", getContext); var enableAsyncHooks = function() { getContext = getContextAsyncHooks; util.notEnumerableProp(Promise, "_getContext", getContextAsyncHooks); }; var disableAsyncHooks = function() { getContext = getContextDomain; util.notEnumerableProp(Promise, "_getContext", getContextDomain); }; var es5 = require("./es5"); var Async = require("./async"); var async = new Async(); es5.defineProperty(Promise, "_async", {value: async}); var errors = require("./errors"); var TypeError = Promise.TypeError = errors.TypeError; Promise.RangeError = errors.RangeError; var CancellationError = Promise.CancellationError = errors.CancellationError; Promise.TimeoutError = errors.TimeoutError; Promise.OperationalError = errors.OperationalError; Promise.RejectionError = errors.OperationalError; Promise.AggregateError = errors.AggregateError; var INTERNAL = function(){}; var APPLY = {}; var NEXT_FILTER = {}; var tryConvertToPromise = require("./thenables")(Promise, INTERNAL); var PromiseArray = require("./promise_array")(Promise, INTERNAL, tryConvertToPromise, apiRejection, Proxyable); var Context = require("./context")(Promise); /*jshint unused:false*/ var createContext = Context.create; var debug = require("./debuggability")(Promise, Context, enableAsyncHooks, disableAsyncHooks); var CapturedTrace = debug.CapturedTrace; var PassThroughHandlerContext = require("./finally")(Promise, tryConvertToPromise, NEXT_FILTER); var catchFilter = require("./catch_filter")(NEXT_FILTER); var nodebackForPromise = require("./nodeback"); var errorObj = util.errorObj; var tryCatch = util.tryCatch; function check(self, executor) { if (self == null || self.constructor !== Promise) { throw new TypeError(CONSTRUCT_ERROR_INVOCATION); } if (typeof executor !== "function") { throw new TypeError(FUNCTION_ERROR + util.classString(executor)); } } function Promise(executor) { if (executor !== INTERNAL) { check(this, executor); } this._bitField = NO_STATE; this._fulfillmentHandler0 = undefined; this._rejectionHandler0 = undefined; this._promise0 = undefined; this._receiver0 = undefined; this._resolveFromExecutor(executor); this._promiseCreated(); this._fireEvent("promiseCreated", this); } Promise.prototype.toString = function () { return "[object Promise]"; }; Promise.prototype.caught = Promise.prototype["catch"] = function (fn) { var len = arguments.length; if (len > 1) { var catchInstances = new Array(len - 1), j = 0, i; for (i = 0; i < len - 1; ++i) { var item = arguments[i]; if (util.isObject(item)) { catchInstances[j++] = item; } else { return apiRejection("Catch statement predicate: " + OBJECT_ERROR + util.classString(item)); } } catchInstances.length = j; fn = arguments[i]; if (typeof fn !== "function") { throw new TypeError("The last argument to .catch() " + "must be a function, got " + util.toString(fn)); } return this.then(undefined, catchFilter(catchInstances, fn, this)); } return this.then(undefined, fn); }; Promise.prototype.reflect = function () { return this._then(reflectHandler, reflectHandler, undefined, this, undefined); }; Promise.prototype.then = function (didFulfill, didReject) { if (debug.warnings() && arguments.length > 0 && typeof didFulfill !== "function" && typeof didReject !== "function") { var msg = ".then() only accepts functions but was passed: " + util.classString(didFulfill); if (arguments.length > 1) { msg += ", " + util.classString(didReject); } this._warn(msg); } return this._then(didFulfill, didReject, undefined, undefined, undefined); }; Promise.prototype.done = function (didFulfill, didReject) { var promise = this._then(didFulfill, didReject, undefined, undefined, undefined); promise._setIsFinal(); }; Promise.prototype.spread = function (fn) { if (typeof fn !== "function") { return apiRejection(FUNCTION_ERROR + util.classString(fn)); } return this.all()._then(fn, undefined, undefined, APPLY, undefined); }; Promise.prototype.toJSON = function () { var ret = { isFulfilled: false, isRejected: false, fulfillmentValue: undefined, rejectionReason: undefined }; if (this.isFulfilled()) { ret.fulfillmentValue = this.value(); ret.isFulfilled = true; } else if (this.isRejected()) { ret.rejectionReason = this.reason(); ret.isRejected = true; } return ret; }; Promise.prototype.all = function () { if (arguments.length > 0) { this._warn(".all() was passed arguments but it does not take any"); } return new PromiseArray(this).promise(); }; Promise.prototype.error = function (fn) { return this.caught(util.originatesFromRejection, fn); }; Promise.getNewLibraryCopy = module.exports; Promise.is = function (val) { return val instanceof Promise; }; Promise.fromNode = Promise.fromCallback = function(fn) { var ret = new Promise(INTERNAL); ret._captureStackTrace(); var multiArgs = arguments.length > 1 ? !!Object(arguments[1]).multiArgs : false; var result = tryCatch(fn)(nodebackForPromise(ret, multiArgs)); if (result === errorObj) { ret._rejectCallback(result.e, true); } if (!ret._isFateSealed()) ret._setAsyncGuaranteed(); return ret; }; Promise.all = function (promises) { return new PromiseArray(promises).promise(); }; Promise.cast = function (obj) { var ret = tryConvertToPromise(obj); if (!(ret instanceof Promise)) { ret = new Promise(INTERNAL); ret._captureStackTrace(); ret._setFulfilled(); ret._rejectionHandler0 = obj; } return ret; }; Promise.resolve = Promise.fulfilled = Promise.cast; Promise.reject = Promise.rejected = function (reason) { var ret = new Promise(INTERNAL); ret._captureStackTrace(); ret._rejectCallback(reason, true); return ret; }; Promise.setScheduler = function(fn) { if (typeof fn !== "function") { throw new TypeError(FUNCTION_ERROR + util.classString(fn)); } return async.setScheduler(fn); }; Promise.prototype._then = function ( didFulfill, didReject, _, // For fast-cast compatibility between bluebird versions receiver, internalData ) { ASSERT(arguments.length === 5); var haveInternalData = internalData !== undefined; var promise = haveInternalData ? internalData : new Promise(INTERNAL); var target = this._target(); var bitField = target._bitField; if (!haveInternalData) { promise._propagateFrom(this, PROPAGATE_ALL); promise._captureStackTrace(); if (receiver === undefined && BIT_FIELD_CHECK(IS_BOUND, this._bitField)) { if (!BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) { receiver = this._boundValue(); } else { receiver = target === this ? undefined : this._boundTo; } } this._fireEvent("promiseChained", this, promise); } var context = getContext(); if (!BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) { var handler, value, settler = target._settlePromiseCtx; if (BIT_FIELD_CHECK(IS_FULFILLED)) { value = target._rejectionHandler0; handler = didFulfill; } else if (BIT_FIELD_CHECK(IS_REJECTED)) { value = target._fulfillmentHandler0; handler = didReject; target._unsetRejectionIsUnhandled(); } else { settler = target._settlePromiseLateCancellationObserver; value = new CancellationError(LATE_CANCELLATION_OBSERVER); target._attachExtraTrace(value); handler = didReject; } async.invoke(settler, target, { handler: util.contextBind(context, handler), promise: promise, receiver: receiver, value: value }); } else { target._addCallbacks(didFulfill, didReject, promise, receiver, context); } return promise; }; Promise.prototype._length = function () { ASSERT(arguments.length === 0); return this._bitField & LENGTH_MASK; }; Promise.prototype._isFateSealed = function () { return (this._bitField & IS_FATE_SEALED) !== 0; }; Promise.prototype._isFollowing = function () { return (this._bitField & IS_FOLLOWING) === IS_FOLLOWING; }; Promise.prototype._setLength = function (len) { this._bitField = (this._bitField & LENGTH_CLEAR_MASK) | (len & LENGTH_MASK); }; Promise.prototype._setFulfilled = function () { this._bitField = this._bitField | IS_FULFILLED; this._fireEvent("promiseFulfilled", this); }; Promise.prototype._setRejected = function () { this._bitField = this._bitField | IS_REJECTED; this._fireEvent("promiseRejected", this); }; Promise.prototype._setFollowing = function () { this._bitField = this._bitField | IS_FOLLOWING; this._fireEvent("promiseResolved", this); }; Promise.prototype._setIsFinal = function () { this._bitField = this._bitField | IS_FINAL; }; Promise.prototype._isFinal = function () { return (this._bitField & IS_FINAL) > 0; }; Promise.prototype._unsetCancelled = function() { this._bitField = this._bitField & (~IS_CANCELLED); }; Promise.prototype._setCancelled = function() { this._bitField = this._bitField | IS_CANCELLED; this._fireEvent("promiseCancelled", this); }; Promise.prototype._setWillBeCancelled = function() { this._bitField = this._bitField | WILL_BE_CANCELLED; }; Promise.prototype._setAsyncGuaranteed = function() { if (async.hasCustomScheduler()) return; var bitField = this._bitField; this._bitField = bitField | (((bitField & NO_ASYNC_GUARANTEE) >> ASYNC_GUARANTEE_SHIFT) ^ IS_ASYNC_GUARANTEED); }; Promise.prototype._setNoAsyncGuarantee = function() { this._bitField = (this._bitField | NO_ASYNC_GUARANTEE) & (~IS_ASYNC_GUARANTEED); }; Promise.prototype._receiverAt = function (index) { ASSERT(!this._isFollowing()); var ret = index === 0 ? this._receiver0 : this[ index * CALLBACK_SIZE - CALLBACK_SIZE + CALLBACK_RECEIVER_OFFSET]; //Only use the bound value when not calling internal methods if (ret === UNDEFINED_BINDING) { return undefined; } else if (ret === undefined && this._isBound()) { return this._boundValue(); } return ret; }; Promise.prototype._promiseAt = function (index) { ASSERT(index > 0); ASSERT(!this._isFollowing()); return this[ index * CALLBACK_SIZE - CALLBACK_SIZE + CALLBACK_PROMISE_OFFSET]; }; Promise.prototype._fulfillmentHandlerAt = function (index) { ASSERT(!this._isFollowing()); ASSERT(index > 0); return this[ index * CALLBACK_SIZE - CALLBACK_SIZE + CALLBACK_FULFILL_OFFSET]; }; Promise.prototype._rejectionHandlerAt = function (index) { ASSERT(!this._isFollowing()); ASSERT(index > 0); return this[ index * CALLBACK_SIZE - CALLBACK_SIZE + CALLBACK_REJECT_OFFSET]; }; Promise.prototype._boundValue = function() {}; Promise.prototype._migrateCallback0 = function (follower) { var bitField = follower._bitField; var fulfill = follower._fulfillmentHandler0; var reject = follower._rejectionHandler0; var promise = follower._promise0; var receiver = follower._receiverAt(0); if (receiver === undefined) receiver = UNDEFINED_BINDING; this._addCallbacks(fulfill, reject, promise, receiver, null); }; Promise.prototype._migrateCallbackAt = function (follower, index) { ASSERT(index > 0); var fulfill = follower._fulfillmentHandlerAt(index); var reject = follower._rejectionHandlerAt(index); var promise = follower._promiseAt(index); var receiver = follower._receiverAt(index); if (receiver === undefined) receiver = UNDEFINED_BINDING; this._addCallbacks(fulfill, reject, promise, receiver, null); }; Promise.prototype._addCallbacks = function ( fulfill, reject, promise, receiver, context ) { ASSERT(typeof context === "object"); ASSERT(!this._isFateSealed()); ASSERT(!this._isFollowing()); var index = this._length(); if (index >= MAX_LENGTH - CALLBACK_SIZE) { index = 0; this._setLength(0); } if (index === 0) { ASSERT(this._promise0 === undefined); ASSERT(this._receiver0 === undefined); ASSERT(this._fulfillmentHandler0 === undefined); ASSERT(this._rejectionHandler0 === undefined); this._promise0 = promise; this._receiver0 = receiver; if (typeof fulfill === "function") { this._fulfillmentHandler0 = util.contextBind(context, fulfill); } if (typeof reject === "function") { this._rejectionHandler0 = util.contextBind(context, reject); } } else { ASSERT(this[base + CALLBACK_PROMISE_OFFSET] === undefined); ASSERT(this[base + CALLBACK_RECEIVER_OFFSET] === undefined); ASSERT(this[base + CALLBACK_FULFILL_OFFSET] === undefined); ASSERT(this[base + CALLBACK_REJECT_OFFSET] === undefined); var base = index * CALLBACK_SIZE - CALLBACK_SIZE; this[base + CALLBACK_PROMISE_OFFSET] = promise; this[base + CALLBACK_RECEIVER_OFFSET] = receiver; if (typeof fulfill === "function") { this[base + CALLBACK_FULFILL_OFFSET] = util.contextBind(context, fulfill); } if (typeof reject === "function") { this[base + CALLBACK_REJECT_OFFSET] = util.contextBind(context, reject); } } this._setLength(index + 1); return index; }; Promise.prototype._proxy = function (proxyable, arg) { ASSERT(proxyable instanceof Proxyable); ASSERT(!(arg instanceof Promise)); ASSERT(!this._isFollowing()); ASSERT(arguments.length === 2); ASSERT(!this._isFateSealed()); this._addCallbacks(undefined, undefined, arg, proxyable, null); }; Promise.prototype._resolveCallback = function(value, shouldBind) { if (BIT_FIELD_CHECK(IS_FATE_SEALED, this._bitField)) return; if (value === this) return this._rejectCallback(makeSelfResolutionError(), false); var maybePromise = tryConvertToPromise(value, this); if (!(maybePromise instanceof Promise)) return this._fulfill(value); if (shouldBind) this._propagateFrom(maybePromise, PROPAGATE_BIND); var promise = maybePromise._target(); if (promise === this) { this._reject(makeSelfResolutionError()); return; } var bitField = promise._bitField; if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) { var len = this._length(); if (len > 0) promise._migrateCallback0(this); for (var i = 1; i < len; ++i) { promise._migrateCallbackAt(this, i); } this._setFollowing(); this._setLength(0); this._setFollowee(maybePromise); } else if (BIT_FIELD_CHECK(IS_FULFILLED)) { this._fulfill(promise._value()); } else if (BIT_FIELD_CHECK(IS_REJECTED)) { this._reject(promise._reason()); } else { var reason = new CancellationError(LATE_CANCELLATION_OBSERVER); promise._attachExtraTrace(reason); this._reject(reason); } }; Promise.prototype._rejectCallback = function(reason, synchronous, ignoreNonErrorWarnings) { var trace = util.ensureErrorObject(reason); var hasStack = trace === reason; if (!hasStack && !ignoreNonErrorWarnings && debug.warnings()) { var message = "a promise was rejected with a non-error: " + util.classString(reason); this._warn(message, true); } this._attachExtraTrace(trace, synchronous ? hasStack : false); this._reject(reason); }; Promise.prototype._resolveFromExecutor = function (executor) { if (executor === INTERNAL) return; ASSERT(typeof executor === "function"); var promise = this; this._captureStackTrace(); this._pushContext(); var synchronous = true; var r = this._execute(executor, function(value) { promise._resolveCallback(value); }, function (reason) { promise._rejectCallback(reason, synchronous); }); synchronous = false; this._popContext(); if (r !== undefined) { promise._rejectCallback(r, true); } }; Promise.prototype._settlePromiseFromHandler = function ( handler, receiver, value, promise ) { var bitField = promise._bitField; if (BIT_FIELD_CHECK(IS_CANCELLED)) return; promise._pushContext(); var x; if (receiver === APPLY) { if (!value || typeof value.length !== "number") { x = errorObj; x.e = new TypeError("cannot .spread() a non-array: " + util.classString(value)); } else { x = tryCatch(handler).apply(this._boundValue(), value); } } else { x = tryCatch(handler).call(receiver, value); } var promiseCreated = promise._popContext(); bitField = promise._bitField; if (BIT_FIELD_CHECK(IS_CANCELLED)) return; ASSERT(!promise._isFateSealed()); if (x === NEXT_FILTER) { promise._reject(value); } else if (x === errorObj) { promise._rejectCallback(x.e, false); } else { debug.checkForgottenReturns(x, promiseCreated, "", promise, this); promise._resolveCallback(x); } }; Promise.prototype._target = function() { var ret = this; while (ret._isFollowing()) ret = ret._followee(); return ret; }; Promise.prototype._followee = function() { ASSERT(this._isFollowing()); ASSERT(this._rejectionHandler0 instanceof Promise); return this._rejectionHandler0; }; Promise.prototype._setFollowee = function(promise) { ASSERT(this._isFollowing()); ASSERT(!(this._rejectionHandler0 instanceof Promise)); this._rejectionHandler0 = promise; }; Promise.prototype._settlePromise = function(promise, handler, receiver, value) { ASSERT(!this._isFollowing()); var isPromise = promise instanceof Promise; var bitField = this._bitField; var asyncGuaranteed = BIT_FIELD_CHECK(IS_ASYNC_GUARANTEED); if (BIT_FIELD_CHECK(IS_CANCELLED)) { if (isPromise) promise._invokeInternalOnCancel(); if (receiver instanceof PassThroughHandlerContext && receiver.isFinallyHandler()) { receiver.cancelPromise = promise; if (tryCatch(handler).call(receiver, value) === errorObj) { promise._reject(errorObj.e); } } else if (handler === reflectHandler) { promise._fulfill(reflectHandler.call(receiver)); } else if (receiver instanceof Proxyable) { receiver._promiseCancelled(promise); } else if (isPromise || promise instanceof PromiseArray) { promise._cancel(); } else { receiver.cancel(); } } else if (typeof handler === "function") { //if promise is not instanceof Promise //it is internally smuggled data if (!isPromise) { handler.call(receiver, value, promise); } else { if (asyncGuaranteed) promise._setAsyncGuaranteed(); this._settlePromiseFromHandler(handler, receiver, value, promise); } } else if (receiver instanceof Proxyable) { if (!receiver._isResolved()) { if (BIT_FIELD_CHECK(IS_FULFILLED)) { receiver._promiseFulfilled(value, promise); } else { receiver._promiseRejected(value, promise); } } } else if (isPromise) { if (asyncGuaranteed) promise._setAsyncGuaranteed(); if (BIT_FIELD_CHECK(IS_FULFILLED)) { promise._fulfill(value); } else { promise._reject(value); } } }; Promise.prototype._settlePromiseLateCancellationObserver = function(ctx) { var handler = ctx.handler; var promise = ctx.promise; var receiver = ctx.receiver; var value = ctx.value; if (typeof handler === "function") { if (!(promise instanceof Promise)) { handler.call(receiver, value, promise); } else { this._settlePromiseFromHandler(handler, receiver, value, promise); } } else if (promise instanceof Promise) { promise._reject(value); } }; Promise.prototype._settlePromiseCtx = function(ctx) { this._settlePromise(ctx.promise, ctx.handler, ctx.receiver, ctx.value); }; Promise.prototype._settlePromise0 = function(handler, value, bitField) { var promise = this._promise0; var receiver = this._receiverAt(0); this._promise0 = undefined; this._receiver0 = undefined; this._settlePromise(promise, handler, receiver, value); }; Promise.prototype._clearCallbackDataAtIndex = function(index) { ASSERT(!this._isFollowing()); ASSERT(index > 0); var base = index * CALLBACK_SIZE - CALLBACK_SIZE; this[base + CALLBACK_PROMISE_OFFSET] = this[base + CALLBACK_RECEIVER_OFFSET] = this[base + CALLBACK_FULFILL_OFFSET] = this[base + CALLBACK_REJECT_OFFSET] = undefined; }; Promise.prototype._fulfill = function (value) { var bitField = this._bitField; if (BIT_FIELD_READ(IS_FATE_SEALED)) return; if (value === this) { var err = makeSelfResolutionError(); this._attachExtraTrace(err); return this._reject(err); } this._setFulfilled(); this._rejectionHandler0 = value; if (BIT_FIELD_READ(LENGTH_MASK) > 0) { if (BIT_FIELD_CHECK(IS_ASYNC_GUARANTEED)) { this._settlePromises(); } else { async.settlePromises(this); } this._dereferenceTrace(); } }; Promise.prototype._reject = function (reason) { var bitField = this._bitField; if (BIT_FIELD_READ(IS_FATE_SEALED)) return; this._setRejected(); this._fulfillmentHandler0 = reason; if (this._isFinal()) { ASSERT(this._length() === 0); return async.fatalError(reason, util.isNode); } if (BIT_FIELD_READ(LENGTH_MASK) > 0) { async.settlePromises(this); } else { this._ensurePossibleRejectionHandled(); } }; Promise.prototype._fulfillPromises = function (len, value) { for (var i = 1; i < len; i++) { var handler = this._fulfillmentHandlerAt(i); var promise = this._promiseAt(i); var receiver = this._receiverAt(i); this._clearCallbackDataAtIndex(i); this._settlePromise(promise, handler, receiver, value); } }; Promise.prototype._rejectPromises = function (len, reason) { for (var i = 1; i < len; i++) { var handler = this._rejectionHandlerAt(i); var promise = this._promiseAt(i); var receiver = this._receiverAt(i); this._clearCallbackDataAtIndex(i); this._settlePromise(promise, handler, receiver, reason); } }; Promise.prototype._settlePromises = function () { var bitField = this._bitField; var len = BIT_FIELD_READ(LENGTH_MASK); if (len > 0) { if (BIT_FIELD_CHECK(IS_REJECTED_OR_CANCELLED)) { var reason = this._fulfillmentHandler0; this._settlePromise0(this._rejectionHandler0, reason, bitField); this._rejectPromises(len, reason); } else { var value = this._rejectionHandler0; this._settlePromise0(this._fulfillmentHandler0, value, bitField); this._fulfillPromises(len, value); } this._setLength(0); } this._clearCancellationData(); }; Promise.prototype._settledValue = function() { ASSERT(!this._isFollowing()); ASSERT(this._isFateSealed()); var bitField = this._bitField; if (BIT_FIELD_CHECK(IS_FULFILLED)) { return this._rejectionHandler0; } else if (BIT_FIELD_CHECK(IS_REJECTED)) { return this._fulfillmentHandler0; } // Implicit undefined for cancelled promise. }; if (typeof Symbol !== "undefined" && Symbol.toStringTag) { es5.defineProperty(Promise.prototype, Symbol.toStringTag, { get: function () { return "Object"; } }); } function deferResolve(v) {this.promise._resolveCallback(v);} function deferReject(v) {this.promise._rejectCallback(v, false);} Promise.defer = Promise.pending = function() { debug.deprecated("Promise.defer", "new Promise"); var promise = new Promise(INTERNAL); return { promise: promise, resolve: deferResolve, reject: deferReject }; }; util.notEnumerableProp(Promise, "_makeSelfResolutionError", makeSelfResolutionError); require("./method")(Promise, INTERNAL, tryConvertToPromise, apiRejection, debug); require("./bind")(Promise, INTERNAL, tryConvertToPromise, debug); require("./cancel")(Promise, PromiseArray, apiRejection, debug); require("./direct_resolve")(Promise); require("./synchronous_inspection")(Promise); require("./join")( Promise, PromiseArray, tryConvertToPromise, INTERNAL, async); Promise.Promise = Promise; Promise.version = "__VERSION__"; }; ================================================ FILE: src/promise_array.js ================================================ "use strict"; module.exports = function(Promise, INTERNAL, tryConvertToPromise, apiRejection, Proxyable) { var ASSERT = require("./assert"); var util = require("./util"); var isArray = util.isArray; //To avoid eagerly allocating the objects //and also because undefined cannot be smuggled function toResolutionValue(val) { switch(val) { case RESOLVE_ARRAY: return []; case RESOLVE_OBJECT: return {}; case RESOLVE_MAP: return new Map(); } ASSERT(false); } function PromiseArray(values) { ASSERT(arguments.length === 1); var promise = this._promise = new Promise(INTERNAL); if (values instanceof Promise) { promise._propagateFrom(values, PROPAGATE_ALL); values.suppressUnhandledRejections(); } promise._setOnCancel(this); this._values = values; this._length = 0; this._totalResolved = 0; this._init(undefined, RESOLVE_ARRAY); } util.inherits(PromiseArray, Proxyable); PromiseArray.prototype.length = function () { return this._length; }; PromiseArray.prototype.promise = function () { return this._promise; }; PromiseArray.prototype._init = function init(_, resolveValueIfEmpty) { var values = tryConvertToPromise(this._values, this._promise); if (values instanceof Promise) { values = values._target(); var bitField = values._bitField; USE(bitField); this._values = values; if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) { ASSERT(typeof resolveValueIfEmpty === "number"); ASSERT(resolveValueIfEmpty < 0); this._promise._setAsyncGuaranteed(); return values._then( init, this._reject, undefined, this, resolveValueIfEmpty ); } else if (BIT_FIELD_CHECK(IS_FULFILLED)) { values = values._value(); } else if (BIT_FIELD_CHECK(IS_REJECTED)) { return this._reject(values._reason()); } else { return this._cancel(); } } values = util.asArray(values); if (values === null) { var err = apiRejection( COLLECTION_ERROR + util.classString(values)).reason(); this._promise._rejectCallback(err, false); return; } if (values.length === 0) { if (resolveValueIfEmpty === RESOLVE_CALL_METHOD) { this._resolveEmptyArray(); } else { this._resolve(toResolutionValue(resolveValueIfEmpty)); } return; } this._iterate(values); }; PromiseArray.prototype._iterate = function(values) { var len = this.getActualLength(values.length); this._length = len; this._values = this.shouldCopyValues() ? new Array(len) : this._values; var result = this._promise; var isResolved = false; var bitField = null; for (var i = 0; i < len; ++i) { var maybePromise = tryConvertToPromise(values[i], result); if (maybePromise instanceof Promise) { maybePromise = maybePromise._target(); bitField = maybePromise._bitField; } else { bitField = null; } if (isResolved) { if (bitField !== null) { maybePromise.suppressUnhandledRejections(); } } else if (bitField !== null) { if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) { // Optimized for just passing the updates through maybePromise._proxy(this, i); this._values[i] = maybePromise; } else if (BIT_FIELD_CHECK(IS_FULFILLED)) { isResolved = this._promiseFulfilled(maybePromise._value(), i); } else if (BIT_FIELD_CHECK(IS_REJECTED)) { isResolved = this._promiseRejected(maybePromise._reason(), i); } else { isResolved = this._promiseCancelled(i); } } else { isResolved = this._promiseFulfilled(maybePromise, i); } ASSERT(typeof isResolved === "boolean"); } if (!isResolved) result._setAsyncGuaranteed(); }; PromiseArray.prototype._isResolved = function () { return this._values === null; }; PromiseArray.prototype._resolve = function (value) { ASSERT(!this._isResolved()); ASSERT(!(value instanceof Promise)); this._values = null; this._promise._fulfill(value); }; PromiseArray.prototype._cancel = function() { if (this._isResolved() || !this._promise._isCancellable()) return; this._values = null; this._promise._cancel(); }; PromiseArray.prototype._reject = function (reason) { ASSERT(!this._isResolved()); this._values = null; this._promise._rejectCallback(reason, false); }; PromiseArray.prototype._promiseFulfilled = function (value, index) { ASSERT(!this._isResolved()); ASSERT(isArray(this._values)); ASSERT(typeof index === "number"); this._values[index] = value; var totalResolved = ++this._totalResolved; if (totalResolved >= this._length) { this._resolve(this._values); return true; } return false; }; PromiseArray.prototype._promiseCancelled = function() { this._cancel(); return true; }; PromiseArray.prototype._promiseRejected = function (reason) { ASSERT(!this._isResolved()); ASSERT(isArray(this._values)); this._totalResolved++; this._reject(reason); return true; }; PromiseArray.prototype._resultCancelled = function() { if (this._isResolved()) return; var values = this._values; this._cancel(); if (values instanceof Promise) { values.cancel(); } else { for (var i = 0; i < values.length; ++i) { if (values[i] instanceof Promise) { values[i].cancel(); } } } }; PromiseArray.prototype.shouldCopyValues = function () { return true; }; PromiseArray.prototype.getActualLength = function (len) { return len; }; return PromiseArray; }; ================================================ FILE: src/promisify.js ================================================ "use strict"; module.exports = function(Promise, INTERNAL) { var THIS = {}; var util = require("./util"); var nodebackForPromise = require("./nodeback"); var withAppended = util.withAppended; var maybeWrapAsError = util.maybeWrapAsError; var canEvaluate = util.canEvaluate; var ASSERT = require("./assert"); var TypeError = require("./errors").TypeError; var defaultSuffix = AFTER_PROMISIFIED_SUFFIX; var defaultPromisified = {__isPromisified__: true}; var noCopyProps = [ "arity", // Firefox 4 "length", "name", "arguments", "caller", "callee", "prototype", "__isPromisified__" ]; var noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$"); var defaultFilter = function(name) { return util.isIdentifier(name) && name.charAt(0) !== "_" && name !== "constructor"; }; function propsFilter(key) { return !noCopyPropsPattern.test(key); } function isPromisified(fn) { try { return fn.__isPromisified__ === true; } catch (e) { return false; } } function hasPromisified(obj, key, suffix) { var val = util.getDataPropertyOrDefault(obj, key + suffix, defaultPromisified); return val ? isPromisified(val) : false; } function checkValid(ret, suffix, suffixRegexp) { // Verify that in the list of methods to promisify there is no // method that has a name ending in "Async"-suffix while // also having a method with the same name but no Async suffix for (var i = 0; i < ret.length; i += 2) { var key = ret[i]; if (suffixRegexp.test(key)) { var keyWithoutAsyncSuffix = key.replace(suffixRegexp, ""); for (var j = 0; j < ret.length; j += 2) { if (ret[j] === keyWithoutAsyncSuffix) { throw new TypeError(PROMISIFICATION_NORMAL_METHODS_ERROR .replace("%s", suffix)); } } } } } function promisifiableMethods(obj, suffix, suffixRegexp, filter) { var keys = util.inheritedDataKeys(obj); var ret = []; for (var i = 0; i < keys.length; ++i) { var key = keys[i]; var value = obj[key]; var passesDefaultFilter = filter === defaultFilter ? true : defaultFilter(key, value, obj); if (typeof value === "function" && !isPromisified(value) && !hasPromisified(obj, key, suffix) && filter(key, value, obj, passesDefaultFilter)) { ret.push(key, value); } } checkValid(ret, suffix, suffixRegexp); return ret; } var escapeIdentRegex = function(str) { return str.replace(/([$])/, "\\$"); }; var makeNodePromisifiedEval; if (!__BROWSER__) { //Gives an optimal sequence of argument count to try given a formal parameter //.length for a function var switchCaseArgumentOrder = function(likelyArgumentCount) { var ret = [likelyArgumentCount]; var min = Math.max(0, likelyArgumentCount - 1 - PARAM_COUNTS_TO_TRY); for(var i = likelyArgumentCount - 1; i >= min; --i) { ret.push(i); } for(var i = likelyArgumentCount + 1; i <= PARAM_COUNTS_TO_TRY; ++i) { ret.push(i); } return ret; }; var argumentSequence = function(argumentCount) { return util.filledRange(argumentCount, "_arg", ""); }; var parameterDeclaration = function(parameterCount) { return util.filledRange( Math.max(parameterCount, PARAM_COUNTS_TO_TRY), "_arg", ""); }; var parameterCount = function(fn) { if (typeof fn.length === "number") { return Math.max(Math.min(fn.length, MAX_PARAM_COUNT + 1), 0); } //Unsupported .length for functions return 0; }; makeNodePromisifiedEval = function(callback, receiver, originalName, fn, _, multiArgs) { //-1 for the callback parameter var newParameterCount = Math.max(0, parameterCount(fn) - 1); var argumentOrder = switchCaseArgumentOrder(newParameterCount); var shouldProxyThis = typeof callback === "string" || receiver === THIS; function generateCallForArgumentCount(count) { var args = argumentSequence(count).join(", "); var comma = count > 0 ? ", " : ""; var ret; if (shouldProxyThis) { ret = "ret = callback.call(this, {{args}}, nodeback); break;\n"; } else { ret = receiver === undefined ? "ret = callback({{args}}, nodeback); break;\n" : "ret = callback.call(receiver, {{args}}, nodeback); break;\n"; } return ret.replace("{{args}}", args).replace(", ", comma); } function generateArgumentSwitchCase() { var ret = ""; for (var i = 0; i < argumentOrder.length; ++i) { ret += "case " + argumentOrder[i] +":" + generateCallForArgumentCount(argumentOrder[i]); } ret += " \n\ default: \n\ var args = new Array(len + 1); \n\ var i = 0; \n\ for (var i = 0; i < len; ++i) { \n\ args[i] = arguments[i]; \n\ } \n\ args[i] = nodeback; \n\ [CodeForCall] \n\ break; \n\ ".replace("[CodeForCall]", (shouldProxyThis ? "ret = callback.apply(this, args);\n" : "ret = callback.apply(receiver, args);\n")); return ret; } var getFunctionCode = typeof callback === "string" ? ("this != null ? this['"+callback+"'] : fn") : "fn"; var body = "'use strict'; \n\ var ret = function (Parameters) { \n\ 'use strict'; \n\ var len = arguments.length; \n\ var promise = new Promise(INTERNAL); \n\ promise._captureStackTrace(); \n\ var nodeback = nodebackForPromise(promise, " + multiArgs + "); \n\ var ret; \n\ var callback = tryCatch([GetFunctionCode]); \n\ switch(len) { \n\ [CodeForSwitchCase] \n\ } \n\ if (ret === errorObj) { \n\ promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\ } \n\ if (!promise._isFateSealed()) promise._setAsyncGuaranteed(); \n\ return promise; \n\ }; \n\ notEnumerableProp(ret, '__isPromisified__', true); \n\ return ret; \n\ ".replace("[CodeForSwitchCase]", generateArgumentSwitchCase()) .replace("[GetFunctionCode]", getFunctionCode); body = body.replace("Parameters", parameterDeclaration(newParameterCount)); return new Function("Promise", "fn", "receiver", "withAppended", "maybeWrapAsError", "nodebackForPromise", "tryCatch", "errorObj", "notEnumerableProp", "INTERNAL", body)( Promise, fn, receiver, withAppended, maybeWrapAsError, nodebackForPromise, util.tryCatch, util.errorObj, util.notEnumerableProp, INTERNAL); }; } function makeNodePromisifiedClosure(callback, receiver, _, fn, __, multiArgs) { var defaultThis = (function() {return this;})(); var method = callback; if (typeof method === "string") { callback = fn; } function promisified() { var _receiver = receiver; if (receiver === THIS) _receiver = this; ASSERT(typeof callback === "function"); var promise = new Promise(INTERNAL); promise._captureStackTrace(); var cb = typeof method === "string" && this !== defaultThis ? this[method] : callback; var fn = nodebackForPromise(promise, multiArgs); try { cb.apply(_receiver, withAppended(arguments, fn)); } catch(e) { promise._rejectCallback(maybeWrapAsError(e), true, true); } if (!promise._isFateSealed()) promise._setAsyncGuaranteed(); return promise; } util.notEnumerableProp(promisified, "__isPromisified__", true); return promisified; } var makeNodePromisified = canEvaluate ? makeNodePromisifiedEval : makeNodePromisifiedClosure; function promisifyAll(obj, suffix, filter, promisifier, multiArgs) { ASSERT(typeof suffix === "string"); ASSERT(typeof filter === "function"); var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$"); var methods = promisifiableMethods(obj, suffix, suffixRegexp, filter); for (var i = 0, len = methods.length; i < len; i+= 2) { var key = methods[i]; var fn = methods[i+1]; var promisifiedKey = key + suffix; if (promisifier === makeNodePromisified) { obj[promisifiedKey] = makeNodePromisified(key, THIS, key, fn, suffix, multiArgs); } else { var promisified = promisifier(fn, function() { return makeNodePromisified(key, THIS, key, fn, suffix, multiArgs); }); util.notEnumerableProp(promisified, "__isPromisified__", true); obj[promisifiedKey] = promisified; } } util.toFastProperties(obj); return obj; } function promisify(callback, receiver, multiArgs) { return makeNodePromisified(callback, receiver, undefined, callback, null, multiArgs); } Promise.promisify = function (fn, options) { if (typeof fn !== "function") { throw new TypeError(FUNCTION_ERROR + util.classString(fn)); } if (isPromisified(fn)) { return fn; } options = Object(options); var receiver = options.context === undefined ? THIS : options.context; var multiArgs = !!options.multiArgs; var ret = promisify(fn, receiver, multiArgs); util.copyDescriptors(fn, ret, propsFilter); return ret; }; Promise.promisifyAll = function (target, options) { if (typeof target !== "function" && typeof target !== "object") { throw new TypeError(PROMISIFY_TYPE_ERROR); } options = Object(options); var multiArgs = !!options.multiArgs; var suffix = options.suffix; if (typeof suffix !== "string") suffix = defaultSuffix; var filter = options.filter; if (typeof filter !== "function") filter = defaultFilter; var promisifier = options.promisifier; if (typeof promisifier !== "function") promisifier = makeNodePromisified; if (!util.isIdentifier(suffix)) { throw new RangeError(SUFFIX_NOT_IDENTIFIER); } var keys = util.inheritedDataKeys(target); for (var i = 0; i < keys.length; ++i) { var value = target[keys[i]]; if (keys[i] !== "constructor" && util.isClass(value)) { promisifyAll(value.prototype, suffix, filter, promisifier, multiArgs); promisifyAll(value, suffix, filter, promisifier, multiArgs); } } return promisifyAll(target, suffix, filter, promisifier, multiArgs); }; }; ================================================ FILE: src/props.js ================================================ "use strict"; module.exports = function( Promise, PromiseArray, tryConvertToPromise, apiRejection) { var ASSERT = require("./assert"); var util = require("./util"); var isObject = util.isObject; var es5 = require("./es5"); var Es6Map; if (typeof Map === "function") Es6Map = Map; var mapToEntries = (function() { var index = 0; var size = 0; function extractEntry(value, key) { this[index] = value; this[index + size] = key; index++; } return function mapToEntries(map) { size = map.size; index = 0; var ret = new Array(map.size * 2); map.forEach(extractEntry, ret); return ret; }; })(); var entriesToMap = function(entries) { var ret = new Es6Map(); var length = entries.length / 2 | 0; for (var i = 0; i < length; ++i) { var key = entries[length + i]; var value = entries[i]; ret.set(key, value); } return ret; }; function PropertiesPromiseArray(obj) { var isMap = false; var entries; if (Es6Map !== undefined && obj instanceof Es6Map) { entries = mapToEntries(obj); isMap = true; } else { var keys = es5.keys(obj); var len = keys.length; entries = new Array(len * 2); for (var i = 0; i < len; ++i) { var key = keys[i]; entries[i] = obj[key]; entries[i + len] = key; } } this.constructor$(entries); this._isMap = isMap; this._init$(undefined, isMap ? RESOLVE_MAP : RESOLVE_OBJECT); } util.inherits(PropertiesPromiseArray, PromiseArray); //Override PropertiesPromiseArray.prototype._init = function () {}; //Override PropertiesPromiseArray.prototype._promiseFulfilled = function (value, index) { ASSERT(!this._isResolved()); ASSERT(!(value instanceof Promise)); this._values[index] = value; var totalResolved = ++this._totalResolved; if (totalResolved >= this._length) { var val; if (this._isMap) { val = entriesToMap(this._values); } else { val = {}; var keyOffset = this.length(); for (var i = 0, len = this.length(); i < len; ++i) { val[this._values[i + keyOffset]] = this._values[i]; } } this._resolve(val); return true; } return false; }; // Override PropertiesPromiseArray.prototype.shouldCopyValues = function () { return false; }; // Override PropertiesPromiseArray.prototype.getActualLength = function (len) { return len >> 1; }; function props(promises) { var ret; var castValue = tryConvertToPromise(promises); if (!isObject(castValue)) { return apiRejection(PROPS_TYPE_ERROR); } else if (castValue instanceof Promise) { ret = castValue._then( Promise.props, undefined, undefined, undefined, undefined); } else { ret = new PropertiesPromiseArray(castValue).promise(); } if (castValue instanceof Promise) { ret._propagateFrom(castValue, PROPAGATE_BIND); } return ret; } Promise.prototype.props = function () { return props(this); }; Promise.props = function (promises) { return props(promises); }; }; ================================================ FILE: src/queue.js ================================================ "use strict"; var ASSERT = require("./assert"); function arrayMove(src, srcIndex, dst, dstIndex, len) { for (var j = 0; j < len; ++j) { dst[j + dstIndex] = src[j + srcIndex]; src[j + srcIndex] = void 0; } } function Queue(capacity) { this._capacity = capacity; this._length = 0; this._front = 0; } Queue.prototype._willBeOverCapacity = function (size) { return this._capacity < size; }; Queue.prototype._pushOne = function (arg) { var length = this.length(); this._checkCapacity(length + 1); var i = (this._front + length) & (this._capacity - 1); this[i] = arg; this._length = length + 1; }; Queue.prototype.push = function (fn, receiver, arg) { ASSERT(arguments.length === 3); ASSERT(typeof fn === "function"); var length = this.length() + 3; if (this._willBeOverCapacity(length)) { //The fast array copies expect the //underlying array to be filled completely this._pushOne(fn); this._pushOne(receiver); this._pushOne(arg); return; } var j = this._front + length - 3; this._checkCapacity(length); var wrapMask = this._capacity - 1; this[(j + 0) & wrapMask] = fn; this[(j + 1) & wrapMask] = receiver; this[(j + 2) & wrapMask] = arg; this._length = length; }; Queue.prototype.shift = function () { ASSERT(this.length() > 0); var front = this._front, ret = this[front]; this[front] = undefined; this._front = (front + 1) & (this._capacity - 1); this._length--; return ret; }; Queue.prototype.length = function () { return this._length; }; Queue.prototype._checkCapacity = function (size) { if (this._capacity < size) { this._resizeTo(this._capacity << 1); } }; Queue.prototype._resizeTo = function (capacity) { var oldCapacity = this._capacity; this._capacity = capacity; var front = this._front; var length = this._length; var moveItemsCount = (front + length) & (oldCapacity - 1); arrayMove(this, 0, this, oldCapacity, moveItemsCount); }; module.exports = Queue; ================================================ FILE: src/race.js ================================================ "use strict"; module.exports = function( Promise, INTERNAL, tryConvertToPromise, apiRejection) { var util = require("./util"); var raceLater = function (promise) { return promise.then(function(array) { return race(array, promise); }); }; function race(promises, parent) { var maybePromise = tryConvertToPromise(promises); if (maybePromise instanceof Promise) { return raceLater(maybePromise); } else { promises = util.asArray(promises); if (promises === null) return apiRejection(COLLECTION_ERROR + util.classString(promises)); } var ret = new Promise(INTERNAL); if (parent !== undefined) { ret._propagateFrom(parent, PROPAGATE_ALL); } var fulfill = ret._fulfill; var reject = ret._reject; for (var i = 0, len = promises.length; i < len; ++i) { var val = promises[i]; if (val === undefined && !(i in promises)) { continue; } Promise.cast(val)._then(fulfill, reject, undefined, ret, null); } //Yes, if promises were empty, it will be forever pending :-) return ret; } Promise.race = function (promises) { return race(promises, undefined); }; Promise.prototype.race = function () { return race(this, undefined); }; }; ================================================ FILE: src/reduce.js ================================================ "use strict"; module.exports = function(Promise, PromiseArray, apiRejection, tryConvertToPromise, INTERNAL, debug) { var util = require("./util"); var tryCatch = util.tryCatch; function ReductionPromiseArray(promises, fn, initialValue, _each) { this.constructor$(promises); var context = Promise._getContext(); this._fn = util.contextBind(context, fn); if (initialValue !== undefined) { initialValue = Promise.resolve(initialValue); initialValue._attachCancellationCallback(this); } this._initialValue = initialValue; this._currentCancellable = null; if(_each === INTERNAL) { this._eachValues = Array(this._length); } else if (_each === 0) { this._eachValues = null; } else { this._eachValues = undefined; } this._promise._captureStackTrace(); this._init$(undefined, RESOLVE_CALL_METHOD); } util.inherits(ReductionPromiseArray, PromiseArray); ReductionPromiseArray.prototype._gotAccum = function(accum) { if (this._eachValues !== undefined && this._eachValues !== null && accum !== INTERNAL) { this._eachValues.push(accum); } }; ReductionPromiseArray.prototype._eachComplete = function(value) { if (this._eachValues !== null) { this._eachValues.push(value); } return this._eachValues; }; // Override ReductionPromiseArray.prototype._init = function() {}; // Override ReductionPromiseArray.prototype._resolveEmptyArray = function() { this._resolve(this._eachValues !== undefined ? this._eachValues : this._initialValue); }; // Override ReductionPromiseArray.prototype.shouldCopyValues = function () { return false; }; // Override ReductionPromiseArray.prototype._resolve = function(value) { this._promise._resolveCallback(value); this._values = null; }; // Override ReductionPromiseArray.prototype._resultCancelled = function(sender) { if (sender === this._initialValue) return this._cancel(); if (this._isResolved()) return; this._resultCancelled$(); if (this._currentCancellable instanceof Promise) { this._currentCancellable.cancel(); } if (this._initialValue instanceof Promise) { this._initialValue.cancel(); } }; // Override ReductionPromiseArray.prototype._iterate = function (values) { this._values = values; var value; var i; var length = values.length; if (this._initialValue !== undefined) { value = this._initialValue; i = 0; } else { value = Promise.resolve(values[0]); i = 1; } this._currentCancellable = value; for (var j = i; j < length; ++j) { var maybePromise = values[j]; if (maybePromise instanceof Promise) { maybePromise.suppressUnhandledRejections(); } } if (!value.isRejected()) { for (; i < length; ++i) { var ctx = { accum: null, value: values[i], index: i, length: length, array: this }; value = value._then(gotAccum, undefined, undefined, ctx, undefined); // Too many promises chained with asyncGuaranteed will result in // stack overflow. Break up long chains to reset stack. if ((i & 127) === 0) { value._setNoAsyncGuarantee(); } } } if (this._eachValues !== undefined) { value = value ._then(this._eachComplete, undefined, undefined, this, undefined); } value._then(completed, completed, undefined, value, this); }; Promise.prototype.reduce = function (fn, initialValue) { return reduce(this, fn, initialValue, null); }; Promise.reduce = function (promises, fn, initialValue, _each) { return reduce(promises, fn, initialValue, _each); }; function completed(valueOrReason, array) { if (this.isFulfilled()) { array._resolve(valueOrReason); } else { array._reject(valueOrReason); } } function reduce(promises, fn, initialValue, _each) { if (typeof fn !== "function") { return apiRejection(FUNCTION_ERROR + util.classString(fn)); } var array = new ReductionPromiseArray(promises, fn, initialValue, _each); return array.promise(); } function gotAccum(accum) { this.accum = accum; this.array._gotAccum(accum); var value = tryConvertToPromise(this.value, this.array._promise); if (value instanceof Promise) { this.array._currentCancellable = value; return value._then(gotValue, undefined, undefined, this, undefined); } else { return gotValue.call(this, value); } } function gotValue(value) { var array = this.array; var promise = array._promise; var fn = tryCatch(array._fn); promise._pushContext(); var ret; if (array._eachValues !== undefined) { ret = fn.call(promise._boundValue(), value, this.index, this.length); } else { ret = fn.call(promise._boundValue(), this.accum, value, this.index, this.length); } if (ret instanceof Promise) { array._currentCancellable = ret; } var promiseCreated = promise._popContext(); debug.checkForgottenReturns( ret, promiseCreated, array._eachValues !== undefined ? "Promise.each" : "Promise.reduce", promise ); return ret; } }; ================================================ FILE: src/schedule.js ================================================ "use strict"; var util = require("./util"); var schedule; var noAsyncScheduler = function() { throw new Error(NO_ASYNC_SCHEDULER); }; var NativePromise = util.getNativePromise(); // This file figures out which scheduler to use for Bluebird. It normalizes // async task scheduling across target platforms. Note that not all JS target // platforms come supported. The scheduler is overridable with `setScheduler`. // Our scheduler for Node.js/io.js is setImmediate for recent // versions of node because of macrotask semantics. // The `typeof` check is for an edge case with nw.js. if (util.isNode && typeof MutationObserver === "undefined") { var GlobalSetImmediate = global.setImmediate; var ProcessNextTick = process.nextTick; schedule = util.isRecentNode ? function(fn) { GlobalSetImmediate.call(global, fn); } : function(fn) { ProcessNextTick.call(process, fn); }; } else if (typeof NativePromise === "function" && typeof NativePromise.resolve === "function") { var nativePromise = NativePromise.resolve(); schedule = function(fn) { nativePromise.then(fn); }; // Outside of Node, we're using MutationObservers because they provide low // latency. The second check is to guard against iOS standalone apps which // do not fire DOM mutation events for some reason on iOS 8.3+ and cordova // apps which have the same bug but are not `.navigator.standalone` } else if ((typeof MutationObserver !== "undefined") && !(typeof window !== "undefined" && window.navigator && (window.navigator.standalone || window.cordova)) && ("classList" in document.documentElement)) { schedule = (function() { // Using 2 mutation observers to batch multiple updates into one. var div = document.createElement("div"); var opts = {attributes: true}; var toggleScheduled = false; var div2 = document.createElement("div"); var o2 = new MutationObserver(function() { div.classList.toggle("foo"); toggleScheduled = false; }); o2.observe(div2, opts); var scheduleToggle = function() { if (toggleScheduled) return; toggleScheduled = true; div2.classList.toggle("foo"); }; return function schedule(fn) { var o = new MutationObserver(function() { o.disconnect(); fn(); }); o.observe(div, opts); scheduleToggle(); }; })(); // setImmediate has higher latency but is still pretty good. This is useful for // cases where MutationObserver is not defined (older IE, for example). } else if (typeof setImmediate !== "undefined") { schedule = function (fn) { setImmediate(fn); }; // setTimeout also works, it has the most latency but it does the trick. } else if (typeof setTimeout !== "undefined") { schedule = function (fn) { setTimeout(fn, 0); }; } else { // Do __Not__ default to a sync scheduler, that would break Promises/A+ // compliancy and cause race conditions. schedule = noAsyncScheduler; } module.exports = schedule; ================================================ FILE: src/settle.js ================================================ "use strict"; module.exports = function(Promise, PromiseArray, debug) { var ASSERT = require("./assert"); var PromiseInspection = Promise.PromiseInspection; var util = require("./util"); function SettledPromiseArray(values) { this.constructor$(values); } util.inherits(SettledPromiseArray, PromiseArray); SettledPromiseArray.prototype._promiseResolved = function (index, inspection) { ASSERT(typeof index === "number"); this._values[index] = inspection; var totalResolved = ++this._totalResolved; if (totalResolved >= this._length) { this._resolve(this._values); return true; } return false; }; //override SettledPromiseArray.prototype._promiseFulfilled = function (value, index) { ASSERT(!this._isResolved()); ASSERT(typeof index === "number"); var ret = new PromiseInspection(); ret._bitField = IS_FULFILLED; ret._settledValueField = value; return this._promiseResolved(index, ret); }; //override SettledPromiseArray.prototype._promiseRejected = function (reason, index) { ASSERT(!this._isResolved()); ASSERT(typeof index === "number"); var ret = new PromiseInspection(); ret._bitField = IS_REJECTED; ret._settledValueField = reason; return this._promiseResolved(index, ret); }; Promise.settle = function (promises) { debug.deprecated(".settle()", ".reflect()"); return new SettledPromiseArray(promises).promise(); }; Promise.allSettled = function (promises) { return new SettledPromiseArray(promises).promise(); }; Promise.prototype.settle = function () { return Promise.settle(this); }; }; ================================================ FILE: src/some.js ================================================ "use strict"; module.exports = function(Promise, PromiseArray, apiRejection) { var ASSERT = require("./assert"); var util = require("./util"); var RangeError = require("./errors").RangeError; var AggregateError = require("./errors").AggregateError; var isArray = util.isArray; var CANCELLATION = {}; function SomePromiseArray(values) { this.constructor$(values); this._howMany = 0; this._unwrap = false; this._initialized = false; } util.inherits(SomePromiseArray, PromiseArray); SomePromiseArray.prototype._init = function () { if (!this._initialized) { return; } if (this._howMany === 0) { this._resolve([]); return; } this._init$(undefined, RESOLVE_CALL_METHOD); var isArrayResolved = isArray(this._values); if (!this._isResolved() && isArrayResolved && this._howMany > this._canPossiblyFulfill()) { this._reject(this._getRangeError(this.length())); } }; SomePromiseArray.prototype.init = function () { this._initialized = true; this._init(); }; SomePromiseArray.prototype.setUnwrap = function () { this._unwrap = true; }; SomePromiseArray.prototype.howMany = function () { return this._howMany; }; SomePromiseArray.prototype.setHowMany = function (count) { ASSERT(!this._isResolved()); this._howMany = count; }; //override SomePromiseArray.prototype._promiseFulfilled = function (value) { ASSERT(!this._isResolved()); this._addFulfilled(value); if (this._fulfilled() === this.howMany()) { this._values.length = this.howMany(); if (this.howMany() === 1 && this._unwrap) { this._resolve(this._values[0]); } else { this._resolve(this._values); } return true; } return false; }; //override SomePromiseArray.prototype._promiseRejected = function (reason) { ASSERT(!this._isResolved()); this._addRejected(reason); return this._checkOutcome(); }; //override SomePromiseArray.prototype._promiseCancelled = function () { if (this._values instanceof Promise || this._values == null) { return this._cancel(); } ASSERT(!this._isResolved()); this._addRejected(CANCELLATION); return this._checkOutcome(); }; SomePromiseArray.prototype._checkOutcome = function() { if (this.howMany() > this._canPossiblyFulfill()) { var e = new AggregateError(); for (var i = this.length(); i < this._values.length; ++i) { if (this._values[i] !== CANCELLATION) { e.push(this._values[i]); } } if (e.length > 0) { this._reject(e); } else { this._cancel(); } return true; } return false; }; SomePromiseArray.prototype._fulfilled = function () { return this._totalResolved; }; SomePromiseArray.prototype._rejected = function () { return this._values.length - this.length(); }; //Use the same array past .length() to store rejection reasons SomePromiseArray.prototype._addRejected = function (reason) { this._values.push(reason); }; SomePromiseArray.prototype._addFulfilled = function (value) { this._values[this._totalResolved++] = value; }; SomePromiseArray.prototype._canPossiblyFulfill = function () { return this.length() - this._rejected(); }; SomePromiseArray.prototype._getRangeError = function (count) { var message = "Input array must contain at least " + this._howMany + " items but contains only " + count + " items"; return new RangeError(message); }; SomePromiseArray.prototype._resolveEmptyArray = function () { this._reject(this._getRangeError(0)); }; function some(promises, howMany) { if ((howMany | 0) !== howMany || howMany < 0) { return apiRejection(POSITIVE_INTEGER_ERROR); } var ret = new SomePromiseArray(promises); var promise = ret.promise(); ASSERT(promise.isPending()); ASSERT(ret instanceof SomePromiseArray); ret.setHowMany(howMany); ret.init(); return promise; } Promise.some = function (promises, howMany) { return some(promises, howMany); }; Promise.prototype.some = function (howMany) { return some(this, howMany); }; Promise._SomePromiseArray = SomePromiseArray; }; ================================================ FILE: src/synchronous_inspection.js ================================================ "use strict"; module.exports = function(Promise) { function PromiseInspection(promise) { if (promise !== undefined) { promise = promise._target(); this._bitField = promise._bitField; this._settledValueField = promise._isFateSealed() ? promise._settledValue() : undefined; } else { this._bitField = 0; this._settledValueField = undefined; } } PromiseInspection.prototype._settledValue = function() { return this._settledValueField; }; var value = PromiseInspection.prototype.value = function () { if (!this.isFulfilled()) { throw new TypeError(INSPECTION_VALUE_ERROR); } return this._settledValue(); }; var reason = PromiseInspection.prototype.error = PromiseInspection.prototype.reason = function () { if (!this.isRejected()) { throw new TypeError(INSPECTION_REASON_ERROR); } return this._settledValue(); }; var isFulfilled = PromiseInspection.prototype.isFulfilled = function() { return (this._bitField & IS_FULFILLED) !== 0; }; var isRejected = PromiseInspection.prototype.isRejected = function () { return (this._bitField & IS_REJECTED) !== 0; }; var isPending = PromiseInspection.prototype.isPending = function () { return (this._bitField & IS_REJECTED_OR_FULFILLED_OR_CANCELLED) === 0; }; var isResolved = PromiseInspection.prototype.isResolved = function () { return (this._bitField & IS_REJECTED_OR_FULFILLED) !== 0; }; PromiseInspection.prototype.isCancelled = function() { return (this._bitField & IS_CANCELLED_OR_WILL_BE_CANCELLED) !== 0; }; Promise.prototype.__isCancelled = function() { return (this._bitField & IS_CANCELLED) === IS_CANCELLED; }; Promise.prototype._isCancelled = function() { return this._target().__isCancelled(); }; Promise.prototype.isCancelled = function() { return (this._target()._bitField & IS_CANCELLED_OR_WILL_BE_CANCELLED) !== 0; }; Promise.prototype.isPending = function() { return isPending.call(this._target()); }; Promise.prototype.isRejected = function() { return isRejected.call(this._target()); }; Promise.prototype.isFulfilled = function() { return isFulfilled.call(this._target()); }; Promise.prototype.isResolved = function() { return isResolved.call(this._target()); }; Promise.prototype.value = function() { return value.call(this._target()); }; Promise.prototype.reason = function() { var target = this._target(); target._unsetRejectionIsUnhandled(); return reason.call(target); }; Promise.prototype._value = function() { return this._settledValue(); }; Promise.prototype._reason = function() { this._unsetRejectionIsUnhandled(); return this._settledValue(); }; Promise.PromiseInspection = PromiseInspection; }; ================================================ FILE: src/thenables.js ================================================ "use strict"; module.exports = function(Promise, INTERNAL) { var ASSERT = require("./assert"); var util = require("./util"); var errorObj = util.errorObj; var isObject = util.isObject; function tryConvertToPromise(obj, context) { if (isObject(obj)) { if (obj instanceof Promise) return obj; var then = getThen(obj); if (then === errorObj) { if (context) context._pushContext(); var ret = Promise.reject(then.e); if (context) context._popContext(); return ret; } else if (typeof then === "function") { //Make casting from another bluebird fast if (isAnyBluebirdPromise(obj)) { var ret = new Promise(INTERNAL); obj._then( ret._fulfill, ret._reject, undefined, ret, null ); return ret; } return doThenable(obj, then, context); } } return obj; } function doGetThen(obj) { return obj.then; } function getThen(obj) { try { return doGetThen(obj); } catch (e) { errorObj.e = e; return errorObj; } } var hasProp = {}.hasOwnProperty; function isAnyBluebirdPromise(obj) { try { return hasProp.call(obj, "_promise0"); } catch (e) { return false; } } function doThenable(x, then, context) { ASSERT(typeof then === "function"); var promise = new Promise(INTERNAL); var ret = promise; if (context) context._pushContext(); promise._captureStackTrace(); if (context) context._popContext(); var synchronous = true; var result = util.tryCatch(then).call(x, resolve, reject); synchronous = false; if (promise && result === errorObj) { promise._rejectCallback(result.e, true, true); promise = null; } function resolve(value) { if (!promise) return; promise._resolveCallback(value); promise = null; } function reject(reason) { if (!promise) return; promise._rejectCallback(reason, synchronous, true); promise = null; } return ret; } return tryConvertToPromise; }; ================================================ FILE: src/timers.js ================================================ "use strict"; module.exports = function(Promise, INTERNAL, debug) { var util = require("./util"); var TimeoutError = Promise.TimeoutError; function HandleWrapper(handle) { this.handle = handle; } HandleWrapper.prototype._resultCancelled = function() { clearTimeout(this.handle); }; var afterValue = function(value) { return delay(+this).thenReturn(value); }; var delay = Promise.delay = function (ms, value) { var ret; var handle; if (value !== undefined) { ret = Promise.resolve(value) ._then(afterValue, null, null, ms, undefined); if (debug.cancellation() && value instanceof Promise) { ret._setOnCancel(value); } } else { ret = new Promise(INTERNAL); handle = setTimeout(function() { ret._fulfill(); }, +ms); if (debug.cancellation()) { ret._setOnCancel(new HandleWrapper(handle)); } ret._captureStackTrace(); } ret._setAsyncGuaranteed(); return ret; }; Promise.prototype.delay = function (ms) { return delay(ms, this); }; var afterTimeout = function (promise, message, parent) { var err; if (typeof message !== "string") { if (message instanceof Error) { err = message; } else { err = new TimeoutError(TIMEOUT_ERROR); } } else { err = new TimeoutError(message); } util.markAsOriginatingFromRejection(err); promise._attachExtraTrace(err); promise._reject(err); if (parent != null) { parent.cancel(); } }; function successClear(value) { clearTimeout(this.handle); return value; } function failureClear(reason) { clearTimeout(this.handle); throw reason; } Promise.prototype.timeout = function (ms, message) { ms = +ms; var ret, parent; var handleWrapper = new HandleWrapper(setTimeout(function timeoutTimeout() { if (ret.isPending()) { afterTimeout(ret, message, parent); } }, ms)); if (debug.cancellation()) { parent = this.then(); ret = parent._then(successClear, failureClear, undefined, handleWrapper, undefined); ret._setOnCancel(handleWrapper); } else { ret = this._then(successClear, failureClear, undefined, handleWrapper, undefined); } return ret; }; }; ================================================ FILE: src/using.js ================================================ "use strict"; module.exports = function (Promise, apiRejection, tryConvertToPromise, createContext, INTERNAL, debug) { var util = require("./util"); var TypeError = require("./errors").TypeError; var inherits = require("./util").inherits; var errorObj = util.errorObj; var tryCatch = util.tryCatch; var NULL = {}; function thrower(e) { setTimeout(function(){throw e;}, 0); } function castPreservingDisposable(thenable) { var maybePromise = tryConvertToPromise(thenable); if (maybePromise !== thenable && typeof thenable._isDisposable === "function" && typeof thenable._getDisposer === "function" && thenable._isDisposable()) { maybePromise._setDisposable(thenable._getDisposer()); } return maybePromise; } function dispose(resources, inspection) { var i = 0; var len = resources.length; var ret = new Promise(INTERNAL); function iterator() { if (i >= len) return ret._fulfill(); var maybePromise = castPreservingDisposable(resources[i++]); if (maybePromise instanceof Promise && maybePromise._isDisposable()) { try { maybePromise = tryConvertToPromise( maybePromise._getDisposer().tryDispose(inspection), resources.promise); } catch (e) { return thrower(e); } if (maybePromise instanceof Promise) { return maybePromise._then(iterator, thrower, null, null, null); } } iterator(); } iterator(); return ret; } function Disposer(data, promise, context) { this._data = data; this._promise = promise; this._context = context; } Disposer.prototype.data = function () { return this._data; }; Disposer.prototype.promise = function () { return this._promise; }; Disposer.prototype.resource = function () { if (this.promise().isFulfilled()) { return this.promise().value(); } return NULL; }; Disposer.prototype.tryDispose = function(inspection) { var resource = this.resource(); var context = this._context; if (context !== undefined) context._pushContext(); var ret = resource !== NULL ? this.doDispose(resource, inspection) : null; if (context !== undefined) context._popContext(); this._promise._unsetDisposable(); this._data = null; return ret; }; Disposer.isDisposer = function (d) { return (d != null && typeof d.resource === "function" && typeof d.tryDispose === "function"); }; function FunctionDisposer(fn, promise, context) { this.constructor$(fn, promise, context); } inherits(FunctionDisposer, Disposer); FunctionDisposer.prototype.doDispose = function (resource, inspection) { var fn = this.data(); return fn.call(resource, resource, inspection); }; function maybeUnwrapDisposer(value) { if (Disposer.isDisposer(value)) { this.resources[this.index]._setDisposable(value); return value.promise(); } return value; } function ResourceList(length) { this.length = length; this.promise = null; this[length-1] = null; } ResourceList.prototype._resultCancelled = function() { var len = this.length; for (var i = 0; i < len; ++i) { var item = this[i]; if (item instanceof Promise) { item.cancel(); } } }; Promise.using = function () { var len = arguments.length; if (len < 2) return apiRejection( "you must pass at least 2 arguments to Promise.using"); var fn = arguments[len - 1]; if (typeof fn !== "function") { return apiRejection(FUNCTION_ERROR + util.classString(fn)); } var input; var spreadArgs = true; if (len === 2 && Array.isArray(arguments[0])) { input = arguments[0]; len = input.length; spreadArgs = false; } else { input = arguments; len--; } var resources = new ResourceList(len); for (var i = 0; i < len; ++i) { var resource = input[i]; if (Disposer.isDisposer(resource)) { var disposer = resource; resource = resource.promise(); resource._setDisposable(disposer); } else { var maybePromise = tryConvertToPromise(resource); if (maybePromise instanceof Promise) { resource = maybePromise._then(maybeUnwrapDisposer, null, null, { resources: resources, index: i }, undefined); } } resources[i] = resource; } var reflectedResources = new Array(resources.length); for (var i = 0; i < reflectedResources.length; ++i) { reflectedResources[i] = Promise.resolve(resources[i]).reflect(); } var resultPromise = Promise.all(reflectedResources) .then(function(inspections) { for (var i = 0; i < inspections.length; ++i) { var inspection = inspections[i]; if (inspection.isRejected()) { errorObj.e = inspection.error(); return errorObj; } else if (!inspection.isFulfilled()) { resultPromise.cancel(); return; } inspections[i] = inspection.value(); } promise._pushContext(); fn = tryCatch(fn); var ret = spreadArgs ? fn.apply(undefined, inspections) : fn(inspections); var promiseCreated = promise._popContext(); debug.checkForgottenReturns( ret, promiseCreated, "Promise.using", promise); return ret; }); var promise = resultPromise.lastly(function() { var inspection = new Promise.PromiseInspection(resultPromise); return dispose(resources, inspection); }); resources.promise = promise; promise._setOnCancel(resources); return promise; }; Promise.prototype._setDisposable = function (disposer) { this._bitField = this._bitField | IS_DISPOSABLE; this._disposer = disposer; }; Promise.prototype._isDisposable = function () { return (this._bitField & IS_DISPOSABLE) > 0; }; Promise.prototype._getDisposer = function () { return this._disposer; }; Promise.prototype._unsetDisposable = function () { this._bitField = this._bitField & (~IS_DISPOSABLE); this._disposer = undefined; }; Promise.prototype.disposer = function (fn) { if (typeof fn === "function") { return new FunctionDisposer(fn, this, createContext()); } throw new TypeError(); }; }; ================================================ FILE: src/util.js ================================================ "use strict"; var ASSERT = require("./assert"); var es5 = require("./es5"); // Assume CSP if browser var canEvaluate = typeof navigator == "undefined"; //Try catch is not supported in optimizing //compiler, so it is isolated var errorObj = {e: {}}; var tryCatchTarget; var globalObject = typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : this !== undefined ? this : null; function tryCatcher() { try { var target = tryCatchTarget; tryCatchTarget = null; return target.apply(this, arguments); } catch (e) { errorObj.e = e; return errorObj; } } function tryCatch(fn) { ASSERT(typeof fn === "function"); tryCatchTarget = fn; return tryCatcher; } //Un-magical enough that using this doesn't prevent //extending classes from outside using any convention var inherits = function(Child, Parent) { var hasProp = {}.hasOwnProperty; function T() { this.constructor = Child; this.constructor$ = Parent; for (var propertyName in Parent.prototype) { if (hasProp.call(Parent.prototype, propertyName) && propertyName.charAt(propertyName.length-1) !== "$" ) { this[propertyName + "$"] = Parent.prototype[propertyName]; } } } T.prototype = Parent.prototype; Child.prototype = new T(); return Child.prototype; }; function isPrimitive(val) { return val == null || val === true || val === false || typeof val === "string" || typeof val === "number"; } function isObject(value) { return typeof value === "function" || typeof value === "object" && value !== null; } function maybeWrapAsError(maybeError) { if (!isPrimitive(maybeError)) return maybeError; return new Error(safeToString(maybeError)); } function withAppended(target, appendee) { var len = target.length; var ret = new Array(len + 1); var i; for (i = 0; i < len; ++i) { ret[i] = target[i]; } ret[i] = appendee; return ret; } function getDataPropertyOrDefault(obj, key, defaultValue) { if (es5.isES5) { var desc = Object.getOwnPropertyDescriptor(obj, key); if (desc != null) { return desc.get == null && desc.set == null ? desc.value : defaultValue; } } else { return {}.hasOwnProperty.call(obj, key) ? obj[key] : undefined; } } function notEnumerableProp(obj, name, value) { if (isPrimitive(obj)) return obj; var descriptor = { value: value, configurable: true, enumerable: false, writable: true }; es5.defineProperty(obj, name, descriptor); return obj; } function thrower(r) { throw r; } var inheritedDataKeys = (function() { var excludedPrototypes = [ Array.prototype, Object.prototype, Function.prototype ]; var isExcludedProto = function(val) { for (var i = 0; i < excludedPrototypes.length; ++i) { if (excludedPrototypes[i] === val) { return true; } } return false; }; if (es5.isES5) { var getKeys = Object.getOwnPropertyNames; return function(obj) { var ret = []; var visitedKeys = Object.create(null); while (obj != null && !isExcludedProto(obj)) { var keys; try { keys = getKeys(obj); } catch (e) { return ret; } for (var i = 0; i < keys.length; ++i) { var key = keys[i]; if (visitedKeys[key]) continue; visitedKeys[key] = true; var desc = Object.getOwnPropertyDescriptor(obj, key); if (desc != null && desc.get == null && desc.set == null) { ret.push(key); } } obj = es5.getPrototypeOf(obj); } return ret; }; } else { var hasProp = {}.hasOwnProperty; return function(obj) { if (isExcludedProto(obj)) return []; var ret = []; /*jshint forin:false */ enumeration: for (var key in obj) { if (hasProp.call(obj, key)) { ret.push(key); } else { for (var i = 0; i < excludedPrototypes.length; ++i) { if (hasProp.call(excludedPrototypes[i], key)) { continue enumeration; } } ret.push(key); } } return ret; }; } })(); var thisAssignmentPattern = /this\s*\.\s*\S+\s*=/; function isClass(fn) { try { if (typeof fn === "function") { var keys = es5.names(fn.prototype); var hasMethods = es5.isES5 && keys.length > 1; var hasMethodsOtherThanConstructor = keys.length > 0 && !(keys.length === 1 && keys[0] === "constructor"); var hasThisAssignmentAndStaticMethods = thisAssignmentPattern.test(fn + "") && es5.names(fn).length > 0; if (hasMethods || hasMethodsOtherThanConstructor || hasThisAssignmentAndStaticMethods) { return true; } } return false; } catch (e) { return false; } } var fastProto = null; var kInlineCacheCutoff = 10; function FastObject(o) { if (fastProto !== null && typeof fastProto.property) { var result = fastProto; fastProto = FastObject.prototype = null; return result; } fastProto = FastObject.prototype = o == null ? Object.create(null) : o; return new FastObject(); } // Initialize the inline property cache of FastObject for(var i = 0; i <= kInlineCacheCutoff; i++) { FastObject({}); } function toFastProperties(obj) { FastObject(obj); ASSERT("%HasFastProperties", true, obj); return obj; } var rident = /^[a-z$_][a-z$_0-9]*$/i; function isIdentifier(str) { return rident.test(str); } function filledRange(count, prefix, suffix) { var ret = new Array(count); for(var i = 0; i < count; ++i) { ret[i] = prefix + i + suffix; } return ret; } function safeToString(obj) { try { return obj + ""; } catch (e) { return "[no string representation]"; } } function isError(obj) { return obj instanceof Error || (obj !== null && typeof obj === "object" && typeof obj.message === "string" && typeof obj.name === "string"); } function markAsOriginatingFromRejection(e) { try { notEnumerableProp(e, OPERATIONAL_ERROR_KEY, true); } catch(ignore) {} } function originatesFromRejection(e) { if (e == null) return false; return ((e instanceof Error[BLUEBIRD_ERRORS].OperationalError) || e[OPERATIONAL_ERROR_KEY] === true); } function canAttachTrace(obj) { return isError(obj) && es5.propertyIsWritable(obj, "stack"); } var ensureErrorObject = (function() { if (!("stack" in new Error())) { return function(value) { if (canAttachTrace(value)) return value; try {throw new Error(safeToString(value));} catch(err) {return err;} }; } else { return function(value) { if (canAttachTrace(value)) return value; return new Error(safeToString(value)); }; } })(); function classString(obj) { return {}.toString.call(obj); } function copyDescriptors(from, to, filter) { var keys = es5.names(from); for (var i = 0; i < keys.length; ++i) { var key = keys[i]; if (filter(key)) { try { es5.defineProperty(to, key, es5.getDescriptor(from, key)); } catch (ignore) {} } } } var asArray = function(v) { if (es5.isArray(v)) { return v; } return null; }; if (typeof Symbol !== "undefined" && Symbol.iterator) { var ArrayFrom = typeof Array.from === "function" ? function(v) { return Array.from(v); } : function(v) { var ret = []; var it = v[Symbol.iterator](); var itResult; while (!((itResult = it.next()).done)) { ret.push(itResult.value); } return ret; }; asArray = function(v) { if (es5.isArray(v)) { return v; } else if (v != null && typeof v[Symbol.iterator] === "function") { return ArrayFrom(v); } return null; }; } var isNode = typeof process !== "undefined" && classString(process).toLowerCase() === "[object process]"; var hasEnvVariables = typeof process !== "undefined" && typeof process.env !== "undefined"; function env(key) { return hasEnvVariables ? process.env[key] : undefined; } function getNativePromise() { if (typeof Promise === "function") { try { var promise = new Promise(function(){}); if (classString(promise) === "[object Promise]") { return Promise; } } catch (e) {} } } var reflectHandler; function contextBind(ctx, cb) { if (ctx === null || typeof cb !== "function" || cb === reflectHandler) { return cb; } if (ctx.domain !== null) { cb = ctx.domain.bind(cb); } var async = ctx.async; if (async !== null) { var old = cb; cb = function() { INLINE_SLICE_LEFT_PADDED(2, args, arguments); args[0] = old; args[1] = this; return async.runInAsyncScope.apply(async, args); }; } return cb; } var ret = { setReflectHandler: function(fn) { reflectHandler = fn; }, isClass: isClass, isIdentifier: isIdentifier, inheritedDataKeys: inheritedDataKeys, getDataPropertyOrDefault: getDataPropertyOrDefault, thrower: thrower, isArray: es5.isArray, asArray: asArray, notEnumerableProp: notEnumerableProp, isPrimitive: isPrimitive, isObject: isObject, isError: isError, canEvaluate: canEvaluate, errorObj: errorObj, tryCatch: tryCatch, inherits: inherits, withAppended: withAppended, maybeWrapAsError: maybeWrapAsError, toFastProperties: toFastProperties, filledRange: filledRange, toString: safeToString, canAttachTrace: canAttachTrace, ensureErrorObject: ensureErrorObject, originatesFromRejection: originatesFromRejection, markAsOriginatingFromRejection: markAsOriginatingFromRejection, classString: classString, copyDescriptors: copyDescriptors, isNode: isNode, hasEnvVariables: hasEnvVariables, env: env, global: globalObject, getNativePromise: getNativePromise, contextBind: contextBind }; ret.isRecentNode = ret.isNode && (function() { var version; if (process.versions && process.versions.node) { version = process.versions.node.split(".").map(Number); } else if (process.version) { version = process.version.split(".").map(Number); } return (version[0] === 0 && version[1] > 10) || (version[0] > 0); })(); ret.nodeSupportsAsyncResource = ret.isNode && (function() { var supportsAsync = false; try { var res = require("async_hooks").AsyncResource; supportsAsync = typeof res.prototype.runInAsyncScope === "function"; } catch (e) { supportsAsync = false; } return supportsAsync; })(); if (ret.isNode) ret.toFastProperties(process); try {throw new Error(); } catch (e) {ret.lastLineError = e;} module.exports = ret; ================================================ FILE: test/browser/es5-sham.js ================================================ // Copyright 2009-2012 by contributors, MIT License // vim: ts=4 sts=4 sw=4 expandtab // Module systems magic dance (function (definition) { // RequireJS if (typeof define == "function") { define(definition); // YUI3 } else if (typeof YUI == "function") { YUI.add("es5-sham", definition); // CommonJS and ================================================ FILE: test/browser/json.js ================================================ /*! JSON v3.3.0 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */ (function(n){function K(p,q){function s(a){if(s[a]!==v)return s[a];var c;if("bug-string-char-index"==a)c="a"!="a"[0];else if("json"==a)c=s("json-stringify")&&s("json-parse");else{var e;if("json-stringify"==a){c=q.stringify;var b="function"==typeof c&&r;if(b){(e=function(){return 1}).toJSON=e;try{b="0"===c(0)&&"0"===c(new A)&&'""'==c(new B)&&c(t)===v&&c(v)===v&&c()===v&&"1"===c(e)&&"[1]"==c([e])&&"[null]"==c([v])&&"null"==c(null)&&"[null,null,null]"==c([v,t,null])&&'{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'== c({a:[e,!0,!1,null,"\x00\b\n\f\r\t"]})&&"1"===c(null,e)&&"[\n 1,\n 2\n]"==c([1,2],null,1)&&'"-271821-04-20T00:00:00.000Z"'==c(new D(-864E13))&&'"+275760-09-13T00:00:00.000Z"'==c(new D(864E13))&&'"-000001-01-01T00:00:00.000Z"'==c(new D(-621987552E5))&&'"1969-12-31T23:59:59.999Z"'==c(new D(-1))}catch(f){b=!1}}c=b}if("json-parse"==a){c=q.parse;if("function"==typeof c)try{if(0===c("0")&&!c(!1)){e=c('{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}');var l=5==e.a.length&&1===e.a[0];if(l){try{l=!c('"\t"')}catch(d){}if(l)try{l= 1!==c("01")}catch(h){}if(l)try{l=1!==c("1.")}catch(m){}}}}catch(X){l=!1}c=l}}return s[a]=!!c}p||(p=n.Object());q||(q=n.Object());var A=p.Number||n.Number,B=p.String||n.String,G=p.Object||n.Object,D=p.Date||n.Date,J=p.SyntaxError||n.SyntaxError,N=p.TypeError||n.TypeError,R=p.Math||n.Math,H=p.JSON||n.JSON;"object"==typeof H&&H&&(q.stringify=H.stringify,q.parse=H.parse);var G=G.prototype,t=G.toString,u,C,v,r=new D(-0xc782b5b800cec);try{r=-109252==r.getUTCFullYear()&&0===r.getUTCMonth()&&1===r.getUTCDate()&& 10==r.getUTCHours()&&37==r.getUTCMinutes()&&6==r.getUTCSeconds()&&708==r.getUTCMilliseconds()}catch(Y){}if(!s("json")){var E=s("bug-string-char-index");if(!r)var w=R.floor,S=[0,31,59,90,120,151,181,212,243,273,304,334],F=function(a,c){return S[c]+365*(a-1970)+w((a-1969+(c=+(1d){c+="\\u00"+x(2,d.toString(16));break}c+=f?l[b]:a.charAt(b)}}return c+'"'},L=function(a,c,b,g,f,l,d){var h,m,k,n,p,q,r,s,y;try{h= c[a]}catch(z){}if("object"==typeof h&&h)if(m=t.call(h),"[object Date]"!=m||u.call(h,"toJSON"))"function"==typeof h.toJSON&&("[object Number]"!=m&&"[object String]"!=m&&"[object Array]"!=m||u.call(h,"toJSON"))&&(h=h.toJSON(a));else if(h>-1/0&&h<1/0){if(F){n=w(h/864E5);for(m=w(n/365.2425)+1970-1;F(m+1,0)<=n;m++);for(k=w((n-F(m,0))/30.42);F(m,k+1)<=n;k++);n=1+n-F(m,k);p=(h%864E5+864E5)%864E5;q=w(p/36E5)%24;r=w(p/6E4)%60;s=w(p/1E3)%60;p%=1E3}else m=h.getUTCFullYear(),k=h.getUTCMonth(),n=h.getUTCDate(), q=h.getUTCHours(),r=h.getUTCMinutes(),s=h.getUTCSeconds(),p=h.getUTCMilliseconds();h=(0>=m||1E4<=m?(0>m?"-":"+")+x(6,0>m?-m:m):x(4,m))+"-"+x(2,k+1)+"-"+x(2,n)+"T"+x(2,q)+":"+x(2,r)+":"+x(2,s)+"."+x(3,p)+"Z"}else h=null;b&&(h=b.call(c,a,h));if(null===h)return"null";m=t.call(h);if("[object Boolean]"==m)return""+h;if("[object Number]"==m)return h>-1/0&&h<1/0?""+h:"null";if("[object String]"==m)return O(""+h);if("object"==typeof h){for(a=d.length;a--;)if(d[a]===h)throw N();d.push(h);y=[];c=l;l+=f;if("[object Array]"== m){k=0;for(a=h.length;k=b.length?b:b.slice(0,10));return L("",(k={},k[""]=a,k),f,l,g,"",[])}}if(!s("json-parse")){var V=B.fromCharCode,W={92:"\\",34:'"',47:"/",98:"\b",116:"\t",110:"\n",102:"\f",114:"\r"},b,I,k=function(){b=I=null;throw J();},z=function(){for(var a=I,c=a.length,e,g,f,l,d;bd)k();else if(92==d)switch(d=a.charCodeAt(++b),d){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:e+=W[d];b++;break;case 117:g=++b;for(f=b+4;b=d||97<=d&&102>=d||65<=d&&70>=d||k();e+=V("0x"+a.slice(g,b));break;default:k()}else{if(34==d)break;d=a.charCodeAt(b);for(g=b;32<= d&&92!=d&&34!=d;)d=a.charCodeAt(++b);e+=a.slice(g,b)}if(34==a.charCodeAt(b))return b++,e;k();default:g=b;45==d&&(l=!0,d=a.charCodeAt(++b));if(48<=d&&57>=d){for(48==d&&(d=a.charCodeAt(b+1),48<=d&&57>=d)&&k();b=d);b++);if(46==a.charCodeAt(b)){for(f=++b;f=d);f++);f==b&&k();b=f}d=a.charCodeAt(b);if(101==d||69==d){d=a.charCodeAt(++b);43!=d&&45!=d||b++;for(f=b;f=d);f++);f==b&&k();b=f}return+a.slice(g,b)}l&& k();if("true"==a.slice(b,b+4))return b+=4,!0;if("false"==a.slice(b,b+5))return b+=5,!1;if("null"==a.slice(b,b+4))return b+=4,null;k()}return"$"},M=function(a){var c,b;"$"==a&&k();if("string"==typeof a){if("@"==(E?a.charAt(0):a[0]))return a.slice(1);if("["==a){for(c=[];;b||(b=!0)){a=z();if("]"==a)break;b&&(","==a?(a=z(),"]"==a&&k()):k());","==a&&k();c.push(M(a))}return c}if("{"==a){for(c={};;b||(b=!0)){a=z();if("}"==a)break;b&&(","==a?(a=z(),"}"==a&&k()):k());","!=a&&"string"==typeof a&&"@"==(E?a.charAt(0): a[0])&&":"==z()||k();c[a.slice(1)]=M(z())}return c}k()}return a},Q=function(a,b,e){e=P(a,b,e);e===v?delete a[b]:a[b]=e},P=function(a,b,e){var g=a[b],f;if("object"==typeof g&&g)if("[object Array]"==t.call(g))for(f=g.length;f--;)Q(g,f,e);else C(g,function(a){Q(g,a,e)});return e.call(a,b,g)};q.parse=function(a,c){var e,g;b=0;I=""+a;e=M(z());"$"!=z()&&k();b=I=null;return c&&"[object Function]"==t.call(c)?P((g={},g[""]=e,g),"",c):e}}}q.runInContext=K;return q}var J=typeof define==="function"&&define.amd, A="object"==typeof global&&global;!A||A.global!==A&&A.window!==A||(n=A);if("object"!=typeof exports||!exports||exports.nodeType||J){var N=n.JSON,B=K(n,n.JSON3={noConflict:function(){n.JSON=N;return B}});n.JSON={parse:B.parse,stringify:B.stringify}}else K(n,exports);J&&define(function(){return B})})(this); ================================================ FILE: test/browser/main.js ================================================ adapter.defer = adapter.pending = function() { var ret = {}; ret.promise = new Promise(function(resolve, reject) { ret.resolve = ret.fulfill = resolve; ret.reject = reject; }); return ret; }; (function() { var currentTime = 0; var timers = {}; var currentId = 0; var global = window; function checkTimers() { var keys = Object.keys(timers); for (var i = 0; i < keys.length; ++i) { key = keys[i]; var timer = timers[key]; if (!timer) continue; if (currentTime >= (timer.started + timer.time)) { if (timer.interval) { timer.started = currentTime; } else { delete timers[key]; } var fn = timer.fn; fn(); } } } function setInterval(fn, time) { var id = currentId++; time = (+time || 0) | 0; if (time < 0) time = 0; timers[id] = { fn: fn, time: time, started: currentTime, interval: true }; return id; } function setTimeout(fn, time) { var id = currentId++; time = (+time || 0) | 0; if (time < 0) time = 0; timers[id] = { fn: fn, time: time, started: currentTime, interval: false }; return id; } function clearTimeout(id) { delete timers[id]; } window.oldSetTimeout = window.setTimeout; window.oldClearTimeout = window.clearTimeout; var clearInterval = clearTimeout; window.setInterval(function timerLoop() { currentTime += 10; checkTimers(); }, 1); window.setTimeout = setTimeout; window.clearTimeout = clearTimeout; window.setInterval = setInterval; window.clearInterval = clearInterval; })(); window.adapter = window.Promise; global.Promise = global.adapter = window.adapter; window.assert = require("assert"); var prev = window.assert.deepEqual; var areDeepEqual = function(a, b) { if (Array.isArray(a) && Array.isArray(b)) { if (a.length === b.length) { for (var i = 0; i < a.length; ++i) { if (a[i] !== b[i]) { return false; } } return true; } return false; } else { prev.call(window.assert, a, b); return true; } }; window.assert.deepEqual = function(a, b) { if (!areDeepEqual(a, b)) { throw new Error("a not equal to b"); } }; window.setImmediate = function(fn){ setTimeout(fn, 0); }; window.onload = function(){ var runner = mocha.run(); var failedTests = []; runner.on('end', function(){ window.mochaResults = runner.stats; window.mochaResults.reports = failedTests; if (window.__coverage__) { postCoverage(); } }); runner.on('fail', logFailure); function logFailure(test, err) { var flattenTitles = function(test){ var titles = []; while (test.parent.title){ titles.push(test.parent.title); test = test.parent; } return titles.reverse(); }; failedTests.push({name: test.title, result: false, message: err.message, stack: err.stack, titles: flattenTitles(test) }); } }; function postCoverage() { var json = JSON.stringify(window.__coverage__); var xhr = new XMLHttpRequest(); var browser = (navigator.userAgent + "").replace(/[^a-zA-Z0-9]/g, ""); var data = "json=" + encodeURIComponent(json) + "&browser=" + encodeURIComponent(browser); xhr.open("POST", "/coverdata", true); xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xhr.send(data); } ================================================ FILE: test/browser/mocha.css ================================================ @charset "utf-8"; body { margin:0; } #mocha { font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; margin: 60px 50px; } #mocha ul, #mocha li { margin: 0; padding: 0; } #mocha ul { list-style: none; } #mocha h1, #mocha h2 { margin: 0; } #mocha h1 { margin-top: 15px; font-size: 1em; font-weight: 200; } #mocha h1 a { text-decoration: none; color: inherit; } #mocha h1 a:hover { text-decoration: underline; } #mocha .suite .suite h1 { margin-top: 0; font-size: .8em; } #mocha .hidden { display: none; } #mocha h2 { font-size: 12px; font-weight: normal; cursor: pointer; } #mocha .suite { margin-left: 15px; } #mocha .test { margin-left: 15px; overflow: hidden; } #mocha .test.pending:hover h2::after { content: '(pending)'; font-family: arial, sans-serif; } #mocha .test.pass.medium .duration { background: #c09853; } #mocha .test.pass.slow .duration { background: #b94a48; } #mocha .test.pass::before { content: '✓'; font-size: 12px; display: block; float: left; margin-right: 5px; color: #00d6b2; } #mocha .test.pass .duration { font-size: 9px; margin-left: 5px; padding: 2px 5px; color: #fff; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); box-shadow: inset 0 1px 1px rgba(0,0,0,.2); -webkit-border-radius: 5px; -moz-border-radius: 5px; -ms-border-radius: 5px; -o-border-radius: 5px; border-radius: 5px; } #mocha .test.pass.fast .duration { display: none; } #mocha .test.pending { color: #0b97c4; } #mocha .test.pending::before { content: '◦'; color: #0b97c4; } #mocha .test.fail { color: #c00; } #mocha .test.fail pre { color: black; } #mocha .test.fail::before { content: '✖'; font-size: 12px; display: block; float: left; margin-right: 5px; color: #c00; } #mocha .test pre.error { color: #c00; max-height: 300px; overflow: auto; } /** * (1): approximate for browsers not supporting calc * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) * ^^ seriously */ #mocha .test pre { display: block; float: left; clear: left; font: 12px/1.5 monaco, monospace; margin: 5px; padding: 15px; border: 1px solid #eee; max-width: 85%; /*(1)*/ max-width: calc(100% - 42px); /*(2)*/ word-wrap: break-word; border-bottom-color: #ddd; -webkit-border-radius: 3px; -webkit-box-shadow: 0 1px 3px #eee; -moz-border-radius: 3px; -moz-box-shadow: 0 1px 3px #eee; border-radius: 3px; } #mocha .test h2 { position: relative; } #mocha .test a.replay { position: absolute; top: 3px; right: 0; text-decoration: none; vertical-align: middle; display: block; width: 15px; height: 15px; line-height: 15px; text-align: center; background: #eee; font-size: 15px; -moz-border-radius: 15px; border-radius: 15px; -webkit-transition: opacity 200ms; -moz-transition: opacity 200ms; transition: opacity 200ms; opacity: 0.3; color: #888; } #mocha .test:hover a.replay { opacity: 1; } #mocha-report.pass .test.fail { display: none; } #mocha-report.fail .test.pass { display: none; } #mocha-report.pending .test.pass, #mocha-report.pending .test.fail { display: none; } #mocha-report.pending .test.pass.pending { display: block; } #mocha-error { color: #c00; font-size: 1.5em; font-weight: 100; letter-spacing: 1px; } #mocha-stats { position: fixed; top: 15px; right: 10px; font-size: 12px; margin: 0; color: #888; z-index: 1; } #mocha-stats .progress { float: right; padding-top: 0; } #mocha-stats em { color: black; } #mocha-stats a { text-decoration: none; color: inherit; } #mocha-stats a:hover { border-bottom: 1px solid #eee; } #mocha-stats li { display: inline-block; margin: 0 5px; list-style: none; padding-top: 11px; } #mocha-stats canvas { width: 40px; height: 40px; } #mocha code .comment { color: #ddd; } #mocha code .init { color: #2f6fad; } #mocha code .string { color: #5890ad; } #mocha code .keyword { color: #8a6343; } #mocha code .number { color: #2f6fad; } @media screen and (max-device-width: 480px) { #mocha { margin: 60px 0px; } #mocha #stats { position: absolute; } } ================================================ FILE: test/browser/mocha.js ================================================ ;(function(){ // CommonJS require() function require(p){ var path = require.resolve(p) , mod = require.modules[path]; if (!mod) throw new Error('failed to require "' + p + '"'); if (!mod.exports) { mod.exports = {}; mod.call(mod.exports, mod, mod.exports, require.relative(path)); } return mod.exports; } require.modules = {}; require.resolve = function (path){ var orig = path , reg = path + '.js' , index = path + '/index.js'; return require.modules[reg] && reg || require.modules[index] && index || orig; }; require.register = function (path, fn){ require.modules[path] = fn; }; require.relative = function (parent) { return function(p){ if ('.' != p.charAt(0)) return require(p); var path = parent.split('/') , segs = p.split('/'); path.pop(); for (var i = 0; i < segs.length; i++) { var seg = segs[i]; if ('..' == seg) path.pop(); else if ('.' != seg) path.push(seg); } return require(path.join('/')); }; }; require.register("browser/debug.js", function(module, exports, require){ module.exports = function(type){ return function(){ } }; }); // module: browser/debug.js require.register("browser/diff.js", function(module, exports, require){ /* See LICENSE file for terms of use */ /* * Text diff implementation. * * This library supports the following APIS: * JsDiff.diffChars: Character by character diff * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace * JsDiff.diffLines: Line based diff * * JsDiff.diffCss: Diff targeted at CSS content * * These methods are based on the implementation proposed in * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 */ var JsDiff = (function() { /*jshint maxparams: 5*/ function clonePath(path) { return { newPos: path.newPos, components: path.components.slice(0) }; } function removeEmpty(array) { var ret = []; for (var i = 0; i < array.length; i++) { if (array[i]) { ret.push(array[i]); } } return ret; } function escapeHTML(s) { var n = s; n = n.replace(/&/g, '&'); n = n.replace(//g, '>'); n = n.replace(/"/g, '"'); return n; } var Diff = function(ignoreWhitespace) { this.ignoreWhitespace = ignoreWhitespace; }; Diff.prototype = { diff: function(oldString, newString) { // Handle the identity case (this is due to unrolling editLength == 0 if (newString === oldString) { return [{ value: newString }]; } if (!newString) { return [{ value: oldString, removed: true }]; } if (!oldString) { return [{ value: newString, added: true }]; } newString = this.tokenize(newString); oldString = this.tokenize(oldString); var newLen = newString.length, oldLen = oldString.length; var maxEditLength = newLen + oldLen; var bestPath = [{ newPos: -1, components: [] }]; // Seed editLength = 0 var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) { return bestPath[0].components; } for (var editLength = 1; editLength <= maxEditLength; editLength++) { for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) { var basePath; var addPath = bestPath[diagonalPath-1], removePath = bestPath[diagonalPath+1]; oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; if (addPath) { // No one else is going to attempt to use this value, clear it bestPath[diagonalPath-1] = undefined; } var canAdd = addPath && addPath.newPos+1 < newLen; var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; if (!canAdd && !canRemove) { bestPath[diagonalPath] = undefined; continue; } // Select the diagonal that we want to branch from. We select the prior // path whose position in the new string is the farthest from the origin // and does not pass the bounds of the diff graph if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { basePath = clonePath(removePath); this.pushComponent(basePath.components, oldString[oldPos], undefined, true); } else { basePath = clonePath(addPath); basePath.newPos++; this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined); } var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath); if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) { return basePath.components; } else { bestPath[diagonalPath] = basePath; } } } }, pushComponent: function(components, value, added, removed) { var last = components[components.length-1]; if (last && last.added === added && last.removed === removed) { // We need to clone here as the component clone operation is just // as shallow array clone components[components.length-1] = {value: this.join(last.value, value), added: added, removed: removed }; } else { components.push({value: value, added: added, removed: removed }); } }, extractCommon: function(basePath, newString, oldString, diagonalPath) { var newLen = newString.length, oldLen = oldString.length, newPos = basePath.newPos, oldPos = newPos - diagonalPath; while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { newPos++; oldPos++; this.pushComponent(basePath.components, newString[newPos], undefined, undefined); } basePath.newPos = newPos; return oldPos; }, equals: function(left, right) { var reWhitespace = /\S/; if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { return true; } else { return left === right; } }, join: function(left, right) { return left + right; }, tokenize: function(value) { return value; } }; var CharDiff = new Diff(); var WordDiff = new Diff(true); var WordWithSpaceDiff = new Diff(); WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { return removeEmpty(value.split(/(\s+|\b)/)); }; var CssDiff = new Diff(true); CssDiff.tokenize = function(value) { return removeEmpty(value.split(/([{}:;,]|\s+)/)); }; var LineDiff = new Diff(); LineDiff.tokenize = function(value) { return value.split(/^/m); }; return { Diff: Diff, diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); }, diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); }, createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { var ret = []; ret.push('Index: ' + fileName); ret.push('==================================================================='); ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); var diff = LineDiff.diff(oldStr, newStr); if (!diff[diff.length-1].value) { diff.pop(); // Remove trailing newline add } diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier function contextLines(lines) { return lines.map(function(entry) { return ' ' + entry; }); } function eofNL(curRange, i, current) { var last = diff[diff.length-2], isLast = i === diff.length-2, isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); // Figure out if this is the last line for the given file and missing NL if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { curRange.push('\\ No newline at end of file'); } } var oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1; for (var i = 0; i < diff.length; i++) { var current = diff[i], lines = current.lines || current.value.replace(/\n$/, '').split('\n'); current.lines = lines; if (current.added || current.removed) { if (!oldRangeStart) { var prev = diff[i-1]; oldRangeStart = oldLine; newRangeStart = newLine; if (prev) { curRange = contextLines(prev.lines.slice(-4)); oldRangeStart -= curRange.length; newRangeStart -= curRange.length; } } curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; })); eofNL(curRange, i, current); if (current.added) { newLine += lines.length; } else { oldLine += lines.length; } } else { if (oldRangeStart) { // Close out any changes that have been output (or join overlapping) if (lines.length <= 8 && i < diff.length-2) { // Overlapping curRange.push.apply(curRange, contextLines(lines)); } else { // end the range and output var contextSize = Math.min(lines.length, 4); ret.push( '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) + ' @@'); ret.push.apply(ret, curRange); ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); if (lines.length <= 4) { eofNL(ret, i, current); } oldRangeStart = 0; newRangeStart = 0; curRange = []; } } oldLine += lines.length; newLine += lines.length; } } return ret.join('\n') + '\n'; }, applyPatch: function(oldStr, uniDiff) { var diffstr = uniDiff.split('\n'); var diff = []; var remEOFNL = false, addEOFNL = false; for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { if(diffstr[i][0] === '@') { var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); diff.unshift({ start:meh[3], oldlength:meh[2], oldlines:[], newlength:meh[4], newlines:[] }); } else if(diffstr[i][0] === '+') { diff[0].newlines.push(diffstr[i].substr(1)); } else if(diffstr[i][0] === '-') { diff[0].oldlines.push(diffstr[i].substr(1)); } else if(diffstr[i][0] === ' ') { diff[0].newlines.push(diffstr[i].substr(1)); diff[0].oldlines.push(diffstr[i].substr(1)); } else if(diffstr[i][0] === '\\') { if (diffstr[i-1][0] === '+') { remEOFNL = true; } else if(diffstr[i-1][0] === '-') { addEOFNL = true; } } } var str = oldStr.split('\n'); for (var i = diff.length - 1; i >= 0; i--) { var d = diff[i]; for (var j = 0; j < d.oldlength; j++) { if(str[d.start-1+j] !== d.oldlines[j]) { return false; } } Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); } if (remEOFNL) { while (!str[str.length-1]) { str.pop(); } } else if (addEOFNL) { str.push(''); } return str.join('\n'); }, convertChangesToXML: function(changes){ var ret = []; for ( var i = 0; i < changes.length; i++) { var change = changes[i]; if (change.added) { ret.push(''); } else if (change.removed) { ret.push(''); } ret.push(escapeHTML(change.value)); if (change.added) { ret.push(''); } else if (change.removed) { ret.push(''); } } return ret.join(''); }, // See: http://code.google.com/p/google-diff-match-patch/wiki/API convertChangesToDMP: function(changes){ var ret = [], change; for ( var i = 0; i < changes.length; i++) { change = changes[i]; ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); } return ret; } }; })(); if (typeof module !== 'undefined') { module.exports = JsDiff; } }); // module: browser/diff.js require.register("browser/escape-string-regexp.js", function(module, exports, require){ 'use strict'; var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; module.exports = function (str) { if (typeof str !== 'string') { throw new TypeError('Expected a string'); } return str.replace(matchOperatorsRe, '\\$&'); }; }); // module: browser/escape-string-regexp.js require.register("browser/events.js", function(module, exports, require){ /** * Module exports. */ exports.EventEmitter = EventEmitter; /** * Check if `obj` is an array. */ function isArray(obj) { return '[object Array]' == {}.toString.call(obj); } /** * Event emitter constructor. * * @api public */ function EventEmitter(){}; /** * Adds a listener. * * @api public */ EventEmitter.prototype.on = function (name, fn) { if (!this.$events) { this.$events = {}; } if (!this.$events[name]) { this.$events[name] = fn; } else if (isArray(this.$events[name])) { this.$events[name].push(fn); } else { this.$events[name] = [this.$events[name], fn]; } return this; }; EventEmitter.prototype.addListener = EventEmitter.prototype.on; /** * Adds a volatile listener. * * @api public */ EventEmitter.prototype.once = function (name, fn) { var self = this; function on () { self.removeListener(name, on); fn.apply(this, arguments); }; on.listener = fn; this.on(name, on); return this; }; /** * Removes a listener. * * @api public */ EventEmitter.prototype.removeListener = function (name, fn) { if (this.$events && this.$events[name]) { var list = this.$events[name]; if (isArray(list)) { var pos = -1; for (var i = 0, l = list.length; i < l; i++) { if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { pos = i; break; } } if (pos < 0) { return this; } list.splice(pos, 1); if (!list.length) { delete this.$events[name]; } } else if (list === fn || (list.listener && list.listener === fn)) { delete this.$events[name]; } } return this; }; /** * Removes all listeners for an event. * * @api public */ EventEmitter.prototype.removeAllListeners = function (name) { if (name === undefined) { this.$events = {}; return this; } if (this.$events && this.$events[name]) { this.$events[name] = null; } return this; }; /** * Gets all listeners for a certain event. * * @api public */ EventEmitter.prototype.listeners = function (name) { if (!this.$events) { this.$events = {}; } if (!this.$events[name]) { this.$events[name] = []; } if (!isArray(this.$events[name])) { this.$events[name] = [this.$events[name]]; } return this.$events[name]; }; /** * Emits an event. * * @api public */ EventEmitter.prototype.emit = function (name) { if (!this.$events) { return false; } var handler = this.$events[name]; if (!handler) { return false; } var args = [].slice.call(arguments, 1); if ('function' == typeof handler) { handler.apply(this, args); } else if (isArray(handler)) { var listeners = handler.slice(); for (var i = 0, l = listeners.length; i < l; i++) { listeners[i].apply(this, args); } } else { return false; } return true; }; }); // module: browser/events.js require.register("browser/fs.js", function(module, exports, require){ }); // module: browser/fs.js require.register("browser/glob.js", function(module, exports, require){ }); // module: browser/glob.js require.register("browser/path.js", function(module, exports, require){ }); // module: browser/path.js require.register("browser/progress.js", function(module, exports, require){ /** * Expose `Progress`. */ module.exports = Progress; /** * Initialize a new `Progress` indicator. */ function Progress() { this.percent = 0; this.size(0); this.fontSize(11); this.font('helvetica, arial, sans-serif'); } /** * Set progress size to `n`. * * @param {Number} n * @return {Progress} for chaining * @api public */ Progress.prototype.size = function(n){ this._size = n; return this; }; /** * Set text to `str`. * * @param {String} str * @return {Progress} for chaining * @api public */ Progress.prototype.text = function(str){ this._text = str; return this; }; /** * Set font size to `n`. * * @param {Number} n * @return {Progress} for chaining * @api public */ Progress.prototype.fontSize = function(n){ this._fontSize = n; return this; }; /** * Set font `family`. * * @param {String} family * @return {Progress} for chaining */ Progress.prototype.font = function(family){ this._font = family; return this; }; /** * Update percentage to `n`. * * @param {Number} n * @return {Progress} for chaining */ Progress.prototype.update = function(n){ this.percent = n; return this; }; /** * Draw on `ctx`. * * @param {CanvasRenderingContext2d} ctx * @return {Progress} for chaining */ Progress.prototype.draw = function(ctx){ try { var percent = Math.min(this.percent, 100) , size = this._size , half = size / 2 , x = half , y = half , rad = half - 1 , fontSize = this._fontSize; ctx.font = fontSize + 'px ' + this._font; var angle = Math.PI * 2 * (percent / 100); ctx.clearRect(0, 0, size, size); // outer circle ctx.strokeStyle = '#9f9f9f'; ctx.beginPath(); ctx.arc(x, y, rad, 0, angle, false); ctx.stroke(); // inner circle ctx.strokeStyle = '#eee'; ctx.beginPath(); ctx.arc(x, y, rad - 1, 0, angle, true); ctx.stroke(); // text var text = this._text || (percent | 0) + '%' , w = ctx.measureText(text).width; ctx.fillText( text , x - w / 2 + 1 , y + fontSize / 2 - 1); } catch (ex) {} //don't fail if we can't render progress return this; }; }); // module: browser/progress.js require.register("browser/tty.js", function(module, exports, require){ exports.isatty = function(){ return true; }; exports.getWindowSize = function(){ if ('innerHeight' in global) { return [global.innerHeight, global.innerWidth]; } else { // In a Web Worker, the DOM Window is not available. return [640, 480]; } }; }); // module: browser/tty.js require.register("context.js", function(module, exports, require){ /** * Expose `Context`. */ module.exports = Context; /** * Initialize a new `Context`. * * @api private */ function Context(){} /** * Set or get the context `Runnable` to `runnable`. * * @param {Runnable} runnable * @return {Context} * @api private */ Context.prototype.runnable = function(runnable){ if (0 == arguments.length) return this._runnable; this.test = this._runnable = runnable; return this; }; /** * Set test timeout `ms`. * * @param {Number} ms * @return {Context} self * @api private */ Context.prototype.timeout = function(ms){ if (arguments.length === 0) return this.runnable().timeout(); this.runnable().timeout(ms); return this; }; /** * Set test timeout `enabled`. * * @param {Boolean} enabled * @return {Context} self * @api private */ Context.prototype.enableTimeouts = function (enabled) { this.runnable().enableTimeouts(enabled); return this; }; /** * Set test slowness threshold `ms`. * * @param {Number} ms * @return {Context} self * @api private */ Context.prototype.slow = function(ms){ this.runnable().slow(ms); return this; }; /** * Inspect the context void of `._runnable`. * * @return {String} * @api private */ Context.prototype.inspect = function(){ return JSON.stringify(this, function(key, val){ if ('_runnable' == key) return; if ('test' == key) return; return val; }, 2); }; }); // module: context.js require.register("hook.js", function(module, exports, require){ /** * Module dependencies. */ var Runnable = require('./runnable'); /** * Expose `Hook`. */ module.exports = Hook; /** * Initialize a new `Hook` with the given `title` and callback `fn`. * * @param {String} title * @param {Function} fn * @api private */ function Hook(title, fn) { Runnable.call(this, title, fn); this.type = 'hook'; } /** * Inherit from `Runnable.prototype`. */ function F(){}; F.prototype = Runnable.prototype; Hook.prototype = new F; Hook.prototype.constructor = Hook; /** * Get or set the test `err`. * * @param {Error} err * @return {Error} * @api public */ Hook.prototype.error = function(err){ if (0 == arguments.length) { var err = this._error; this._error = null; return err; } this._error = err; }; }); // module: hook.js require.register("interfaces/bdd.js", function(module, exports, require){ /** * Module dependencies. */ var Suite = require('../suite') , Test = require('../test') , utils = require('../utils') , escapeRe = require('browser/escape-string-regexp'); /** * BDD-style interface: * * describe('Array', function(){ * describe('#indexOf()', function(){ * it('should return -1 when not present', function(){ * * }); * * it('should return the index when present', function(){ * * }); * }); * }); * */ module.exports = function(suite){ var suites = [suite]; suite.on('pre-require', function(context, file, mocha){ /** * Execute before running tests. */ context.before = function(name, fn){ suites[0].beforeAll(name, fn); }; /** * Execute after running tests. */ context.after = function(name, fn){ suites[0].afterAll(name, fn); }; /** * Execute before each test case. */ context.beforeEach = function(name, fn){ suites[0].beforeEach(name, fn); }; /** * Execute after each test case. */ context.afterEach = function(name, fn){ suites[0].afterEach(name, fn); }; /** * Describe a "suite" with the given `title` * and callback `fn` containing nested suites * and/or tests. */ context.describe = context.context = function(title, fn){ var suite = Suite.create(suites[0], title); suite.file = file; suites.unshift(suite); fn.call(suite); suites.shift(); return suite; }; /** * Pending describe. */ context.xdescribe = context.xcontext = context.describe.skip = function(title, fn){ var suite = Suite.create(suites[0], title); suite.pending = true; suites.unshift(suite); fn.call(suite); suites.shift(); }; /** * Exclusive suite. */ context.describe.only = function(title, fn){ var suite = context.describe(title, fn); mocha.grep(suite.fullTitle()); return suite; }; /** * Describe a specification or test-case * with the given `title` and callback `fn` * acting as a thunk. */ context.it = context.specify = function(title, fn){ var suite = suites[0]; if (suite.pending) fn = null; var test = new Test(title, fn); test.file = file; suite.addTest(test); return test; }; /** * Exclusive test-case. */ context.it.only = function(title, fn){ var test = context.it(title, fn); var reString = '^' + escapeRe(test.fullTitle()) + '$'; mocha.grep(new RegExp(reString)); return test; }; /** * Pending test case. */ context.xit = context.xspecify = context.it.skip = function(title){ context.it(title); }; }); }; }); // module: interfaces/bdd.js require.register("interfaces/exports.js", function(module, exports, require){ /** * Module dependencies. */ var Suite = require('../suite') , Test = require('../test'); /** * TDD-style interface: * * exports.Array = { * '#indexOf()': { * 'should return -1 when the value is not present': function(){ * * }, * * 'should return the correct index when the value is present': function(){ * * } * } * }; * */ module.exports = function(suite){ var suites = [suite]; suite.on('require', visit); function visit(obj, file) { var suite; for (var key in obj) { if ('function' == typeof obj[key]) { var fn = obj[key]; switch (key) { case 'before': suites[0].beforeAll(fn); break; case 'after': suites[0].afterAll(fn); break; case 'beforeEach': suites[0].beforeEach(fn); break; case 'afterEach': suites[0].afterEach(fn); break; default: var test = new Test(key, fn); test.file = file; suites[0].addTest(test); } } else { suite = Suite.create(suites[0], key); suites.unshift(suite); visit(obj[key]); suites.shift(); } } } }; }); // module: interfaces/exports.js require.register("interfaces/index.js", function(module, exports, require){ exports.bdd = require('./bdd'); exports.tdd = require('./tdd'); exports.qunit = require('./qunit'); exports.exports = require('./exports'); }); // module: interfaces/index.js require.register("interfaces/qunit.js", function(module, exports, require){ /** * Module dependencies. */ var Suite = require('../suite') , Test = require('../test') , escapeRe = require('browser/escape-string-regexp') , utils = require('../utils'); /** * QUnit-style interface: * * suite('Array'); * * test('#length', function(){ * var arr = [1,2,3]; * ok(arr.length == 3); * }); * * test('#indexOf()', function(){ * var arr = [1,2,3]; * ok(arr.indexOf(1) == 0); * ok(arr.indexOf(2) == 1); * ok(arr.indexOf(3) == 2); * }); * * suite('String'); * * test('#length', function(){ * ok('foo'.length == 3); * }); * */ module.exports = function(suite){ var suites = [suite]; suite.on('pre-require', function(context, file, mocha){ /** * Execute before running tests. */ context.before = function(name, fn){ suites[0].beforeAll(name, fn); }; /** * Execute after running tests. */ context.after = function(name, fn){ suites[0].afterAll(name, fn); }; /** * Execute before each test case. */ context.beforeEach = function(name, fn){ suites[0].beforeEach(name, fn); }; /** * Execute after each test case. */ context.afterEach = function(name, fn){ suites[0].afterEach(name, fn); }; /** * Describe a "suite" with the given `title`. */ context.suite = function(title){ if (suites.length > 1) suites.shift(); var suite = Suite.create(suites[0], title); suite.file = file; suites.unshift(suite); return suite; }; /** * Exclusive test-case. */ context.suite.only = function(title, fn){ var suite = context.suite(title, fn); mocha.grep(suite.fullTitle()); }; /** * Describe a specification or test-case * with the given `title` and callback `fn` * acting as a thunk. */ context.test = function(title, fn){ var test = new Test(title, fn); test.file = file; suites[0].addTest(test); return test; }; /** * Exclusive test-case. */ context.test.only = function(title, fn){ var test = context.test(title, fn); var reString = '^' + escapeRe(test.fullTitle()) + '$'; mocha.grep(new RegExp(reString)); }; /** * Pending test case. */ context.test.skip = function(title){ context.test(title); }; }); }; }); // module: interfaces/qunit.js require.register("interfaces/tdd.js", function(module, exports, require){ /** * Module dependencies. */ var Suite = require('../suite') , Test = require('../test') , escapeRe = require('browser/escape-string-regexp') , utils = require('../utils'); /** * TDD-style interface: * * suite('Array', function(){ * suite('#indexOf()', function(){ * suiteSetup(function(){ * * }); * * test('should return -1 when not present', function(){ * * }); * * test('should return the index when present', function(){ * * }); * * suiteTeardown(function(){ * * }); * }); * }); * */ module.exports = function(suite){ var suites = [suite]; suite.on('pre-require', function(context, file, mocha){ /** * Execute before each test case. */ context.setup = function(name, fn){ suites[0].beforeEach(name, fn); }; /** * Execute after each test case. */ context.teardown = function(name, fn){ suites[0].afterEach(name, fn); }; /** * Execute before the suite. */ context.suiteSetup = function(name, fn){ suites[0].beforeAll(name, fn); }; /** * Execute after the suite. */ context.suiteTeardown = function(name, fn){ suites[0].afterAll(name, fn); }; /** * Describe a "suite" with the given `title` * and callback `fn` containing nested suites * and/or tests. */ context.suite = function(title, fn){ var suite = Suite.create(suites[0], title); suite.file = file; suites.unshift(suite); fn.call(suite); suites.shift(); return suite; }; /** * Pending suite. */ context.suite.skip = function(title, fn) { var suite = Suite.create(suites[0], title); suite.pending = true; suites.unshift(suite); fn.call(suite); suites.shift(); }; /** * Exclusive test-case. */ context.suite.only = function(title, fn){ var suite = context.suite(title, fn); mocha.grep(suite.fullTitle()); }; /** * Describe a specification or test-case * with the given `title` and callback `fn` * acting as a thunk. */ context.test = function(title, fn){ var suite = suites[0]; if (suite.pending) fn = null; var test = new Test(title, fn); test.file = file; suite.addTest(test); return test; }; /** * Exclusive test-case. */ context.test.only = function(title, fn){ var test = context.test(title, fn); var reString = '^' + escapeRe(test.fullTitle()) + '$'; mocha.grep(new RegExp(reString)); }; /** * Pending test case. */ context.test.skip = function(title){ context.test(title); }; }); }; }); // module: interfaces/tdd.js require.register("mocha.js", function(module, exports, require){ /*! * mocha * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var path = require('browser/path') , escapeRe = require('browser/escape-string-regexp') , utils = require('./utils'); /** * Expose `Mocha`. */ exports = module.exports = Mocha; /** * To require local UIs and reporters when running in node. */ if (typeof process !== 'undefined' && typeof process.cwd === 'function') { var join = path.join , cwd = process.cwd(); module.paths.push(cwd, join(cwd, 'node_modules')); } /** * Expose internals. */ exports.utils = utils; exports.interfaces = require('./interfaces'); exports.reporters = require('./reporters'); exports.Runnable = require('./runnable'); exports.Context = require('./context'); exports.Runner = require('./runner'); exports.Suite = require('./suite'); exports.Hook = require('./hook'); exports.Test = require('./test'); /** * Return image `name` path. * * @param {String} name * @return {String} * @api private */ function image(name) { return __dirname + '/../images/' + name + '.png'; } /** * Setup mocha with `options`. * * Options: * * - `ui` name "bdd", "tdd", "exports" etc * - `reporter` reporter instance, defaults to `mocha.reporters.spec` * - `globals` array of accepted globals * - `timeout` timeout in milliseconds * - `bail` bail on the first test failure * - `slow` milliseconds to wait before considering a test slow * - `ignoreLeaks` ignore global leaks * - `grep` string or regexp to filter tests with * * @param {Object} options * @api public */ function Mocha(options) { options = options || {}; this.files = []; this.options = options; this.grep(options.grep); this.suite = new exports.Suite('', new exports.Context); this.ui(options.ui); this.bail(options.bail); this.reporter(options.reporter); if (null != options.timeout) this.timeout(options.timeout); this.useColors(options.useColors) if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts); if (options.slow) this.slow(options.slow); this.suite.on('pre-require', function (context) { exports.afterEach = context.afterEach || context.teardown; exports.after = context.after || context.suiteTeardown; exports.beforeEach = context.beforeEach || context.setup; exports.before = context.before || context.suiteSetup; exports.describe = context.describe || context.suite; exports.it = context.it || context.test; exports.setup = context.setup || context.beforeEach; exports.suiteSetup = context.suiteSetup || context.before; exports.suiteTeardown = context.suiteTeardown || context.after; exports.suite = context.suite || context.describe; exports.teardown = context.teardown || context.afterEach; exports.test = context.test || context.it; }); } /** * Enable or disable bailing on the first failure. * * @param {Boolean} [bail] * @api public */ Mocha.prototype.bail = function(bail){ if (0 == arguments.length) bail = true; this.suite.bail(bail); return this; }; /** * Add test `file`. * * @param {String} file * @api public */ Mocha.prototype.addFile = function(file){ this.files.push(file); return this; }; /** * Set reporter to `reporter`, defaults to "spec". * * @param {String|Function} reporter name or constructor * @api public */ Mocha.prototype.reporter = function(reporter){ if ('function' == typeof reporter) { this._reporter = reporter; } else { reporter = reporter || 'spec'; var _reporter; try { _reporter = require('./reporters/' + reporter); } catch (err) {}; if (!_reporter) try { _reporter = require(reporter); } catch (err) {}; if (!_reporter && reporter === 'teamcity') console.warn('The Teamcity reporter was moved to a package named ' + 'mocha-teamcity-reporter ' + '(https://npmjs.org/package/mocha-teamcity-reporter).'); if (!_reporter) throw new Error('invalid reporter "' + reporter + '"'); this._reporter = _reporter; } return this; }; /** * Set test UI `name`, defaults to "bdd". * * @param {String} bdd * @api public */ Mocha.prototype.ui = function(name){ name = name || 'bdd'; this._ui = exports.interfaces[name]; if (!this._ui) try { this._ui = require(name); } catch (err) {}; if (!this._ui) throw new Error('invalid interface "' + name + '"'); this._ui = this._ui(this.suite); return this; }; /** * Load registered files. * * @api private */ Mocha.prototype.loadFiles = function(fn){ var self = this; var suite = this.suite; var pending = this.files.length; this.files.forEach(function(file){ file = path.resolve(file); suite.emit('pre-require', global, file, self); suite.emit('require', require(file), file, self); suite.emit('post-require', global, file, self); --pending || (fn && fn()); }); }; /** * Enable growl support. * * @api private */ Mocha.prototype._growl = function(runner, reporter) { var notify = require('growl'); runner.on('end', function(){ var stats = reporter.stats; if (stats.failures) { var msg = stats.failures + ' of ' + runner.total + ' tests failed'; notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); } else { notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { name: 'mocha' , title: 'Passed' , image: image('ok') }); } }); }; /** * Add regexp to grep, if `re` is a string it is escaped. * * @param {RegExp|String} re * @return {Mocha} * @api public */ Mocha.prototype.grep = function(re){ this.options.grep = 'string' == typeof re ? new RegExp(escapeRe(re)) : re; return this; }; /** * Invert `.grep()` matches. * * @return {Mocha} * @api public */ Mocha.prototype.invert = function(){ this.options.invert = true; return this; }; /** * Ignore global leaks. * * @param {Boolean} ignore * @return {Mocha} * @api public */ Mocha.prototype.ignoreLeaks = function(ignore){ this.options.ignoreLeaks = !!ignore; return this; }; /** * Enable global leak checking. * * @return {Mocha} * @api public */ Mocha.prototype.checkLeaks = function(){ this.options.ignoreLeaks = false; return this; }; /** * Enable growl support. * * @return {Mocha} * @api public */ Mocha.prototype.growl = function(){ this.options.growl = true; return this; }; /** * Ignore `globals` array or string. * * @param {Array|String} globals * @return {Mocha} * @api public */ Mocha.prototype.globals = function(globals){ this.options.globals = (this.options.globals || []).concat(globals); return this; }; /** * Emit color output. * * @param {Boolean} colors * @return {Mocha} * @api public */ Mocha.prototype.useColors = function(colors){ this.options.useColors = arguments.length && colors != undefined ? colors : true; return this; }; /** * Use inline diffs rather than +/-. * * @param {Boolean} inlineDiffs * @return {Mocha} * @api public */ Mocha.prototype.useInlineDiffs = function(inlineDiffs) { this.options.useInlineDiffs = arguments.length && inlineDiffs != undefined ? inlineDiffs : false; return this; }; /** * Set the timeout in milliseconds. * * @param {Number} timeout * @return {Mocha} * @api public */ Mocha.prototype.timeout = function(timeout){ this.suite.timeout(timeout); return this; }; /** * Set slowness threshold in milliseconds. * * @param {Number} slow * @return {Mocha} * @api public */ Mocha.prototype.slow = function(slow){ this.suite.slow(slow); return this; }; /** * Enable timeouts. * * @param {Boolean} enabled * @return {Mocha} * @api public */ Mocha.prototype.enableTimeouts = function(enabled) { this.suite.enableTimeouts(arguments.length && enabled !== undefined ? enabled : true); return this }; /** * Makes all tests async (accepting a callback) * * @return {Mocha} * @api public */ Mocha.prototype.asyncOnly = function(){ this.options.asyncOnly = true; return this; }; /** * Disable syntax highlighting (in browser). * @returns {Mocha} * @api public */ Mocha.prototype.noHighlighting = function() { this.options.noHighlighting = true; return this; }; /** * Run tests and invoke `fn()` when complete. * * @param {Function} fn * @return {Runner} * @api public */ Mocha.prototype.run = function(fn){ if (this.files.length) this.loadFiles(); var suite = this.suite; var options = this.options; options.files = this.files; var runner = new exports.Runner(suite); var reporter = new this._reporter(runner, options); runner.ignoreLeaks = false !== options.ignoreLeaks; runner.asyncOnly = options.asyncOnly; if (options.grep) runner.grep(options.grep, options.invert); if (options.globals) runner.globals(options.globals); if (options.growl) this._growl(runner, reporter); exports.reporters.Base.useColors = options.useColors; exports.reporters.Base.inlineDiffs = options.useInlineDiffs; return runner.run(fn); }; }); // module: mocha.js require.register("ms.js", function(module, exports, require){ /** * Helpers. */ var s = 1000; var m = s * 60; var h = m * 60; var d = h * 24; var y = d * 365.25; /** * Parse or format the given `val`. * * Options: * * - `long` verbose formatting [false] * * @param {String|Number} val * @param {Object} options * @return {String|Number} * @api public */ module.exports = function(val, options){ options = options || {}; if ('string' == typeof val) return parse(val); return options['long'] ? longFormat(val) : shortFormat(val); }; /** * Parse the given `str` and return milliseconds. * * @param {String} str * @return {Number} * @api private */ function parse(str) { var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); if (!match) return; var n = parseFloat(match[1]); var type = (match[2] || 'ms').toLowerCase(); switch (type) { case 'years': case 'year': case 'y': return n * y; case 'days': case 'day': case 'd': return n * d; case 'hours': case 'hour': case 'h': return n * h; case 'minutes': case 'minute': case 'm': return n * m; case 'seconds': case 'second': case 's': return n * s; case 'ms': return n; } } /** * Short format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function shortFormat(ms) { if (ms >= d) return Math.round(ms / d) + 'd'; if (ms >= h) return Math.round(ms / h) + 'h'; if (ms >= m) return Math.round(ms / m) + 'm'; if (ms >= s) return Math.round(ms / s) + 's'; return ms + 'ms'; } /** * Long format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function longFormat(ms) { return plural(ms, d, 'day') || plural(ms, h, 'hour') || plural(ms, m, 'minute') || plural(ms, s, 'second') || ms + ' ms'; } /** * Pluralization helper. */ function plural(ms, n, name) { if (ms < n) return; if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; return Math.ceil(ms / n) + ' ' + name + 's'; } }); // module: ms.js require.register("reporters/base.js", function(module, exports, require){ /** * Module dependencies. */ var tty = require('browser/tty') , diff = require('browser/diff') , ms = require('../ms') , utils = require('../utils'); /** * Save timer references to avoid Sinon interfering (see GH-237). */ var Date = global.Date , setTimeout = global.setTimeout , setInterval = global.setInterval , clearTimeout = global.clearTimeout , clearInterval = global.clearInterval; /** * Check if both stdio streams are associated with a tty. */ var isatty = tty.isatty(1) && tty.isatty(2); /** * Expose `Base`. */ exports = module.exports = Base; /** * Enable coloring by default. */ exports.useColors = isatty || (process.env.MOCHA_COLORS !== undefined); /** * Inline diffs instead of +/- */ exports.inlineDiffs = false; /** * Default color map. */ exports.colors = { 'pass': 90 , 'fail': 31 , 'bright pass': 92 , 'bright fail': 91 , 'bright yellow': 93 , 'pending': 36 , 'suite': 0 , 'error title': 0 , 'error message': 31 , 'error stack': 90 , 'checkmark': 32 , 'fast': 90 , 'medium': 33 , 'slow': 31 , 'green': 32 , 'light': 90 , 'diff gutter': 90 , 'diff added': 42 , 'diff removed': 41 }; /** * Default symbol map. */ exports.symbols = { ok: '✓', err: '✖', dot: '․' }; // With node.js on Windows: use symbols available in terminal default fonts if ('win32' == process.platform) { exports.symbols.ok = '\u221A'; exports.symbols.err = '\u00D7'; exports.symbols.dot = '.'; } /** * Color `str` with the given `type`, * allowing colors to be disabled, * as well as user-defined color * schemes. * * @param {String} type * @param {String} str * @return {String} * @api private */ var color = exports.color = function(type, str) { if (!exports.useColors) return str; return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; }; /** * Expose term window size, with some * defaults for when stderr is not a tty. */ exports.window = { width: isatty ? process.stdout.getWindowSize ? process.stdout.getWindowSize(1)[0] : tty.getWindowSize()[1] : 75 }; /** * Expose some basic cursor interactions * that are common among reporters. */ exports.cursor = { hide: function(){ isatty && process.stdout.write('\u001b[?25l'); }, show: function(){ isatty && process.stdout.write('\u001b[?25h'); }, deleteLine: function(){ isatty && process.stdout.write('\u001b[2K'); }, beginningOfLine: function(){ isatty && process.stdout.write('\u001b[0G'); }, CR: function(){ if (isatty) { exports.cursor.deleteLine(); exports.cursor.beginningOfLine(); } else { process.stdout.write('\r'); } } }; /** * Outut the given `failures` as a list. * * @param {Array} failures * @api public */ exports.list = function(failures){ console.error(); failures.forEach(function(test, i){ // format var fmt = color('error title', ' %s) %s:\n') + color('error message', ' %s') + color('error stack', '\n%s\n'); // msg var err = test.err , message = err.message || '' , stack = err.stack || message , index = stack.indexOf(message) + message.length , msg = stack.slice(0, index) , actual = err.actual , expected = err.expected , escape = true; // uncaught if (err.uncaught) { msg = 'Uncaught ' + msg; } // explicitly show diff if (err.showDiff && sameType(actual, expected)) { escape = false; err.actual = actual = utils.stringify(actual); err.expected = expected = utils.stringify(expected); } // actual / expected diff if (err.showDiff && 'string' == typeof actual && 'string' == typeof expected) { fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); var match = message.match(/^([^:]+): expected/); msg = '\n ' + color('error message', match ? match[1] : msg); if (exports.inlineDiffs) { msg += inlineDiff(err, escape); } else { msg += unifiedDiff(err, escape); } } // indent stack trace without msg stack = stack.slice(index ? index + 1 : index) .replace(/^/gm, ' '); console.error(fmt, (i + 1), test.fullTitle(), msg, stack); }); }; /** * Initialize a new `Base` reporter. * * All other reporters generally * inherit from this reporter, providing * stats such as test duration, number * of tests passed / failed etc. * * @param {Runner} runner * @api public */ function Base(runner) { var self = this , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } , failures = this.failures = []; if (!runner) return; this.runner = runner; runner.stats = stats; runner.on('start', function(){ stats.start = new Date; }); runner.on('suite', function(suite){ stats.suites = stats.suites || 0; suite.root || stats.suites++; }); runner.on('test end', function(test){ stats.tests = stats.tests || 0; stats.tests++; }); runner.on('pass', function(test){ stats.passes = stats.passes || 0; var medium = test.slow() / 2; test.speed = test.duration > test.slow() ? 'slow' : test.duration > medium ? 'medium' : 'fast'; stats.passes++; }); runner.on('fail', function(test, err){ stats.failures = stats.failures || 0; stats.failures++; test.err = err; failures.push(test); }); runner.on('end', function(){ stats.end = new Date; stats.duration = new Date - stats.start; }); runner.on('pending', function(){ stats.pending++; }); } /** * Output common epilogue used by many of * the bundled reporters. * * @api public */ Base.prototype.epilogue = function(){ var stats = this.stats; var tests; var fmt; console.log(); // passes fmt = color('bright pass', ' ') + color('green', ' %d passing') + color('light', ' (%s)'); console.log(fmt, stats.passes || 0, ms(stats.duration)); // pending if (stats.pending) { fmt = color('pending', ' ') + color('pending', ' %d pending'); console.log(fmt, stats.pending); } // failures if (stats.failures) { fmt = color('fail', ' %d failing'); console.error(fmt, stats.failures); Base.list(this.failures); console.error(); } console.log(); }; /** * Pad the given `str` to `len`. * * @param {String} str * @param {String} len * @return {String} * @api private */ function pad(str, len) { str = String(str); return Array(len - str.length + 1).join(' ') + str; } /** * Returns an inline diff between 2 strings with coloured ANSI output * * @param {Error} Error with actual/expected * @return {String} Diff * @api private */ function inlineDiff(err, escape) { var msg = errorDiff(err, 'WordsWithSpace', escape); // linenos var lines = msg.split('\n'); if (lines.length > 4) { var width = String(lines.length).length; msg = lines.map(function(str, i){ return pad(++i, width) + ' |' + ' ' + str; }).join('\n'); } // legend msg = '\n' + color('diff removed', 'actual') + ' ' + color('diff added', 'expected') + '\n\n' + msg + '\n'; // indent msg = msg.replace(/^/gm, ' '); return msg; } /** * Returns a unified diff between 2 strings * * @param {Error} Error with actual/expected * @return {String} Diff * @api private */ function unifiedDiff(err, escape) { var indent = ' '; function cleanUp(line) { if (escape) { line = escapeInvisibles(line); } if (line[0] === '+') return indent + colorLines('diff added', line); if (line[0] === '-') return indent + colorLines('diff removed', line); if (line.match(/\@\@/)) return null; if (line.match(/\\ No newline/)) return null; else return indent + line; } function notBlank(line) { return line != null; } msg = diff.createPatch('string', err.actual, err.expected); var lines = msg.split('\n').splice(4); return '\n ' + colorLines('diff added', '+ expected') + ' ' + colorLines('diff removed', '- actual') + '\n\n' + lines.map(cleanUp).filter(notBlank).join('\n'); } /** * Return a character diff for `err`. * * @param {Error} err * @return {String} * @api private */ function errorDiff(err, type, escape) { var actual = escape ? escapeInvisibles(err.actual) : err.actual; var expected = escape ? escapeInvisibles(err.expected) : err.expected; return diff['diff' + type](actual, expected).map(function(str){ if (str.added) return colorLines('diff added', str.value); if (str.removed) return colorLines('diff removed', str.value); return str.value; }).join(''); } /** * Returns a string with all invisible characters in plain text * * @param {String} line * @return {String} * @api private */ function escapeInvisibles(line) { return line.replace(/\t/g, '') .replace(/\r/g, '') .replace(/\n/g, '\n'); } /** * Color lines for `str`, using the color `name`. * * @param {String} name * @param {String} str * @return {String} * @api private */ function colorLines(name, str) { return str.split('\n').map(function(str){ return color(name, str); }).join('\n'); } /** * Check that a / b have the same type. * * @param {Object} a * @param {Object} b * @return {Boolean} * @api private */ function sameType(a, b) { a = Object.prototype.toString.call(a); b = Object.prototype.toString.call(b); return a == b; } }); // module: reporters/base.js require.register("reporters/doc.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , utils = require('../utils'); /** * Expose `Doc`. */ exports = module.exports = Doc; /** * Initialize a new `Doc` reporter. * * @param {Runner} runner * @api public */ function Doc(runner) { Base.call(this, runner); var self = this , stats = this.stats , total = runner.total , indents = 2; function indent() { return Array(indents).join(' '); } runner.on('suite', function(suite){ if (suite.root) return; ++indents; console.log('%s
', indent()); ++indents; console.log('%s

%s

', indent(), utils.escape(suite.title)); console.log('%s
', indent()); }); runner.on('suite end', function(suite){ if (suite.root) return; console.log('%s
', indent()); --indents; console.log('%s
', indent()); --indents; }); runner.on('pass', function(test){ console.log('%s
%s
', indent(), utils.escape(test.title)); var code = utils.escape(utils.clean(test.fn.toString())); console.log('%s
%s
', indent(), code); }); runner.on('fail', function(test, err){ console.log('%s
%s
', indent(), utils.escape(test.title)); var code = utils.escape(utils.clean(test.fn.toString())); console.log('%s
%s
', indent(), code); console.log('%s
%s
', indent(), utils.escape(err)); }); } }); // module: reporters/doc.js require.register("reporters/dot.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , color = Base.color; /** * Expose `Dot`. */ exports = module.exports = Dot; /** * Initialize a new `Dot` matrix test reporter. * * @param {Runner} runner * @api public */ function Dot(runner) { Base.call(this, runner); var self = this , stats = this.stats , width = Base.window.width * .75 | 0 , n = -1; runner.on('start', function(){ process.stdout.write('\n '); }); runner.on('pending', function(test){ if (++n % width == 0) process.stdout.write('\n '); process.stdout.write(color('pending', Base.symbols.dot)); }); runner.on('pass', function(test){ if (++n % width == 0) process.stdout.write('\n '); if ('slow' == test.speed) { process.stdout.write(color('bright yellow', Base.symbols.dot)); } else { process.stdout.write(color(test.speed, Base.symbols.dot)); } }); runner.on('fail', function(test, err){ if (++n % width == 0) process.stdout.write('\n '); process.stdout.write(color('fail', Base.symbols.dot)); }); runner.on('end', function(){ console.log(); self.epilogue(); }); } /** * Inherit from `Base.prototype`. */ function F(){}; F.prototype = Base.prototype; Dot.prototype = new F; Dot.prototype.constructor = Dot; }); // module: reporters/dot.js require.register("reporters/html-cov.js", function(module, exports, require){ /** * Module dependencies. */ var JSONCov = require('./json-cov') , fs = require('browser/fs'); /** * Expose `HTMLCov`. */ exports = module.exports = HTMLCov; /** * Initialize a new `JsCoverage` reporter. * * @param {Runner} runner * @api public */ function HTMLCov(runner) { var jade = require('jade') , file = __dirname + '/templates/coverage.jade' , str = fs.readFileSync(file, 'utf8') , fn = jade.compile(str, { filename: file }) , self = this; JSONCov.call(this, runner, false); runner.on('end', function(){ process.stdout.write(fn({ cov: self.cov , coverageClass: coverageClass })); }); } /** * Return coverage class for `n`. * * @return {String} * @api private */ function coverageClass(n) { if (n >= 75) return 'high'; if (n >= 50) return 'medium'; if (n >= 25) return 'low'; return 'terrible'; } }); // module: reporters/html-cov.js require.register("reporters/html.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , utils = require('../utils') , Progress = require('../browser/progress') , escape = utils.escape; /** * Save timer references to avoid Sinon interfering (see GH-237). */ var Date = global.Date , setTimeout = global.setTimeout , setInterval = global.setInterval , clearTimeout = global.clearTimeout , clearInterval = global.clearInterval; /** * Expose `HTML`. */ exports = module.exports = HTML; /** * Stats template. */ var statsTemplate = ''; /** * Initialize a new `HTML` reporter. * * @param {Runner} runner * @api public */ function HTML(runner) { Base.call(this, runner); var self = this , stats = this.stats , total = runner.total , stat = fragment(statsTemplate) , items = stat.getElementsByTagName('li') , passes = items[1].getElementsByTagName('em')[0] , passesLink = items[1].getElementsByTagName('a')[0] , failures = items[2].getElementsByTagName('em')[0] , failuresLink = items[2].getElementsByTagName('a')[0] , duration = items[3].getElementsByTagName('em')[0] , canvas = stat.getElementsByTagName('canvas')[0] , report = fragment('
    ') , stack = [report] , progress , ctx , root = document.getElementById('mocha'); if (canvas.getContext) { var ratio = window.devicePixelRatio || 1; canvas.style.width = canvas.width; canvas.style.height = canvas.height; canvas.width *= ratio; canvas.height *= ratio; ctx = canvas.getContext('2d'); ctx.scale(ratio, ratio); progress = new Progress; } if (!root) return error('#mocha div missing, add it to your document'); // pass toggle on(passesLink, 'click', function(){ unhide(); var name = /pass/.test(report.className) ? '' : ' pass'; report.className = report.className.replace(/fail|pass/g, '') + name; if (report.className.trim()) hideSuitesWithout('test pass'); }); // failure toggle on(failuresLink, 'click', function(){ unhide(); var name = /fail/.test(report.className) ? '' : ' fail'; report.className = report.className.replace(/fail|pass/g, '') + name; if (report.className.trim()) hideSuitesWithout('test fail'); }); root.appendChild(stat); root.appendChild(report); if (progress) progress.size(40); runner.on('suite', function(suite){ if (suite.root) return; // suite var url = self.suiteURL(suite); var el = fragment('
  • %s

  • ', url, escape(suite.title)); // container stack[0].appendChild(el); stack.unshift(document.createElement('ul')); el.appendChild(stack[0]); }); runner.on('suite end', function(suite){ if (suite.root) return; stack.shift(); }); runner.on('fail', function(test, err){ if ('hook' == test.type) runner.emit('test end', test); }); runner.on('test end', function(test){ // TODO: add to stats var percent = stats.tests / this.total * 100 | 0; if (progress) progress.update(percent).draw(ctx); // update stats var ms = new Date - stats.start; text(passes, stats.passes); text(failures, stats.failures); text(duration, (ms / 1000).toFixed(2)); // test if ('passed' == test.state) { var url = self.testURL(test); var el = fragment('
  • %e%ems

  • ', test.speed, test.title, test.duration, url); } else if (test.pending) { var el = fragment('
  • %e

  • ', test.title); } else { var el = fragment('
  • %e

  • ', test.title, encodeURIComponent(test.fullTitle())); var str = test.err.stack || test.err.toString(); // FF / Opera do not add the message if (!~str.indexOf(test.err.message)) { str = test.err.message + '\n' + str; } // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we // check for the result of the stringifying. if ('[object Error]' == str) str = test.err.message; // Safari doesn't give you a stack. Let's at least provide a source line. if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; } el.appendChild(fragment('
    %e
    ', str)); } // toggle code // TODO: defer if (!test.pending) { var h2 = el.getElementsByTagName('h2')[0]; on(h2, 'click', function(){ pre.style.display = 'none' == pre.style.display ? 'block' : 'none'; }); var pre = fragment('
    %e
    ', utils.clean(test.fn.toString())); el.appendChild(pre); pre.style.display = 'none'; } // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. if (stack[0]) stack[0].appendChild(el); }); } /** * Makes a URL, preserving querystring ("search") parameters. * @param {string} s * @returns {string} your new URL */ var makeUrl = function makeUrl(s) { var search = window.location.search; return (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s); }; /** * Provide suite URL * * @param {Object} [suite] */ HTML.prototype.suiteURL = function(suite){ return makeUrl(suite.fullTitle()); }; /** * Provide test URL * * @param {Object} [test] */ HTML.prototype.testURL = function(test){ return makeUrl(test.fullTitle()); }; /** * Display error `msg`. */ function error(msg) { document.body.appendChild(fragment('
    %s
    ', msg)); } /** * Return a DOM fragment from `html`. */ function fragment(html) { var args = arguments , div = document.createElement('div') , i = 1; div.innerHTML = html.replace(/%([se])/g, function(_, type){ switch (type) { case 's': return String(args[i++]); case 'e': return escape(args[i++]); } }); return div.firstChild; } /** * Check for suites that do not have elements * with `classname`, and hide them. */ function hideSuitesWithout(classname) { var suites = document.getElementsByClassName('suite'); for (var i = 0; i < suites.length; i++) { var els = suites[i].getElementsByClassName(classname); if (0 == els.length) suites[i].className += ' hidden'; } } /** * Unhide .hidden suites. */ function unhide() { var els = document.getElementsByClassName('suite hidden'); for (var i = 0; i < els.length; ++i) { els[i].className = els[i].className.replace('suite hidden', 'suite'); } } /** * Set `el` text to `str`. */ function text(el, str) { if (el.textContent) { el.textContent = str; } else { el.innerText = str; } } /** * Listen on `event` with callback `fn`. */ function on(el, event, fn) { if (el.addEventListener) { el.addEventListener(event, fn, false); } else { el.attachEvent('on' + event, fn); } } }); // module: reporters/html.js require.register("reporters/index.js", function(module, exports, require){ exports.Base = require('./base'); exports.Dot = require('./dot'); exports.Doc = require('./doc'); exports.TAP = require('./tap'); exports.JSON = require('./json'); exports.HTML = require('./html'); exports.List = require('./list'); exports.Min = require('./min'); exports.Spec = require('./spec'); exports.Nyan = require('./nyan'); exports.XUnit = require('./xunit'); exports.Markdown = require('./markdown'); exports.Progress = require('./progress'); exports.Landing = require('./landing'); exports.JSONCov = require('./json-cov'); exports.HTMLCov = require('./html-cov'); exports.JSONStream = require('./json-stream'); }); // module: reporters/index.js require.register("reporters/json-cov.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base'); /** * Expose `JSONCov`. */ exports = module.exports = JSONCov; /** * Initialize a new `JsCoverage` reporter. * * @param {Runner} runner * @param {Boolean} output * @api public */ function JSONCov(runner, output) { var self = this , output = 1 == arguments.length ? true : output; Base.call(this, runner); var tests = [] , failures = [] , passes = []; runner.on('test end', function(test){ tests.push(test); }); runner.on('pass', function(test){ passes.push(test); }); runner.on('fail', function(test){ failures.push(test); }); runner.on('end', function(){ var cov = global._$jscoverage || {}; var result = self.cov = map(cov); result.stats = self.stats; result.tests = tests.map(clean); result.failures = failures.map(clean); result.passes = passes.map(clean); if (!output) return; process.stdout.write(JSON.stringify(result, null, 2 )); }); } /** * Map jscoverage data to a JSON structure * suitable for reporting. * * @param {Object} cov * @return {Object} * @api private */ function map(cov) { var ret = { instrumentation: 'node-jscoverage' , sloc: 0 , hits: 0 , misses: 0 , coverage: 0 , files: [] }; for (var filename in cov) { var data = coverage(filename, cov[filename]); ret.files.push(data); ret.hits += data.hits; ret.misses += data.misses; ret.sloc += data.sloc; } ret.files.sort(function(a, b) { return a.filename.localeCompare(b.filename); }); if (ret.sloc > 0) { ret.coverage = (ret.hits / ret.sloc) * 100; } return ret; } /** * Map jscoverage data for a single source file * to a JSON structure suitable for reporting. * * @param {String} filename name of the source file * @param {Object} data jscoverage coverage data * @return {Object} * @api private */ function coverage(filename, data) { var ret = { filename: filename, coverage: 0, hits: 0, misses: 0, sloc: 0, source: {} }; data.source.forEach(function(line, num){ num++; if (data[num] === 0) { ret.misses++; ret.sloc++; } else if (data[num] !== undefined) { ret.hits++; ret.sloc++; } ret.source[num] = { source: line , coverage: data[num] === undefined ? '' : data[num] }; }); ret.coverage = ret.hits / ret.sloc * 100; return ret; } /** * Return a plain-object representation of `test` * free of cyclic properties etc. * * @param {Object} test * @return {Object} * @api private */ function clean(test) { return { title: test.title , fullTitle: test.fullTitle() , duration: test.duration } } }); // module: reporters/json-cov.js require.register("reporters/json-stream.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , color = Base.color; /** * Expose `List`. */ exports = module.exports = List; /** * Initialize a new `List` test reporter. * * @param {Runner} runner * @api public */ function List(runner) { Base.call(this, runner); var self = this , stats = this.stats , total = runner.total; runner.on('start', function(){ console.log(JSON.stringify(['start', { total: total }])); }); runner.on('pass', function(test){ console.log(JSON.stringify(['pass', clean(test)])); }); runner.on('fail', function(test, err){ test = clean(test); test.err = err.message; console.log(JSON.stringify(['fail', test])); }); runner.on('end', function(){ process.stdout.write(JSON.stringify(['end', self.stats])); }); } /** * Return a plain-object representation of `test` * free of cyclic properties etc. * * @param {Object} test * @return {Object} * @api private */ function clean(test) { return { title: test.title , fullTitle: test.fullTitle() , duration: test.duration } } }); // module: reporters/json-stream.js require.register("reporters/json.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , cursor = Base.cursor , color = Base.color; /** * Expose `JSON`. */ exports = module.exports = JSONReporter; /** * Initialize a new `JSON` reporter. * * @param {Runner} runner * @api public */ function JSONReporter(runner) { var self = this; Base.call(this, runner); var tests = [] , pending = [] , failures = [] , passes = []; runner.on('test end', function(test){ tests.push(test); }); runner.on('pass', function(test){ passes.push(test); }); runner.on('fail', function(test){ failures.push(test); }); runner.on('pending', function(test){ pending.push(test); }); runner.on('end', function(){ var obj = { stats: self.stats, tests: tests.map(clean), pending: pending.map(clean), failures: failures.map(clean), passes: passes.map(clean) }; runner.testResults = obj; process.stdout.write(JSON.stringify(obj, null, 2)); }); } /** * Return a plain-object representation of `test` * free of cyclic properties etc. * * @param {Object} test * @return {Object} * @api private */ function clean(test) { return { title: test.title, fullTitle: test.fullTitle(), duration: test.duration, err: errorJSON(test.err || {}) } } /** * Transform `error` into a JSON object. * @param {Error} err * @return {Object} */ function errorJSON(err) { var res = {}; Object.getOwnPropertyNames(err).forEach(function(key) { res[key] = err[key]; }, err); return res; } }); // module: reporters/json.js require.register("reporters/landing.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , cursor = Base.cursor , color = Base.color; /** * Expose `Landing`. */ exports = module.exports = Landing; /** * Airplane color. */ Base.colors.plane = 0; /** * Airplane crash color. */ Base.colors['plane crash'] = 31; /** * Runway color. */ Base.colors.runway = 90; /** * Initialize a new `Landing` reporter. * * @param {Runner} runner * @api public */ function Landing(runner) { Base.call(this, runner); var self = this , stats = this.stats , width = Base.window.width * .75 | 0 , total = runner.total , stream = process.stdout , plane = color('plane', '✈') , crashed = -1 , n = 0; function runway() { var buf = Array(width).join('-'); return ' ' + color('runway', buf); } runner.on('start', function(){ stream.write('\n\n\n '); cursor.hide(); }); runner.on('test end', function(test){ // check if the plane crashed var col = -1 == crashed ? width * ++n / total | 0 : crashed; // show the crash if ('failed' == test.state) { plane = color('plane crash', '✈'); crashed = col; } // render landing strip stream.write('\u001b['+(width+1)+'D\u001b[2A'); stream.write(runway()); stream.write('\n '); stream.write(color('runway', Array(col).join('⋅'))); stream.write(plane) stream.write(color('runway', Array(width - col).join('⋅') + '\n')); stream.write(runway()); stream.write('\u001b[0m'); }); runner.on('end', function(){ cursor.show(); console.log(); self.epilogue(); }); } /** * Inherit from `Base.prototype`. */ function F(){}; F.prototype = Base.prototype; Landing.prototype = new F; Landing.prototype.constructor = Landing; }); // module: reporters/landing.js require.register("reporters/list.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , cursor = Base.cursor , color = Base.color; /** * Expose `List`. */ exports = module.exports = List; /** * Initialize a new `List` test reporter. * * @param {Runner} runner * @api public */ function List(runner) { Base.call(this, runner); var self = this , stats = this.stats , n = 0; runner.on('start', function(){ console.log(); }); runner.on('test', function(test){ process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); }); runner.on('pending', function(test){ var fmt = color('checkmark', ' -') + color('pending', ' %s'); console.log(fmt, test.fullTitle()); }); runner.on('pass', function(test){ var fmt = color('checkmark', ' '+Base.symbols.dot) + color('pass', ' %s: ') + color(test.speed, '%dms'); cursor.CR(); console.log(fmt, test.fullTitle(), test.duration); }); runner.on('fail', function(test, err){ cursor.CR(); console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); }); runner.on('end', self.epilogue.bind(self)); } /** * Inherit from `Base.prototype`. */ function F(){}; F.prototype = Base.prototype; List.prototype = new F; List.prototype.constructor = List; }); // module: reporters/list.js require.register("reporters/markdown.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , utils = require('../utils'); /** * Expose `Markdown`. */ exports = module.exports = Markdown; /** * Initialize a new `Markdown` reporter. * * @param {Runner} runner * @api public */ function Markdown(runner) { Base.call(this, runner); var self = this , stats = this.stats , level = 0 , buf = ''; function title(str) { return Array(level).join('#') + ' ' + str; } function indent() { return Array(level).join(' '); } function mapTOC(suite, obj) { var ret = obj; obj = obj[suite.title] = obj[suite.title] || { suite: suite }; suite.suites.forEach(function(suite){ mapTOC(suite, obj); }); return ret; } function stringifyTOC(obj, level) { ++level; var buf = ''; var link; for (var key in obj) { if ('suite' == key) continue; if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; if (key) buf += Array(level).join(' ') + link; buf += stringifyTOC(obj[key], level); } --level; return buf; } function generateTOC(suite) { var obj = mapTOC(suite, {}); return stringifyTOC(obj, 0); } generateTOC(runner.suite); runner.on('suite', function(suite){ ++level; var slug = utils.slug(suite.fullTitle()); buf += '' + '\n'; buf += title(suite.title) + '\n'; }); runner.on('suite end', function(suite){ --level; }); runner.on('pass', function(test){ var code = utils.clean(test.fn.toString()); buf += test.title + '.\n'; buf += '\n```js\n'; buf += code + '\n'; buf += '```\n\n'; }); runner.on('end', function(){ process.stdout.write('# TOC\n'); process.stdout.write(generateTOC(runner.suite)); process.stdout.write(buf); }); } }); // module: reporters/markdown.js require.register("reporters/min.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base'); /** * Expose `Min`. */ exports = module.exports = Min; /** * Initialize a new `Min` minimal test reporter (best used with --watch). * * @param {Runner} runner * @api public */ function Min(runner) { Base.call(this, runner); runner.on('start', function(){ // clear screen process.stdout.write('\u001b[2J'); // set cursor position process.stdout.write('\u001b[1;3H'); }); runner.on('end', this.epilogue.bind(this)); } /** * Inherit from `Base.prototype`. */ function F(){}; F.prototype = Base.prototype; Min.prototype = new F; Min.prototype.constructor = Min; }); // module: reporters/min.js require.register("reporters/nyan.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , color = Base.color; /** * Expose `Dot`. */ exports = module.exports = NyanCat; /** * Initialize a new `Dot` matrix test reporter. * * @param {Runner} runner * @api public */ function NyanCat(runner) { Base.call(this, runner); var self = this , stats = this.stats , width = Base.window.width * .75 | 0 , rainbowColors = this.rainbowColors = self.generateColors() , colorIndex = this.colorIndex = 0 , numerOfLines = this.numberOfLines = 4 , trajectories = this.trajectories = [[], [], [], []] , nyanCatWidth = this.nyanCatWidth = 11 , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) , scoreboardWidth = this.scoreboardWidth = 5 , tick = this.tick = 0 , n = 0; runner.on('start', function(){ Base.cursor.hide(); self.draw(); }); runner.on('pending', function(test){ self.draw(); }); runner.on('pass', function(test){ self.draw(); }); runner.on('fail', function(test, err){ self.draw(); }); runner.on('end', function(){ Base.cursor.show(); for (var i = 0; i < self.numberOfLines; i++) write('\n'); self.epilogue(); }); } /** * Draw the nyan cat * * @api private */ NyanCat.prototype.draw = function(){ this.appendRainbow(); this.drawScoreboard(); this.drawRainbow(); this.drawNyanCat(); this.tick = !this.tick; }; /** * Draw the "scoreboard" showing the number * of passes, failures and pending tests. * * @api private */ NyanCat.prototype.drawScoreboard = function(){ var stats = this.stats; var colors = Base.colors; function draw(color, n) { write(' '); write('\u001b[' + color + 'm' + n + '\u001b[0m'); write('\n'); } draw(colors.green, stats.passes); draw(colors.fail, stats.failures); draw(colors.pending, stats.pending); write('\n'); this.cursorUp(this.numberOfLines); }; /** * Append the rainbow. * * @api private */ NyanCat.prototype.appendRainbow = function(){ var segment = this.tick ? '_' : '-'; var rainbowified = this.rainbowify(segment); for (var index = 0; index < this.numberOfLines; index++) { var trajectory = this.trajectories[index]; if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); trajectory.push(rainbowified); } }; /** * Draw the rainbow. * * @api private */ NyanCat.prototype.drawRainbow = function(){ var self = this; this.trajectories.forEach(function(line, index) { write('\u001b[' + self.scoreboardWidth + 'C'); write(line.join('')); write('\n'); }); this.cursorUp(this.numberOfLines); }; /** * Draw the nyan cat * * @api private */ NyanCat.prototype.drawNyanCat = function() { var self = this; var startWidth = this.scoreboardWidth + this.trajectories[0].length; var color = '\u001b[' + startWidth + 'C'; var padding = ''; write(color); write('_,------,'); write('\n'); write(color); padding = self.tick ? ' ' : ' '; write('_|' + padding + '/\\_/\\ '); write('\n'); write(color); padding = self.tick ? '_' : '__'; var tail = self.tick ? '~' : '^'; var face; write(tail + '|' + padding + this.face() + ' '); write('\n'); write(color); padding = self.tick ? ' ' : ' '; write(padding + '"" "" '); write('\n'); this.cursorUp(this.numberOfLines); }; /** * Draw nyan cat face. * * @return {String} * @api private */ NyanCat.prototype.face = function() { var stats = this.stats; if (stats.failures) { return '( x .x)'; } else if (stats.pending) { return '( o .o)'; } else if(stats.passes) { return '( ^ .^)'; } else { return '( - .-)'; } }; /** * Move cursor up `n`. * * @param {Number} n * @api private */ NyanCat.prototype.cursorUp = function(n) { write('\u001b[' + n + 'A'); }; /** * Move cursor down `n`. * * @param {Number} n * @api private */ NyanCat.prototype.cursorDown = function(n) { write('\u001b[' + n + 'B'); }; /** * Generate rainbow colors. * * @return {Array} * @api private */ NyanCat.prototype.generateColors = function(){ var colors = []; for (var i = 0; i < (6 * 7); i++) { var pi3 = Math.floor(Math.PI / 3); var n = (i * (1.0 / 6)); var r = Math.floor(3 * Math.sin(n) + 3); var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); colors.push(36 * r + 6 * g + b + 16); } return colors; }; /** * Apply rainbow to the given `str`. * * @param {String} str * @return {String} * @api private */ NyanCat.prototype.rainbowify = function(str){ var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; this.colorIndex += 1; return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; }; /** * Stdout helper. */ function write(string) { process.stdout.write(string); } /** * Inherit from `Base.prototype`. */ function F(){}; F.prototype = Base.prototype; NyanCat.prototype = new F; NyanCat.prototype.constructor = NyanCat; }); // module: reporters/nyan.js require.register("reporters/progress.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , cursor = Base.cursor , color = Base.color; /** * Expose `Progress`. */ exports = module.exports = Progress; /** * General progress bar color. */ Base.colors.progress = 90; /** * Initialize a new `Progress` bar test reporter. * * @param {Runner} runner * @param {Object} options * @api public */ function Progress(runner, options) { Base.call(this, runner); var self = this , options = options || {} , stats = this.stats , width = Base.window.width * .50 | 0 , total = runner.total , complete = 0 , max = Math.max , lastN = -1; // default chars options.open = options.open || '['; options.complete = options.complete || '▬'; options.incomplete = options.incomplete || Base.symbols.dot; options.close = options.close || ']'; options.verbose = false; // tests started runner.on('start', function(){ console.log(); cursor.hide(); }); // tests complete runner.on('test end', function(){ complete++; var incomplete = total - complete , percent = complete / total , n = width * percent | 0 , i = width - n; if (lastN === n && !options.verbose) { // Don't re-render the line if it hasn't changed return; } lastN = n; cursor.CR(); process.stdout.write('\u001b[J'); process.stdout.write(color('progress', ' ' + options.open)); process.stdout.write(Array(n).join(options.complete)); process.stdout.write(Array(i).join(options.incomplete)); process.stdout.write(color('progress', options.close)); if (options.verbose) { process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); } }); // tests are complete, output some stats // and the failures if any runner.on('end', function(){ cursor.show(); console.log(); self.epilogue(); }); } /** * Inherit from `Base.prototype`. */ function F(){}; F.prototype = Base.prototype; Progress.prototype = new F; Progress.prototype.constructor = Progress; }); // module: reporters/progress.js require.register("reporters/spec.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , cursor = Base.cursor , color = Base.color; /** * Expose `Spec`. */ exports = module.exports = Spec; /** * Initialize a new `Spec` test reporter. * * @param {Runner} runner * @api public */ function Spec(runner) { Base.call(this, runner); var self = this , stats = this.stats , indents = 0 , n = 0; function indent() { return Array(indents).join(' ') } runner.on('start', function(){ console.log(); }); runner.on('suite', function(suite){ ++indents; console.log(color('suite', '%s%s'), indent(), suite.title); }); runner.on('suite end', function(suite){ --indents; if (1 == indents) console.log(); }); runner.on('pending', function(test){ var fmt = indent() + color('pending', ' - %s'); console.log(fmt, test.title); }); runner.on('pass', function(test){ if ('fast' == test.speed) { var fmt = indent() + color('checkmark', ' ' + Base.symbols.ok) + color('pass', ' %s '); cursor.CR(); console.log(fmt, test.title); } else { var fmt = indent() + color('checkmark', ' ' + Base.symbols.ok) + color('pass', ' %s ') + color(test.speed, '(%dms)'); cursor.CR(); console.log(fmt, test.title, test.duration); } }); runner.on('fail', function(test, err){ cursor.CR(); console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); }); runner.on('end', self.epilogue.bind(self)); } /** * Inherit from `Base.prototype`. */ function F(){}; F.prototype = Base.prototype; Spec.prototype = new F; Spec.prototype.constructor = Spec; }); // module: reporters/spec.js require.register("reporters/tap.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , cursor = Base.cursor , color = Base.color; /** * Expose `TAP`. */ exports = module.exports = TAP; /** * Initialize a new `TAP` reporter. * * @param {Runner} runner * @api public */ function TAP(runner) { Base.call(this, runner); var self = this , stats = this.stats , n = 1 , passes = 0 , failures = 0; runner.on('start', function(){ var total = runner.grepTotal(runner.suite); console.log('%d..%d', 1, total); }); runner.on('test end', function(){ ++n; }); runner.on('pending', function(test){ console.log('ok %d %s # SKIP -', n, title(test)); }); runner.on('pass', function(test){ passes++; console.log('ok %d %s', n, title(test)); }); runner.on('fail', function(test, err){ failures++; console.log('not ok %d %s', n, title(test)); if (err.stack) console.log(err.stack.replace(/^/gm, ' ')); }); runner.on('end', function(){ console.log('# tests ' + (passes + failures)); console.log('# pass ' + passes); console.log('# fail ' + failures); }); } /** * Return a TAP-safe title of `test` * * @param {Object} test * @return {String} * @api private */ function title(test) { return test.fullTitle().replace(/#/g, ''); } }); // module: reporters/tap.js require.register("reporters/xunit.js", function(module, exports, require){ /** * Module dependencies. */ var Base = require('./base') , utils = require('../utils') , escape = utils.escape; /** * Save timer references to avoid Sinon interfering (see GH-237). */ var Date = global.Date , setTimeout = global.setTimeout , setInterval = global.setInterval , clearTimeout = global.clearTimeout , clearInterval = global.clearInterval; /** * Expose `XUnit`. */ exports = module.exports = XUnit; /** * Initialize a new `XUnit` reporter. * * @param {Runner} runner * @api public */ function XUnit(runner) { Base.call(this, runner); var stats = this.stats , tests = [] , self = this; runner.on('pending', function(test){ tests.push(test); }); runner.on('pass', function(test){ tests.push(test); }); runner.on('fail', function(test){ tests.push(test); }); runner.on('end', function(){ console.log(tag('testsuite', { name: 'Mocha Tests' , tests: stats.tests , failures: stats.failures , errors: stats.failures , skipped: stats.tests - stats.failures - stats.passes , timestamp: (new Date).toUTCString() , time: (stats.duration / 1000) || 0 }, false)); tests.forEach(test); console.log(''); }); } /** * Inherit from `Base.prototype`. */ function F(){}; F.prototype = Base.prototype; XUnit.prototype = new F; XUnit.prototype.constructor = XUnit; /** * Output tag for the given `test.` */ function test(test) { var attrs = { classname: test.parent.fullTitle() , name: test.title , time: (test.duration / 1000) || 0 }; if ('failed' == test.state) { var err = test.err; console.log(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack)))); } else if (test.pending) { console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); } else { console.log(tag('testcase', attrs, true) ); } } /** * HTML tag helper. */ function tag(name, attrs, close, content) { var end = close ? '/>' : '>' , pairs = [] , tag; for (var key in attrs) { pairs.push(key + '="' + escape(attrs[key]) + '"'); } tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; if (content) tag += content + ''; } }); // module: reporters/xunit.js require.register("runnable.js", function(module, exports, require){ /** * Module dependencies. */ var EventEmitter = require('browser/events').EventEmitter , debug = require('browser/debug')('mocha:runnable') , milliseconds = require('./ms'); /** * Save timer references to avoid Sinon interfering (see GH-237). */ var Date = global.Date , setTimeout = global.setTimeout , setInterval = global.setInterval , clearTimeout = global.clearTimeout , clearInterval = global.clearInterval; /** * Object#toString(). */ var toString = Object.prototype.toString; /** * Expose `Runnable`. */ module.exports = Runnable; /** * Initialize a new `Runnable` with the given `title` and callback `fn`. * * @param {String} title * @param {Function} fn * @api private */ function Runnable(title, fn) { this.title = title; this.fn = fn; this.async = fn && fn.length; this.sync = ! this.async; this._timeout = 1000 * 60 * 5; this._slow = 75; this._enableTimeouts = true; this.timedOut = false; this._trace = new Error('done() called multiple times') } /** * Inherit from `EventEmitter.prototype`. */ function F(){}; F.prototype = EventEmitter.prototype; Runnable.prototype = new F; Runnable.prototype.constructor = Runnable; /** * Set & get timeout `ms`. * * @param {Number|String} ms * @return {Runnable|Number} ms or self * @api private */ Runnable.prototype.timeout = function(ms){ if (0 == arguments.length) return this._timeout; if (ms === 0) this._enableTimeouts = false; if ('string' == typeof ms) ms = milliseconds(ms); debug('timeout %d', ms); this._timeout = ms; if (this.timer) this.resetTimeout(); return this; }; /** * Set & get slow `ms`. * * @param {Number|String} ms * @return {Runnable|Number} ms or self * @api private */ Runnable.prototype.slow = function(ms){ if (0 === arguments.length) return this._slow; if ('string' == typeof ms) ms = milliseconds(ms); debug('timeout %d', ms); this._slow = ms; return this; }; /** * Set and & get timeout `enabled`. * * @param {Boolean} enabled * @return {Runnable|Boolean} enabled or self * @api private */ Runnable.prototype.enableTimeouts = function(enabled){ if (arguments.length === 0) return this._enableTimeouts; debug('enableTimeouts %s', enabled); this._enableTimeouts = enabled; return this; }; /** * Return the full title generated by recursively * concatenating the parent's full title. * * @return {String} * @api public */ Runnable.prototype.fullTitle = function(){ return this.parent.fullTitle() + ' ' + this.title; }; /** * Clear the timeout. * * @api private */ Runnable.prototype.clearTimeout = function(){ clearTimeout(this.timer); }; /** * Inspect the runnable void of private properties. * * @return {String} * @api private */ Runnable.prototype.inspect = function(){ return JSON.stringify(this, function(key, val){ if ('_' == key[0]) return; if ('parent' == key) return '#'; if ('ctx' == key) return '#'; return val; }, 2); }; /** * Reset the timeout. * * @api private */ Runnable.prototype.resetTimeout = function(){ var self = this; var ms = this.timeout() || 1e9; if (!this._enableTimeouts) return; this.clearTimeout(); this.timer = setTimeout(function(){ if (!self._enableTimeouts) return; self.callback(new Error('timeout of ' + ms + 'ms exceeded')); self.timedOut = true; }, ms); }; /** * Whitelist these globals for this test run * * @api private */ Runnable.prototype.globals = function(arr){ var self = this; this._allowedGlobals = arr; }; /** * Run the test and invoke `fn(err)`. * * @param {Function} fn * @api private */ Runnable.prototype.run = function(fn){ var self = this , start = new Date , ctx = this.ctx , finished , emitted; // Some times the ctx exists but it is not runnable if (ctx && ctx.runnable) ctx.runnable(this); // called multiple times function multiple(err) { if (emitted) return; emitted = true; self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate')); } // finished function done(err) { var ms = self.timeout(); if (self.timedOut) return; if (finished) return multiple(err || self._trace); self.clearTimeout(); self.duration = new Date - start; finished = true; if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded'); fn(err); } // for .resetTimeout() this.callback = done; // explicit async with `done` argument if (this.async) { this.resetTimeout(); try { this.fn.call(ctx, function(err){ if (err instanceof Error || toString.call(err) === "[object Error]") return done(err); if (null != err) { if (Object.prototype.toString.call(err) === '[object Object]') { return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err))); } else { return done(new Error('done() invoked with non-Error: ' + err)); } } done(); }); } catch (err) { done(err); } return; } if (this.asyncOnly) { return done(new Error('--async-only option in use without declaring `done()`')); } // sync or promise-returning try { if (this.pending) { done(); } else { callFn(this.fn); } } catch (err) { done(err); } function callFn(fn) { var result = fn.call(ctx); if (result && typeof result.then === 'function') { self.resetTimeout(); result .then(function() { done() }, function(reason) { done(reason || new Error('Promise rejected with no or falsy reason')) }); } else { done(); } } }; }); // module: runnable.js require.register("runner.js", function(module, exports, require){ /** * Module dependencies. */ var EventEmitter = require('browser/events').EventEmitter , debug = require('browser/debug')('mocha:runner') , Test = require('./test') , utils = require('./utils') , filter = utils.filter , keys = utils.keys; /** * Non-enumerable globals. */ var globals = [ 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'XMLHttpRequest', 'Date' ]; /** * Expose `Runner`. */ module.exports = Runner; /** * Initialize a `Runner` for the given `suite`. * * Events: * * - `start` execution started * - `end` execution complete * - `suite` (suite) test suite execution started * - `suite end` (suite) all tests (and sub-suites) have finished * - `test` (test) test execution started * - `test end` (test) test completed * - `hook` (hook) hook execution started * - `hook end` (hook) hook complete * - `pass` (test) test passed * - `fail` (test, err) test failed * - `pending` (test) test pending * * @api public */ function Runner(suite) { var self = this; this._globals = []; this._abort = false; this.suite = suite; this.total = suite.total(); this.failures = 0; this.on('test end', function(test){ self.checkGlobals(test); }); this.on('hook end', function(hook){ self.checkGlobals(hook); }); this.grep(/.*/); this.globals(this.globalProps().concat(extraGlobals())); } /** * Wrapper for setImmediate, process.nextTick, or browser polyfill. * * @param {Function} fn * @api private */ Runner.immediately = global.setImmediate || process.nextTick; /** * Inherit from `EventEmitter.prototype`. */ function F(){}; F.prototype = EventEmitter.prototype; Runner.prototype = new F; Runner.prototype.constructor = Runner; /** * Run tests with full titles matching `re`. Updates runner.total * with number of tests matched. * * @param {RegExp} re * @param {Boolean} invert * @return {Runner} for chaining * @api public */ Runner.prototype.grep = function(re, invert){ debug('grep %s', re); this._grep = re; this._invert = invert; this.total = this.grepTotal(this.suite); return this; }; /** * Returns the number of tests matching the grep search for the * given suite. * * @param {Suite} suite * @return {Number} * @api public */ Runner.prototype.grepTotal = function(suite) { var self = this; var total = 0; suite.eachTest(function(test){ var match = self._grep.test(test.fullTitle()); if (self._invert) match = !match; if (match) total++; }); return total; }; /** * Return a list of global properties. * * @return {Array} * @api private */ Runner.prototype.globalProps = function() { var props = utils.keys(global); // non-enumerables for (var i = 0; i < globals.length; ++i) { if (~utils.indexOf(props, globals[i])) continue; props.push(globals[i]); } return props; }; /** * Allow the given `arr` of globals. * * @param {Array} arr * @return {Runner} for chaining * @api public */ Runner.prototype.globals = function(arr){ if (0 == arguments.length) return this._globals; debug('globals %j', arr); this._globals = this._globals.concat(arr); return this; }; /** * Check for global variable leaks. * * @api private */ Runner.prototype.checkGlobals = function(test){ if (this.ignoreLeaks) return; var ok = this._globals; var globals = this.globalProps(); var leaks; if (test) { ok = ok.concat(test._allowedGlobals || []); } if(this.prevGlobalsLength == globals.length) return; this.prevGlobalsLength = globals.length; leaks = filterLeaks(ok, globals); this._globals = this._globals.concat(leaks); if (leaks.length > 1) { this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); } else if (leaks.length) { this.fail(test, new Error('global leak detected: ' + leaks[0])); } }; /** * Fail the given `test`. * * @param {Test} test * @param {Error} err * @api private */ Runner.prototype.fail = function(test, err){ ++this.failures; test.state = 'failed'; if ('string' == typeof err) { err = new Error('the string "' + err + '" was thrown, throw an Error :)'); } this.emit('fail', test, err); }; /** * Fail the given `hook` with `err`. * * Hook failures work in the following pattern: * - If bail, then exit * - Failed `before` hook skips all tests in a suite and subsuites, * but jumps to corresponding `after` hook * - Failed `before each` hook skips remaining tests in a * suite and jumps to corresponding `after each` hook, * which is run only once * - Failed `after` hook does not alter * execution order * - Failed `after each` hook skips remaining tests in a * suite and subsuites, but executes other `after each` * hooks * * @param {Hook} hook * @param {Error} err * @api private */ Runner.prototype.failHook = function(hook, err){ this.fail(hook, err); if (this.suite.bail()) { this.emit('end'); } }; /** * Run hook `name` callbacks and then invoke `fn()`. * * @param {String} name * @param {Function} function * @api private */ Runner.prototype.hook = function(name, fn){ var suite = this.suite , hooks = suite['_' + name] , self = this , timer; function next(i) { var hook = hooks[i]; if (!hook) return fn(); if (self.failures && suite.bail()) return fn(); self.currentRunnable = hook; hook.ctx.currentTest = self.test; self.emit('hook', hook); hook.on('error', function(err){ self.failHook(hook, err); }); hook.run(function(err){ hook.removeAllListeners('error'); var testError = hook.error(); if (testError) self.fail(self.test, testError); if (err) { self.failHook(hook, err); // stop executing hooks, notify callee of hook err return fn(err); } self.emit('hook end', hook); delete hook.ctx.currentTest; next(++i); }); } Runner.immediately(function(){ next(0); }); }; /** * Run hook `name` for the given array of `suites` * in order, and callback `fn(err, errSuite)`. * * @param {String} name * @param {Array} suites * @param {Function} fn * @api private */ Runner.prototype.hooks = function(name, suites, fn){ var self = this , orig = this.suite; function next(suite) { self.suite = suite; if (!suite) { self.suite = orig; return fn(); } self.hook(name, function(err){ if (err) { var errSuite = self.suite; self.suite = orig; return fn(err, errSuite); } next(suites.pop()); }); } next(suites.pop()); }; /** * Run hooks from the top level down. * * @param {String} name * @param {Function} fn * @api private */ Runner.prototype.hookUp = function(name, fn){ var suites = [this.suite].concat(this.parents()).reverse(); this.hooks(name, suites, fn); }; /** * Run hooks from the bottom up. * * @param {String} name * @param {Function} fn * @api private */ Runner.prototype.hookDown = function(name, fn){ var suites = [this.suite].concat(this.parents()); this.hooks(name, suites, fn); }; /** * Return an array of parent Suites from * closest to furthest. * * @return {Array} * @api private */ Runner.prototype.parents = function(){ var suite = this.suite , suites = []; while (suite = suite.parent) suites.push(suite); return suites; }; /** * Run the current test and callback `fn(err)`. * * @param {Function} fn * @api private */ Runner.prototype.runTest = function(fn){ var test = this.test , self = this; if (this.asyncOnly) test.asyncOnly = true; try { test.on('error', function(err){ self.fail(test, err); }); test.run(fn); } catch (err) { fn(err); } }; /** * Run tests in the given `suite` and invoke * the callback `fn()` when complete. * * @param {Suite} suite * @param {Function} fn * @api private */ Runner.prototype.runTests = function(suite, fn){ var self = this , tests = suite.tests.slice() , test; function hookErr(err, errSuite, after) { // before/after Each hook for errSuite failed: var orig = self.suite; // for failed 'after each' hook start from errSuite parent, // otherwise start from errSuite itself self.suite = after ? errSuite.parent : errSuite; if (self.suite) { // call hookUp afterEach self.hookUp('afterEach', function(err2, errSuite2) { self.suite = orig; // some hooks may fail even now if (err2) return hookErr(err2, errSuite2, true); // report error suite fn(errSuite); }); } else { // there is no need calling other 'after each' hooks self.suite = orig; fn(errSuite); } } function next(err, errSuite) { // if we bail after first err if (self.failures && suite._bail) return fn(); if (self._abort) return fn(); if (err) return hookErr(err, errSuite, true); // next test test = tests.shift(); // all done if (!test) return fn(); // grep var match = self._grep.test(test.fullTitle()); if (self._invert) match = !match; if (!match) return next(); // pending if (test.pending) { self.emit('pending', test); self.emit('test end', test); return next(); } // execute test and hook(s) self.emit('test', self.test = test); self.hookDown('beforeEach', function(err, errSuite){ if (err) return hookErr(err, errSuite, false); self.currentRunnable = self.test; self.runTest(function(err){ test = self.test; if (err) { self.fail(test, err); self.emit('test end', test); return self.hookUp('afterEach', next); } test.state = 'passed'; self.emit('pass', test); self.emit('test end', test); self.hookUp('afterEach', next); }); }); } this.next = next; next(); }; /** * Run the given `suite` and invoke the * callback `fn()` when complete. * * @param {Suite} suite * @param {Function} fn * @api private */ Runner.prototype.runSuite = function(suite, fn){ var total = this.grepTotal(suite) , self = this , i = 0; debug('run suite %s', suite.fullTitle()); if (!total) return fn(); this.emit('suite', this.suite = suite); function next(errSuite) { if (errSuite) { // current suite failed on a hook from errSuite if (errSuite == suite) { // if errSuite is current suite // continue to the next sibling suite return done(); } else { // errSuite is among the parents of current suite // stop execution of errSuite and all sub-suites return done(errSuite); } } if (self._abort) return done(); var curr = suite.suites[i++]; if (!curr) return done(); self.runSuite(curr, next); } function done(errSuite) { self.suite = suite; self.hook('afterAll', function(){ self.emit('suite end', suite); fn(errSuite); }); } this.hook('beforeAll', function(err){ if (err) return done(); self.runTests(suite, next); }); }; /** * Handle uncaught exceptions. * * @param {Error} err * @api private */ Runner.prototype.uncaught = function(err){ if (err) { debug('uncaught exception %s', err !== function () { return this; }.call(err) ? err : ( err.message || err )); } else { debug('uncaught undefined exception'); err = new Error('Caught undefined error, did you throw without specifying what?'); } err.uncaught = true; var runnable = this.currentRunnable; if (!runnable) return; var wasAlreadyDone = runnable.state; this.fail(runnable, err); runnable.clearTimeout(); if (wasAlreadyDone) return; // recover from test if ('test' == runnable.type) { this.emit('test end', runnable); this.hookUp('afterEach', this.next); return; } // bail on hooks this.emit('end'); }; /** * Run the root suite and invoke `fn(failures)` * on completion. * * @param {Function} fn * @return {Runner} for chaining * @api public */ Runner.prototype.run = function(fn){ var self = this , fn = fn || function(){}; function uncaught(err){ self.uncaught(err); } debug('start'); // callback this.on('end', function(){ debug('end'); process.removeListener('uncaughtException', uncaught); fn(self.failures); }); // run suites this.emit('start'); this.runSuite(this.suite, function(){ debug('finished running'); self.emit('end'); }); // uncaught exception process.on('uncaughtException', uncaught); return this; }; /** * Cleanly abort execution * * @return {Runner} for chaining * @api public */ Runner.prototype.abort = function(){ debug('aborting'); this._abort = true; }; /** * Filter leaks with the given globals flagged as `ok`. * * @param {Array} ok * @param {Array} globals * @return {Array} * @api private */ function filterLeaks(ok, globals) { return filter(globals, function(key){ // Firefox and Chrome exposes iframes as index inside the window object if (/^d+/.test(key)) return false; // in firefox // if runner runs in an iframe, this iframe's window.getInterface method not init at first // it is assigned in some seconds if (global.navigator && /^getInterface/.test(key)) return false; // an iframe could be approached by window[iframeIndex] // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak if (global.navigator && /^\d+/.test(key)) return false; // Opera and IE expose global variables for HTML element IDs (issue #243) if (/^mocha-/.test(key)) return false; var matched = filter(ok, function(ok){ if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]); return key == ok; }); return matched.length == 0 && (!global.navigator || 'onerror' !== key); }); } /** * Array of globals dependent on the environment. * * @return {Array} * @api private */ function extraGlobals() { if (typeof(process) === 'object' && typeof(process.version) === 'string') { var nodeVersion = process.version.split('.').reduce(function(a, v) { return a << 8 | v; }); // 'errno' was renamed to process._errno in v0.9.11. if (nodeVersion < 0x00090B) { return ['errno']; } } return []; } }); // module: runner.js require.register("suite.js", function(module, exports, require){ /** * Module dependencies. */ var EventEmitter = require('browser/events').EventEmitter , debug = require('browser/debug')('mocha:suite') , milliseconds = require('./ms') , utils = require('./utils') , Hook = require('./hook'); /** * Expose `Suite`. */ exports = module.exports = Suite; /** * Create a new `Suite` with the given `title` * and parent `Suite`. When a suite with the * same title is already present, that suite * is returned to provide nicer reporter * and more flexible meta-testing. * * @param {Suite} parent * @param {String} title * @return {Suite} * @api public */ exports.create = function(parent, title){ var suite = new Suite(title, parent.ctx); suite.parent = parent; if (parent.pending) suite.pending = true; title = suite.fullTitle(); parent.addSuite(suite); return suite; }; /** * Initialize a new `Suite` with the given * `title` and `ctx`. * * @param {String} title * @param {Context} ctx * @api private */ function Suite(title, parentContext) { this.title = title; var context = function() {}; context.prototype = parentContext; this.ctx = new context(); this.suites = []; this.tests = []; this.pending = false; this._beforeEach = []; this._beforeAll = []; this._afterEach = []; this._afterAll = []; this.root = !title; this._timeout = 1000 * 60 * 5; this._enableTimeouts = true; this._slow = 75; this._bail = false; } /** * Inherit from `EventEmitter.prototype`. */ function F(){}; F.prototype = EventEmitter.prototype; Suite.prototype = new F; Suite.prototype.constructor = Suite; /** * Return a clone of this `Suite`. * * @return {Suite} * @api private */ Suite.prototype.clone = function(){ var suite = new Suite(this.title); debug('clone'); suite.ctx = this.ctx; suite.timeout(this.timeout()); suite.enableTimeouts(this.enableTimeouts()); suite.slow(this.slow()); suite.bail(this.bail()); return suite; }; /** * Set timeout `ms` or short-hand such as "2s". * * @param {Number|String} ms * @return {Suite|Number} for chaining * @api private */ Suite.prototype.timeout = function(ms){ if (0 == arguments.length) return this._timeout; if (ms === 0) this._enableTimeouts = false; if ('string' == typeof ms) ms = milliseconds(ms); debug('timeout %d', ms); this._timeout = parseInt(ms, 10); return this; }; /** * Set timeout `enabled`. * * @param {Boolean} enabled * @return {Suite|Boolean} self or enabled * @api private */ Suite.prototype.enableTimeouts = function(enabled){ if (arguments.length === 0) return this._enableTimeouts; debug('enableTimeouts %s', enabled); this._enableTimeouts = enabled; return this; }; /** * Set slow `ms` or short-hand such as "2s". * * @param {Number|String} ms * @return {Suite|Number} for chaining * @api private */ Suite.prototype.slow = function(ms){ if (0 === arguments.length) return this._slow; if ('string' == typeof ms) ms = milliseconds(ms); debug('slow %d', ms); this._slow = ms; return this; }; /** * Sets whether to bail after first error. * * @parma {Boolean} bail * @return {Suite|Number} for chaining * @api private */ Suite.prototype.bail = function(bail){ if (0 == arguments.length) return this._bail; debug('bail %s', bail); this._bail = bail; return this; }; /** * Run `fn(test[, done])` before running tests. * * @param {Function} fn * @return {Suite} for chaining * @api private */ Suite.prototype.beforeAll = function(title, fn){ if (this.pending) return this; if ('function' === typeof title) { fn = title; title = fn.name; } title = '"before all" hook' + (title ? ': ' + title : ''); var hook = new Hook(title, fn); hook.parent = this; hook.timeout(this.timeout()); hook.enableTimeouts(this.enableTimeouts()); hook.slow(this.slow()); hook.ctx = this.ctx; this._beforeAll.push(hook); this.emit('beforeAll', hook); return this; }; /** * Run `fn(test[, done])` after running tests. * * @param {Function} fn * @return {Suite} for chaining * @api private */ Suite.prototype.afterAll = function(title, fn){ if (this.pending) return this; if ('function' === typeof title) { fn = title; title = fn.name; } title = '"after all" hook' + (title ? ': ' + title : ''); var hook = new Hook(title, fn); hook.parent = this; hook.timeout(this.timeout()); hook.enableTimeouts(this.enableTimeouts()); hook.slow(this.slow()); hook.ctx = this.ctx; this._afterAll.push(hook); this.emit('afterAll', hook); return this; }; /** * Run `fn(test[, done])` before each test case. * * @param {Function} fn * @return {Suite} for chaining * @api private */ Suite.prototype.beforeEach = function(title, fn){ if (this.pending) return this; if ('function' === typeof title) { fn = title; title = fn.name; } title = '"before each" hook' + (title ? ': ' + title : ''); var hook = new Hook(title, fn); hook.parent = this; hook.timeout(this.timeout()); hook.enableTimeouts(this.enableTimeouts()); hook.slow(this.slow()); hook.ctx = this.ctx; this._beforeEach.push(hook); this.emit('beforeEach', hook); return this; }; /** * Run `fn(test[, done])` after each test case. * * @param {Function} fn * @return {Suite} for chaining * @api private */ Suite.prototype.afterEach = function(title, fn){ if (this.pending) return this; if ('function' === typeof title) { fn = title; title = fn.name; } title = '"after each" hook' + (title ? ': ' + title : ''); var hook = new Hook(title, fn); hook.parent = this; hook.timeout(this.timeout()); hook.enableTimeouts(this.enableTimeouts()); hook.slow(this.slow()); hook.ctx = this.ctx; this._afterEach.push(hook); this.emit('afterEach', hook); return this; }; /** * Add a test `suite`. * * @param {Suite} suite * @return {Suite} for chaining * @api private */ Suite.prototype.addSuite = function(suite){ suite.parent = this; suite.timeout(this.timeout()); suite.enableTimeouts(this.enableTimeouts()); suite.slow(this.slow()); suite.bail(this.bail()); this.suites.push(suite); this.emit('suite', suite); return this; }; /** * Add a `test` to this suite. * * @param {Test} test * @return {Suite} for chaining * @api private */ Suite.prototype.addTest = function(test){ test.parent = this; test.timeout(this.timeout()); test.enableTimeouts(this.enableTimeouts()); test.slow(this.slow()); test.ctx = this.ctx; this.tests.push(test); this.emit('test', test); return this; }; /** * Return the full title generated by recursively * concatenating the parent's full title. * * @return {String} * @api public */ Suite.prototype.fullTitle = function(){ if (this.parent) { var full = this.parent.fullTitle(); if (full) return full + ' ' + this.title; } return this.title; }; /** * Return the total number of tests. * * @return {Number} * @api public */ Suite.prototype.total = function(){ return utils.reduce(this.suites, function(sum, suite){ return sum + suite.total(); }, 0) + this.tests.length; }; /** * Iterates through each suite recursively to find * all tests. Applies a function in the format * `fn(test)`. * * @param {Function} fn * @return {Suite} * @api private */ Suite.prototype.eachTest = function(fn){ utils.forEach(this.tests, fn); utils.forEach(this.suites, function(suite){ suite.eachTest(fn); }); return this; }; }); // module: suite.js require.register("test.js", function(module, exports, require){ /** * Module dependencies. */ var Runnable = require('./runnable'); /** * Expose `Test`. */ module.exports = Test; /** * Initialize a new `Test` with the given `title` and callback `fn`. * * @param {String} title * @param {Function} fn * @api private */ function Test(title, fn) { Runnable.call(this, title, fn); this.pending = !fn; this.type = 'test'; } /** * Inherit from `Runnable.prototype`. */ function F(){}; F.prototype = Runnable.prototype; Test.prototype = new F; Test.prototype.constructor = Test; }); // module: test.js require.register("utils.js", function(module, exports, require){ /** * Module dependencies. */ var fs = require('browser/fs') , path = require('browser/path') , basename = path.basename , exists = fs.existsSync || path.existsSync , glob = require('browser/glob') , join = path.join , debug = require('browser/debug')('mocha:watch'); /** * Ignored directories. */ var ignore = ['node_modules', '.git']; /** * Escape special characters in the given string of html. * * @param {String} html * @return {String} * @api private */ exports.escape = function(html){ return String(html) .replace(/&/g, '&') .replace(/"/g, '"') .replace(//g, '>'); }; /** * Array#forEach (<=IE8) * * @param {Array} array * @param {Function} fn * @param {Object} scope * @api private */ exports.forEach = function(arr, fn, scope){ for (var i = 0, l = arr.length; i < l; i++) fn.call(scope, arr[i], i); }; /** * Array#map (<=IE8) * * @param {Array} array * @param {Function} fn * @param {Object} scope * @api private */ exports.map = function(arr, fn, scope){ var result = []; for (var i = 0, l = arr.length; i < l; i++) result.push(fn.call(scope, arr[i], i)); return result; }; /** * Array#indexOf (<=IE8) * * @parma {Array} arr * @param {Object} obj to find index of * @param {Number} start * @api private */ exports.indexOf = function(arr, obj, start){ for (var i = start || 0, l = arr.length; i < l; i++) { if (arr[i] === obj) return i; } return -1; }; /** * Array#reduce (<=IE8) * * @param {Array} array * @param {Function} fn * @param {Object} initial value * @api private */ exports.reduce = function(arr, fn, val){ var rval = val; for (var i = 0, l = arr.length; i < l; i++) { rval = fn(rval, arr[i], i, arr); } return rval; }; /** * Array#filter (<=IE8) * * @param {Array} array * @param {Function} fn * @api private */ exports.filter = function(arr, fn){ var ret = []; for (var i = 0, l = arr.length; i < l; i++) { var val = arr[i]; if (fn(val, i, arr)) ret.push(val); } return ret; }; /** * Object.keys (<=IE8) * * @param {Object} obj * @return {Array} keys * @api private */ exports.keys = Object.keys || function(obj) { var keys = [] , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 for (var key in obj) { if (has.call(obj, key)) { keys.push(key); } } return keys; }; /** * Watch the given `files` for changes * and invoke `fn(file)` on modification. * * @param {Array} files * @param {Function} fn * @api private */ exports.watch = function(files, fn){ var options = { interval: 100 }; files.forEach(function(file){ debug('file %s', file); fs.watchFile(file, options, function(curr, prev){ if (prev.mtime < curr.mtime) fn(file); }); }); }; /** * Ignored files. */ function ignored(path){ return !~ignore.indexOf(path); } /** * Lookup files in the given `dir`. * * @return {Array} * @api private */ exports.files = function(dir, ext, ret){ ret = ret || []; ext = ext || ['js']; var re = new RegExp('\\.(' + ext.join('|') + ')$'); fs.readdirSync(dir) .filter(ignored) .forEach(function(path){ path = join(dir, path); if (fs.statSync(path).isDirectory()) { exports.files(path, ext, ret); } else if (path.match(re)) { ret.push(path); } }); return ret; }; /** * Compute a slug from the given `str`. * * @param {String} str * @return {String} * @api private */ exports.slug = function(str){ return str .toLowerCase() .replace(/ +/g, '-') .replace(/[^-\w]/g, ''); }; /** * Strip the function definition from `str`, * and re-indent for pre whitespace. */ exports.clean = function(str) { str = str .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '') .replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '') .replace(/\s+\}$/, ''); var spaces = str.match(/^\n?( *)/)[1].length , tabs = str.match(/^\n?(\t*)/)[1].length , re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm'); str = str.replace(re, ''); return exports.trim(str); }; /** * Trim the given `str`. * * @param {String} str * @return {String} * @api private */ exports.trim = function(str){ return str.replace(/^\s+|\s+$/g, ''); }; /** * Parse the given `qs`. * * @param {String} qs * @return {Object} * @api private */ exports.parseQuery = function(qs){ return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){ var i = pair.indexOf('=') , key = pair.slice(0, i) , val = pair.slice(++i); obj[key] = decodeURIComponent(val); return obj; }, {}); }; /** * Highlight the given string of `js`. * * @param {String} js * @return {String} * @api private */ function highlight(js) { return js .replace(//g, '>') .replace(/\/\/(.*)/gm, '//$1') .replace(/('.*?')/gm, '$1') .replace(/(\d+\.\d+)/gm, '$1') .replace(/(\d+)/gm, '$1') .replace(/\bnew[ \t]+(\w+)/gm, 'new $1') .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') } /** * Highlight the contents of tag `name`. * * @param {String} name * @api private */ exports.highlightTags = function(name) { var code = document.getElementById('mocha').getElementsByTagName(name); for (var i = 0, len = code.length; i < len; ++i) { code[i].innerHTML = highlight(code[i].innerHTML); } }; /** * Stringify `obj`. * * @param {Object} obj * @return {String} * @api private */ exports.stringify = function(obj) { if (obj instanceof RegExp) return obj.toString(); return JSON.stringify(exports.canonicalize(obj), null, 2).replace(/,(\n|$)/g, '$1'); }; /** * Return a new object that has the keys in sorted order. * @param {Object} obj * @param {Array} [stack] * @return {Object} * @api private */ exports.canonicalize = function(obj, stack) { stack = stack || []; if (exports.indexOf(stack, obj) !== -1) return '[Circular]'; var canonicalizedObj; if ({}.toString.call(obj) === '[object Array]') { stack.push(obj); canonicalizedObj = exports.map(obj, function (item) { return exports.canonicalize(item, stack); }); stack.pop(); } else if (typeof obj === 'object' && obj !== null) { stack.push(obj); canonicalizedObj = {}; exports.forEach(exports.keys(obj).sort(), function (key) { canonicalizedObj[key] = exports.canonicalize(obj[key], stack); }); stack.pop(); } else { canonicalizedObj = obj; } return canonicalizedObj; }; /** * Lookup file names at the given `path`. */ exports.lookupFiles = function lookupFiles(path, extensions, recursive) { var files = []; var re = new RegExp('\\.(' + extensions.join('|') + ')$'); if (!exists(path)) { if (exists(path + '.js')) { path += '.js'; } else { files = glob.sync(path); if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'"); return files; } } try { var stat = fs.statSync(path); if (stat.isFile()) return path; } catch (ignored) { return; } fs.readdirSync(path).forEach(function(file){ file = join(path, file); try { var stat = fs.statSync(file); if (stat.isDirectory()) { if (recursive) { files = files.concat(lookupFiles(file, extensions, recursive)); } return; } } catch (ignored) { return; } if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') return; files.push(file); }); return files; }; }); // module: utils.js // The global object is "self" in Web Workers. var global = (function() { return this; })(); /** * Save timer references to avoid Sinon interfering (see GH-237). */ var Date = global.Date; var setTimeout = global.setTimeout; var setInterval = global.setInterval; var clearTimeout = global.clearTimeout; var clearInterval = global.clearInterval; /** * Node shims. * * These are meant only to allow * mocha.js to run untouched, not * to allow running node code in * the browser. */ var process = {}; process.exit = function(status){}; process.stdout = {}; var uncaughtExceptionHandlers = []; var originalOnerrorHandler = global.onerror; /** * Remove uncaughtException listener. * Revert to original onerror handler if previously defined. */ process.removeListener = function(e, fn){ if ('uncaughtException' == e) { if (originalOnerrorHandler) { global.onerror = originalOnerrorHandler; } else { global.onerror = function() {}; } var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn); if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); } } }; /** * Implements uncaughtException listener. */ process.on = function(e, fn){ if ('uncaughtException' == e) { global.onerror = function(err, url, line){ fn(new Error(err + ' (' + url + ':' + line + ')')); return true; }; uncaughtExceptionHandlers.push(fn); } }; /** * Expose mocha. */ var Mocha = global.Mocha = require('mocha'), mocha = global.mocha = new Mocha({ reporter: 'html' }); // The BDD UI is registered by default, but no UI will be functional in the // browser without an explicit call to the overridden `mocha.ui` (see below). // Ensure that this default UI does not expose its methods to the global scope. mocha.suite.removeAllListeners('pre-require'); var immediateQueue = [] , immediateTimeout; function timeslice() { var immediateStart = new Date().getTime(); while (immediateQueue.length && (new Date().getTime() - immediateStart) < 100) { immediateQueue.shift()(); } if (immediateQueue.length) { immediateTimeout = setTimeout(timeslice, 0); } else { immediateTimeout = null; } } /** * High-performance override of Runner.immediately. */ Mocha.Runner.immediately = function(callback) { immediateQueue.push(callback); if (!immediateTimeout) { immediateTimeout = setTimeout(timeslice, 0); } }; /** * Function to allow assertion libraries to throw errors directly into mocha. * This is useful when running tests in a browser because window.onerror will * only receive the 'message' attribute of the Error. */ mocha.throwError = function(err) { Mocha.utils.forEach(uncaughtExceptionHandlers, function (fn) { fn(err); }); throw err; }; /** * Override ui to ensure that the ui functions are initialized. * Normally this would happen in Mocha.prototype.loadFiles. */ mocha.ui = function(ui){ Mocha.prototype.ui.call(this, ui); this.suite.emit('pre-require', global, null, this); return this; }; /** * Setup mocha with the given setting options. */ mocha.setup = function(opts){ if ('string' == typeof opts) opts = { ui: opts }; for (var opt in opts) this[opt](opts[opt]); return this; }; /** * Run mocha, returning the Runner. */ mocha.run = function(fn){ var options = mocha.options; mocha.globals('location'); var query = Mocha.utils.parseQuery(global.location.search || ''); if (query.grep) mocha.grep(query.grep); if (query.invert) mocha.invert(); return Mocha.prototype.run.call(mocha, function(err){ // The DOM Document is not available in Web Workers. var document = global.document; if (document && document.getElementById('mocha') && options.noHighlighting !== true) { Mocha.utils.highlightTags('code'); } if (fn) fn(err); }); }; /** * Expose the process shim. */ Mocha.process = process; })(); ================================================ FILE: test/browser/package.json ================================================ { "name": "bluebird-nw", "version": "1.0.0", "main": "index.html" } ================================================ FILE: test/browser/promise_debug.js ================================================ var Promise = require("../../js/browser/bluebird.min.js"); Promise.longStackTraces(); Promise.config({cancellation:true}); self.Promise = Promise; self.adapter = Promise; ================================================ FILE: test/browser/promise_instrumented.js ================================================ var Promise = require("../../js/instrumented/bluebird.js"); window.Promise = Promise; window.adapter = Promise; Promise.config({cancellation:true}); ================================================ FILE: test/browser/worker.js ================================================ self.importScripts("./worker_bundle.js"); var currentPromise; function handler(ev) { ev.preventDefault(); self.postMessage(ev.type); if (ev.type === "unhandledrejection") { currentPromise.catch(function () {}); } } self.addEventListener("unhandledrejection", handler); self.addEventListener("rejectionhandled", handler); self.onmessage = function onmessage(ev) { if (ev.data === "reject") { currentPromise = Promise.reject(new Error("rejected")); } }; ================================================ FILE: test/mocha/2.1.2.js ================================================ "use strict"; var assert = require("assert"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var adapter = global.adapter; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it describe("2.1.2.1: When fulfilled, a promise: must not transition to any other state.", function () { testFulfilled(dummy, function (promise, done) { var onFulfilledCalled = false; promise.then(function onFulfilled() { onFulfilledCalled = true; }, function onRejected() { assert.strictEqual(onFulfilledCalled, false); done(); }); setTimeout(function(){done();}, 100); }); specify("trying to fulfill then immediately reject", function (done) { var tuple = pending(); var onFulfilledCalled = false; tuple.promise.then(function onFulfilled() { onFulfilledCalled = true; }, function onRejected() { assert.strictEqual(onFulfilledCalled, false); done(); }); tuple.fulfill(dummy); tuple.reject(dummy); setTimeout(function(){done();}, 100); }); specify("trying to fulfill then reject, delayed", function (done) { var tuple = pending(); var onFulfilledCalled = false; tuple.promise.then(function onFulfilled() { onFulfilledCalled = true; }, function onRejected() { assert.strictEqual(onFulfilledCalled, false); done(); }); setTimeout(function () { tuple.fulfill(dummy); tuple.reject(dummy); }, 50); setTimeout(function(){done();}, 100); }); specify("trying to fulfill immediately then reject delayed", function (done) { var tuple = pending(); var onFulfilledCalled = false; tuple.promise.then(function onFulfilled() { onFulfilledCalled = true; }, function onRejected() { assert.strictEqual(onFulfilledCalled, false); done(); }); tuple.fulfill(dummy); setTimeout(function () { tuple.reject(dummy); }, 50); setTimeout(function(){done();}, 100); }); }); ================================================ FILE: test/mocha/2.1.3.js ================================================ "use strict"; var assert = require("assert"); var testRejected = require("./helpers/testThreeCases").testRejected; var adapter = global.adapter; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it describe("2.1.3.1: When rejected, a promise: must not transition to any other state.", function () { testRejected(dummy, function (promise, done) { var onRejectedCalled = false; promise.then(function onFulfilled() { assert.strictEqual(onRejectedCalled, false); done(); }, function onRejected() { onRejectedCalled = true; }); setTimeout(function(){done();}, 100); }); specify("trying to reject then immediately fulfill", function (done) { var tuple = pending(); var onRejectedCalled = false; tuple.promise.then(function onFulfilled() { assert.strictEqual(onRejectedCalled, false); done(); }, function onRejected() { onRejectedCalled = true; }); tuple.reject(dummy); tuple.fulfill(dummy); setTimeout(function(){done();}, 100); }); specify("trying to reject then fulfill, delayed", function (done) { var tuple = pending(); var onRejectedCalled = false; tuple.promise.then(function onFulfilled() { assert.strictEqual(onRejectedCalled, false); done(); }, function onRejected() { onRejectedCalled = true; }); setTimeout(function () { tuple.reject(dummy); tuple.fulfill(dummy); }, 50); setTimeout(function(){done();}, 100); }); specify("trying to reject immediately then fulfill delayed", function (done) { var tuple = pending(); var onRejectedCalled = false; tuple.promise.then(function onFulfilled() { assert.strictEqual(onRejectedCalled, false); done(); }, function onRejected() { onRejectedCalled = true; }); tuple.reject(dummy); setTimeout(function () { tuple.fulfill(dummy); }, 50); setTimeout(function(){done();}, 100); }); }); ================================================ FILE: test/mocha/2.2.1.js ================================================ "use strict"; var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it describe("2.2.1: Both `onFulfilled` and `onRejected` are optional arguments.", function () { describe("2.2.1.1: If `onFulfilled` is not a function, it must be ignored.", function () { function testNonFunction(nonFunction, stringRepresentation) { specify("`onFulfilled` is " + stringRepresentation, function (done) { rejected(dummy).then(nonFunction, function () { done(); }); }); } testNonFunction(undefined, "`undefined`"); testNonFunction(null, "`null`"); testNonFunction(false, "`false`"); testNonFunction(5, "`5`"); testNonFunction({}, "an object"); }); describe("2.2.1.2: If `onRejected` is not a function, it must be ignored.", function () { function testNonFunction(nonFunction, stringRepresentation) { specify("`onRejected` is " + stringRepresentation, function (done) { fulfilled(dummy).then(function () { done(); }, nonFunction); }); } testNonFunction(undefined, "`undefined`"); testNonFunction(null, "`null`"); testNonFunction(false, "`false`"); testNonFunction(5, "`5`"); testNonFunction({}, "an object"); }); }); ================================================ FILE: test/mocha/2.2.2.js ================================================ "use strict"; var assert = require("assert"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var adapter = global.adapter; var fulfilled = adapter.fulfilled; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality describe("2.2.2: If `onFulfilled` is a function,", function () { describe("2.2.2.1: it must be called after `promise` is fulfilled, with `promise`’s fulfillment value as its " + "first argument.", function () { testFulfilled(sentinel, function (promise, done) { promise.then(function onFulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("2.2.2.2: it must not be called before `promise` is fulfilled", function () { specify("fulfilled after a delay", function (done) { var tuple = pending(); var isFulfilled = false; tuple.promise.then(function onFulfilled() { assert.strictEqual(isFulfilled, true); done(); }); setTimeout(function () { tuple.fulfill(dummy); isFulfilled = true; }, 50); }); specify("never fulfilled", function (done) { var tuple = pending(); var onFulfilledCalled = false; tuple.promise.then(function onFulfilled() { onFulfilledCalled = true; done(); }); setTimeout(function () { assert.strictEqual(onFulfilledCalled, false); done(); }, 150); }); }); describe("2.2.2.3: it must not be called more than once.", function () { specify("already-fulfilled", function (done) { var timesCalled = 0; fulfilled(dummy).then(function onFulfilled() { assert.strictEqual(++timesCalled, 1); done(); }); }); specify("trying to fulfill a pending promise more than once, immediately", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled, 1); done(); }); tuple.fulfill(dummy); tuple.fulfill(dummy); }); specify("trying to fulfill a pending promise more than once, delayed", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled, 1); done(); }); setTimeout(function () { tuple.fulfill(dummy); tuple.fulfill(dummy); }, 50); }); specify("trying to fulfill a pending promise more than once, immediately then delayed", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled, 1); done(); }); tuple.fulfill(dummy); setTimeout(function () { tuple.fulfill(dummy); }, 50); }); specify("when multiple `then` calls are made, spaced apart in time", function (done) { var tuple = pending(); var timesCalled = [0, 0, 0]; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[0], 1); }); setTimeout(function () { tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[1], 1); }); }, 50); setTimeout(function () { tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[2], 1); done(); }); }, 100); setTimeout(function () { tuple.fulfill(dummy); }, 150); }); specify("when `then` is interleaved with fulfillment", function (done) { var tuple = pending(); var timesCalled = [0, 0]; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[0], 1); }); tuple.fulfill(dummy); tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[1], 1); done(); }); }); }); }); ================================================ FILE: test/mocha/2.2.3.js ================================================ "use strict"; var assert = require("assert"); var testRejected = require("./helpers/testThreeCases").testRejected; var adapter = global.adapter; var rejected = adapter.rejected; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality describe("2.2.3: If `onRejected` is a function,", function () { describe("2.2.3.1: it must be called after `promise` is rejected, with `promise`’s rejection reason as its " + "first argument.", function () { testRejected(sentinel, function (promise, done) { promise.then(null, function onRejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("2.2.3.2: it must not be called before `promise` is rejected", function () { specify("rejected after a delay", function (done) { var tuple = pending(); var isRejected = false; tuple.promise.then(null, function onRejected() { assert.strictEqual(isRejected, true); done(); }); setTimeout(function () { tuple.reject(dummy); isRejected = true; }, 50); }); specify("never rejected", function (done) { var tuple = pending(); var onRejectedCalled = false; tuple.promise.then(null, function onRejected() { onRejectedCalled = true; done(); }); setTimeout(function () { assert.strictEqual(onRejectedCalled, false); done(); }, 150); }); }); describe("2.2.3.3: it must not be called more than once.", function () { specify("already-rejected", function (done) { var timesCalled = 0; rejected(dummy).then(null, function onRejected() { assert.strictEqual(++timesCalled, 1); done(); }); }); specify("trying to reject a pending promise more than once, immediately", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled, 1); done(); }); tuple.reject(dummy); tuple.reject(dummy); }); specify("trying to reject a pending promise more than once, delayed", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled, 1); done(); }); setTimeout(function () { tuple.reject(dummy); tuple.reject(dummy); }, 50); }); specify("trying to reject a pending promise more than once, immediately then delayed", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled, 1); done(); }); tuple.reject(dummy); setTimeout(function () { tuple.reject(dummy); }, 50); }); specify("when multiple `then` calls are made, spaced apart in time", function (done) { var tuple = pending(); var timesCalled = [0, 0, 0]; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[0], 1); }); setTimeout(function () { tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[1], 1); }); }, 50); setTimeout(function () { tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[2], 1); done(); }); }, 100); setTimeout(function () { tuple.reject(dummy); }, 150); }); specify("when `then` is interleaved with rejection", function (done) { var tuple = pending(); var timesCalled = [0, 0]; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[0], 1); }); tuple.reject(dummy); tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[1], 1); done(); }); }); }); }); ================================================ FILE: test/mocha/2.2.4.js ================================================ "use strict"; var assert = require("assert"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it describe("2.2.4: `onFulfilled` or `onRejected` must not be called until the execution context stack contains only " + "platform code.", function () { describe("`then` returns before the promise becomes fulfilled or rejected", function () { testFulfilled(dummy, function (promise, done) { var thenHasReturned = false; promise.then(function onFulfilled() { assert.strictEqual(thenHasReturned, true); done(); }); thenHasReturned = true; }); testRejected(dummy, function (promise, done) { var thenHasReturned = false; promise.then(null, function onRejected() { assert.strictEqual(thenHasReturned, true); done(); }); thenHasReturned = true; }); }); describe("Clean-stack execution ordering tests (fulfillment case)", function () { specify("when `onFulfilled` is added immediately before the promise is fulfilled", function () { var tuple = pending(); var onFulfilledCalled = false; tuple.promise.then(function onFulfilled() { onFulfilledCalled = true; }); tuple.fulfill(dummy); assert.strictEqual(onFulfilledCalled, false); }); specify("when `onFulfilled` is added immediately after the promise is fulfilled", function () { var tuple = pending(); var onFulfilledCalled = false; tuple.fulfill(dummy); tuple.promise.then(function onFulfilled() { onFulfilledCalled = true; }); assert.strictEqual(onFulfilledCalled, false); }); specify("when one `onFulfilled` is added inside another `onFulfilled`", function (done) { var promise = fulfilled(); var firstOnFulfilledFinished = false; promise.then(function () { promise.then(function () { assert.strictEqual(firstOnFulfilledFinished, true); done(); }); firstOnFulfilledFinished = true; }); }); specify("when `onFulfilled` is added inside an `onRejected`", function (done) { var promise = rejected(); var promise2 = fulfilled(); var firstOnRejectedFinished = false; promise.then(null, function () { promise2.then(function () { assert.strictEqual(firstOnRejectedFinished, true); done(); }); firstOnRejectedFinished = true; }); }); specify("when the promise is fulfilled asynchronously", function (done) { var tuple = pending(); var firstStackFinished = false; setTimeout(function () { tuple.fulfill(dummy); firstStackFinished = true; }, 0); tuple.promise.then(function () { assert.strictEqual(firstStackFinished, true); done(); }); }); }); describe("Clean-stack execution ordering tests (rejection case)", function () { specify("when `onRejected` is added immediately before the promise is rejected", function () { var tuple = pending(); var onRejectedCalled = false; tuple.promise.then(null, function onRejected() { onRejectedCalled = true; }); tuple.reject(dummy); assert.strictEqual(onRejectedCalled, false); }); specify("when `onRejected` is added immediately after the promise is rejected", function () { var tuple = pending(); var onRejectedCalled = false; tuple.reject(dummy); tuple.promise.then(null, function onRejected() { onRejectedCalled = true; }); assert.strictEqual(onRejectedCalled, false); }); specify("when `onRejected` is added inside an `onFulfilled`", function (done) { var promise = fulfilled(); var promise2 = rejected(); var firstOnFulfilledFinished = false; promise.then(function () { promise2.then(null, function () { assert.strictEqual(firstOnFulfilledFinished, true); done(); }); firstOnFulfilledFinished = true; }); }); specify("when one `onRejected` is added inside another `onRejected`", function (done) { var promise = rejected(); var firstOnRejectedFinished = false; promise.then(null, function () { promise.then(null, function () { assert.strictEqual(firstOnRejectedFinished, true); done(); }); firstOnRejectedFinished = true; }); }); specify("when the promise is rejected asynchronously", function (done) { var tuple = pending(); var firstStackFinished = false; setTimeout(function () { tuple.reject(dummy); firstStackFinished = true; }, 0); tuple.promise.then(null, function () { assert.strictEqual(firstStackFinished, true); done(); }); }); }); }); ================================================ FILE: test/mocha/2.2.5.js ================================================ /*jshint strict: false */ var assert = require("assert"); var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var undefinedThisStrict = (function() { "use strict"; return this; })(); var undefinedThisSloppy = (function() { return this; })(); describe("2.2.5 `onFulfilled` and `onRejected` must be called as functions (i.e. with no `this` value).", function () { describe("strict mode", function () { specify("fulfilled", function (done) { fulfilled(dummy).then(function onFulfilled() { "use strict"; assert(this === undefinedThisStrict || this === undefinedThisSloppy); done(); }); }); specify("rejected", function (done) { rejected(dummy).then(null, function onRejected() { "use strict"; assert(this === undefinedThisStrict || this === undefinedThisSloppy); done(); }); }); }); describe("sloppy mode", function () { specify("fulfilled", function (done) { fulfilled(dummy).then(function onFulfilled() { assert.strictEqual(this, undefinedThisSloppy); done(); }); }); specify("rejected", function (done) { rejected(dummy).then(null, function onRejected() { assert.strictEqual(this, undefinedThisSloppy); done(); }); }); }); }); ================================================ FILE: test/mocha/2.2.6.js ================================================ "use strict"; var assert = require("assert"); var sinon = require("sinon"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var other = { other: "other" }; // a value we don't want to be strict equal to var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality var sentinel2 = { sentinel2: "sentinel2" }; var sentinel3 = { sentinel3: "sentinel3" }; function callbackAggregator(times, ultimateCallback) { var soFar = 0; return function () { if (++soFar === times) { ultimateCallback(); } }; } describe("2.2.6: `then` may be called multiple times on the same promise.", function () { describe("2.2.6.1: If/when `promise` is fulfilled, all respective `onFulfilled` callbacks must execute in the " + "order of their originating calls to `then`.", function () { describe("multiple boring fulfillment handlers", function () { testFulfilled(sentinel, function (promise, done) { var handler1 = sinon.stub().returns(other); var handler2 = sinon.stub().returns(other); var handler3 = sinon.stub().returns(other); var spy = sinon.spy(); promise.then(handler1, spy); promise.then(handler2, spy); promise.then(handler3, spy); promise.then(function (value) { assert.strictEqual(value, sentinel); sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); sinon.assert.notCalled(spy); done(); }); }); }); describe("multiple fulfillment handlers, one of which throws", function () { testFulfilled(sentinel, function (promise, done) { var handler1 = sinon.stub().returns(other); var handler2 = sinon.stub().throws(other); var handler3 = sinon.stub().returns(other); var spy = sinon.spy(); promise.then(handler1, spy); promise.then(handler2, spy).caught(function(){}); promise.then(handler3, spy); promise.then(function (value) { assert.strictEqual(value, sentinel); sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); sinon.assert.notCalled(spy); done(); }); }); }); describe("results in multiple branching chains with their own fulfillment values", function () { testFulfilled(dummy, function (promise, done) { var semiDone = callbackAggregator(3, done); promise.then(function () { return sentinel; }).then(function (value) { assert.strictEqual(value, sentinel); semiDone(); }); promise.then(function () { throw sentinel2; }).then(null, function (reason) { assert.strictEqual(reason, sentinel2); semiDone(); }); promise.then(function () { return sentinel3; }).then(function (value) { assert.strictEqual(value, sentinel3); semiDone(); }); }); }); describe("`onFulfilled` handlers are called in the original order", function () { testFulfilled(dummy, function (promise, done) { var handler1 = sinon.spy(function handler1() {}); var handler2 = sinon.spy(function handler2() {}); var handler3 = sinon.spy(function handler3() {}); promise.then(handler1); promise.then(handler2); promise.then(handler3); promise.then(function () { sinon.assert.callOrder(handler1, handler2, handler3); done(); }); }); describe("even when one handler is added inside another handler", function () { testFulfilled(dummy, function (promise, done) { var handler1 = sinon.spy(function handler1() {}); var handler2 = sinon.spy(function handler2() {}); var handler3 = sinon.spy(function handler3() {}); promise.then(function () { handler1(); promise.then(handler3); }); promise.then(handler2); promise.then(function () { // Give implementations a bit of extra time to flush their internal queue, if necessary. setTimeout(function () { sinon.assert.callOrder(handler1, handler2, handler3); done(); }, 15); }); }); }); }); }); describe("2.2.6.2: If/when `promise` is rejected, all respective `onRejected` callbacks must execute in the " + "order of their originating calls to `then`.", function () { describe("multiple boring rejection handlers", function () { testRejected(sentinel, function (promise, done) { var handler1 = sinon.stub().returns(other); var handler2 = sinon.stub().returns(other); var handler3 = sinon.stub().returns(other); var spy = sinon.spy(); promise.then(spy, handler1); promise.then(spy, handler2); promise.then(spy, handler3); promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); sinon.assert.notCalled(spy); done(); }); }); }); describe("multiple rejection handlers, one of which throws", function () { testRejected(sentinel, function (promise, done) { var handler1 = sinon.stub().returns(other); var handler2 = sinon.stub().throws(other); var handler3 = sinon.stub().returns(other); var spy = sinon.spy(); promise.then(spy, handler1); promise.then(spy, handler2).caught(function(){}); promise.then(spy, handler3); promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); sinon.assert.notCalled(spy); done(); }); }); }); describe("results in multiple branching chains with their own fulfillment values", function () { testRejected(sentinel, function (promise, done) { var semiDone = callbackAggregator(3, done); promise.then(null, function () { return sentinel; }).then(function (value) { assert.strictEqual(value, sentinel); semiDone(); }); promise.then(null, function () { throw sentinel2; }).then(null, function (reason) { assert.strictEqual(reason, sentinel2); semiDone(); }); promise.then(null, function () { return sentinel3; }).then(function (value) { assert.strictEqual(value, sentinel3); semiDone(); }); }); }); describe("`onRejected` handlers are called in the original order", function () { testRejected(dummy, function (promise, done) { var handler1 = sinon.spy(function handler1() {}); var handler2 = sinon.spy(function handler2() {}); var handler3 = sinon.spy(function handler3() {}); promise.then(null, handler1); promise.then(null, handler2); promise.then(null, handler3); promise.then(null, function () { sinon.assert.callOrder(handler1, handler2, handler3); done(); }); }); describe("even when one handler is added inside another handler", function () { testRejected(dummy, function (promise, done) { var handler1 = sinon.spy(function handler1() {}); var handler2 = sinon.spy(function handler2() {}); var handler3 = sinon.spy(function handler3() {}); promise.then(null, function () { handler1(); promise.then(null, handler3); }); promise.then(null, handler2); promise.then(null, function () { // Give implementations a bit of extra time to flush their internal queue, if necessary. setTimeout(function () { sinon.assert.callOrder(handler1, handler2, handler3); done(); }, 15); }); }); }); }); }); }); ================================================ FILE: test/mocha/2.2.7.js ================================================ "use strict"; var assert = require("assert"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; var reasons = require("./helpers/reasons"); var adapter = global.adapter; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality var other = { other: "other" }; // a value we don't want to be strict equal to describe("2.2.7: `then` must return a promise: `promise2 = promise1.then(onFulfilled, onRejected)`", function () { specify("is a promise", function () { var promise1 = pending().promise; var promise2 = promise1.then(); assert(typeof promise2 === "object" || typeof promise2 === "function"); assert.notStrictEqual(promise2, null); assert.strictEqual(typeof promise2.then, "function"); }); describe("2.2.7.1: If either `onFulfilled` or `onRejected` returns a value `x`, run the Promise Resolution " + "Procedure `[[Resolve]](promise2, x)`", function () { specify("see separate 3.3 tests", function () { }); }); describe("2.2.7.2: If either `onFulfilled` or `onRejected` throws an exception `e`, `promise2` must be rejected " + "with `e` as the reason.", function () { function testReason(expectedReason, stringRepresentation) { describe("The reason is " + stringRepresentation, function () { testFulfilled(dummy, function (promise1, done) { var promise2 = promise1.then(function onFulfilled() { throw expectedReason; }); promise2.then(null, function onPromise2Rejected(actualReason) { assert.strictEqual(actualReason, expectedReason); done(); }); }); testRejected(dummy, function (promise1, done) { var promise2 = promise1.then(null, function onRejected() { throw expectedReason; }); promise2.then(null, function onPromise2Rejected(actualReason) { assert.strictEqual(actualReason, expectedReason); done(); }); }); }); } Object.keys(reasons).forEach(function (stringRepresentation) { testReason(reasons[stringRepresentation], stringRepresentation); }); }); describe("2.2.7.3: If `onFulfilled` is not a function and `promise1` is fulfilled, `promise2` must be fulfilled " + "with the same value.", function () { function testNonFunction(nonFunction, stringRepresentation) { describe("`onFulfilled` is " + stringRepresentation, function () { testFulfilled(sentinel, function (promise1, done) { var promise2 = promise1.then(nonFunction); promise2.then(function onPromise2Fulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); }); } testNonFunction(undefined, "`undefined`"); testNonFunction(null, "`null`"); testNonFunction(false, "`false`"); testNonFunction(5, "`5`"); testNonFunction({}, "an object"); testNonFunction([function () { return other; }], "an array containing a function"); }); describe("2.2.7.4: If `onRejected` is not a function and `promise1` is rejected, `promise2` must be rejected " + "with the same reason.", function () { function testNonFunction(nonFunction, stringRepresentation) { describe("`onRejected` is " + stringRepresentation, function () { testRejected(sentinel, function (promise1, done) { var promise2 = promise1.then(null, nonFunction); promise2.then(null, function onPromise2Rejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); } testNonFunction(undefined, "`undefined`"); testNonFunction(null, "`null`"); testNonFunction(false, "`false`"); testNonFunction(5, "`5`"); testNonFunction({}, "an object"); testNonFunction([function () { return other; }], "an array containing a function"); }); }); ================================================ FILE: test/mocha/2.3.1.js ================================================ "use strict"; var assert = require("assert"); var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it describe("2.3.1: If `promise` and `x` refer to the same object, reject `promise` with a `TypeError' as the reason.", function () { specify("via return from a fulfilled promise", function (done) { var promise = fulfilled(dummy).then(function () { return promise; }); promise.then(null, function (reason) { assert(reason instanceof adapter.TypeError); done(); }); }); specify("via return from a rejected promise", function (done) { var promise = rejected(dummy).then(null, function () { return promise; }); promise.then(null, function (reason) { assert(reason instanceof adapter.TypeError); done(); }); }); }); ================================================ FILE: test/mocha/2.3.2.js ================================================ "use strict"; var assert = require("assert"); var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality function testPromiseResolution(xFactory, test) { specify("via return from a fulfilled promise", function (done) { var promise = fulfilled(dummy).then(function onBasePromiseFulfilled() { return xFactory(); }); test(promise, done); }); specify("via return from a rejected promise", function (done) { var promise = rejected(dummy).then(null, function onBasePromiseRejected() { return xFactory(); }); test(promise, done); }); } describe("2.3.2: If `x` is a promise, adopt its state", function () { describe("2.3.2.1: If `x` is pending, `promise` must remain pending until `x` is fulfilled or rejected.", function () { function xFactory() { return pending().promise; } testPromiseResolution(xFactory, function (promise, done) { var wasFulfilled = false; var wasRejected = false; promise.then( function onPromiseFulfilled() { wasFulfilled = true; }, function onPromiseRejected() { wasRejected = true; } ); setTimeout(function () { assert.strictEqual(wasFulfilled, false); assert.strictEqual(wasRejected, false); done(); }, 100); }); }); describe("2.3.2.2: If/when `x` is fulfilled, fulfill `promise` with the same value.", function () { describe("`x` is already-fulfilled", function () { function xFactory() { return fulfilled(sentinel); } testPromiseResolution(xFactory, function (promise, done) { promise.then(function onPromiseFulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("`x` is eventually-fulfilled", function () { var tuple = null; function xFactory() { tuple = pending(); setTimeout(function () { tuple.fulfill(sentinel); }, 50); return tuple.promise; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function onPromiseFulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); }); }); describe("2.3.2.3: If/when `x` is rejected, reject `promise` with the same reason.", function () { describe("`x` is already-rejected", function () { function xFactory() { return rejected(sentinel); } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function onPromiseRejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("`x` is eventually-rejected", function () { var tuple = null; function xFactory() { tuple = pending(); setTimeout(function () { tuple.reject(sentinel); }, 50); return tuple.promise; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function onPromiseRejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); }); }); ================================================ FILE: test/mocha/2.3.3.js ================================================ "use strict"; var assert = require("assert"); var thenables = require("./helpers/thenables"); var reasons = require("./helpers/reasons"); var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality var other = { other: "other" }; // a value we don't want to be strict equal to var sentinelArray = [sentinel]; // a sentinel fulfillment value to test when we need an array function testPromiseResolution(xFactory, test) { specify("via return from a fulfilled promise", function (done) { var promise = fulfilled(dummy).then(function onBasePromiseFulfilled() { return xFactory(); }); test(promise, done); }); specify("via return from a rejected promise", function (done) { var promise = rejected(dummy).then(null, function onBasePromiseRejected() { return xFactory(); }); test(promise, done); }); } function testCallingResolvePromise(yFactory, stringRepresentation, test) { describe("`y` is " + stringRepresentation, function () { describe("`then` calls `resolvePromise` synchronously", function () { function xFactory() { return { then: function (resolvePromise) { resolvePromise(yFactory()); } }; } testPromiseResolution(xFactory, test); }); describe("`then` calls `resolvePromise` asynchronously", function () { function xFactory() { return { then: function (resolvePromise) { setTimeout(function () { resolvePromise(yFactory()); }, 0); } }; } testPromiseResolution(xFactory, test); }); }); } function testCallingRejectPromise(r, stringRepresentation, test) { describe("`r` is " + stringRepresentation, function () { describe("`then` calls `rejectPromise` synchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { rejectPromise(r); } }; } testPromiseResolution(xFactory, test); }); describe("`then` calls `rejectPromise` asynchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { setTimeout(function () { rejectPromise(r); }, 0); } }; } testPromiseResolution(xFactory, test); }); }); } function testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, fulfillmentValue) { testCallingResolvePromise(yFactory, stringRepresentation, function (promise, done) { promise.then(function onPromiseFulfilled(value) { assert.strictEqual(value, fulfillmentValue); done(); }); }); } function testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, rejectionReason) { testCallingResolvePromise(yFactory, stringRepresentation, function (promise, done) { promise.then(null, function onPromiseRejected(reason) { assert.strictEqual(reason, rejectionReason); done(); }); }); } function testCallingRejectPromiseRejectsWith(reason, stringRepresentation) { testCallingRejectPromise(reason, stringRepresentation, function (promise, done) { promise.then(null, function onPromiseRejected(rejectionReason) { assert.strictEqual(rejectionReason, reason); done(); }); }); } describe("2.3.3: Otherwise, if `x` is an object or function,", function () { describe("2.3.3.1: Let `then` be `x.then`", function () { describe("`x` is an object with null prototype", function () { var numberOfTimesThenWasRetrieved = null; beforeEach(function () { numberOfTimesThenWasRetrieved = 0; }); function xFactory() { return Object.create(null, { then: { get: function () { ++numberOfTimesThenWasRetrieved; return function thenMethodForX(onFulfilled) { onFulfilled(); }; } } }); } testPromiseResolution(xFactory, function (promise, done) { promise.then(function () { assert.strictEqual(numberOfTimesThenWasRetrieved, 1); done(); }); }); }); describe("`x` is an object with normal Object.prototype", function () { var numberOfTimesThenWasRetrieved = null; beforeEach(function () { numberOfTimesThenWasRetrieved = 0; }); function xFactory() { return Object.create(Object.prototype, { then: { get: function () { ++numberOfTimesThenWasRetrieved; return function thenMethodForX(onFulfilled) { onFulfilled(); }; } } }); } testPromiseResolution(xFactory, function (promise, done) { promise.then(function () { assert.strictEqual(numberOfTimesThenWasRetrieved, 1); done(); }); }); }); describe("`x` is a function", function () { var numberOfTimesThenWasRetrieved = null; beforeEach(function () { numberOfTimesThenWasRetrieved = 0; }); function xFactory() { function x() { } Object.defineProperty(x, "then", { get: function () { ++numberOfTimesThenWasRetrieved; return function thenMethodForX(onFulfilled) { onFulfilled(); }; } }); return x; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function () { assert.strictEqual(numberOfTimesThenWasRetrieved, 1); done(); }); }); }); }); describe("2.3.3.2: If retrieving the property `x.then` results in a thrown exception `e`, reject `promise` with " + "`e` as the reason.", function () { function testRejectionViaThrowingGetter(e, stringRepresentation) { function xFactory() { return Object.create(Object.prototype, { then: { get: function () { throw e; } } }); } describe("`e` is " + stringRepresentation, function () { testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, e); done(); }); }); }); } Object.keys(reasons).forEach(function (stringRepresentation) { testRejectionViaThrowingGetter(reasons[stringRepresentation], stringRepresentation); }); }); describe("2.3.3.3: If `then` is a function, call it with `x` as `this`, first argument `resolvePromise`, and " + "second argument `rejectPromise`", function () { describe("Calls with `x` as `this` and two function arguments", function () { function xFactory() { var x = { then: function (onFulfilled, onRejected) { assert.strictEqual(this, x); assert.strictEqual(typeof onFulfilled, "function"); assert.strictEqual(typeof onRejected, "function"); onFulfilled(); } }; return x; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function () { done(); }); }); }); describe("Uses the original value of `then`", function () { var numberOfTimesThenWasRetrieved = null; beforeEach(function () { numberOfTimesThenWasRetrieved = 0; }); function xFactory() { return Object.create(Object.prototype, { then: { get: function () { if (numberOfTimesThenWasRetrieved === 0) { return function (onFulfilled) { onFulfilled(); }; } return null; } } }); } testPromiseResolution(xFactory, function (promise, done) { promise.then(function () { done(); }); }); }); describe("2.3.3.3.1: If/when `resolvePromise` is called with value `y`, run `[[Resolve]](promise, y)`", function () { describe("`y` is not a thenable", function () { testCallingResolvePromiseFulfillsWith(function () { return undefined; }, "`undefined`", undefined); testCallingResolvePromiseFulfillsWith(function () { return null; }, "`null`", null); testCallingResolvePromiseFulfillsWith(function () { return false; }, "`false`", false); testCallingResolvePromiseFulfillsWith(function () { return 5; }, "`5`", 5); testCallingResolvePromiseFulfillsWith(function () { return sentinel; }, "an object", sentinel); testCallingResolvePromiseFulfillsWith(function () { return sentinelArray; }, "an array", sentinelArray); }); describe("`y` is a thenable", function () { Object.keys(thenables.fulfilled).forEach(function (stringRepresentation) { function yFactory() { return thenables.fulfilled[stringRepresentation](sentinel); } testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, sentinel); }); Object.keys(thenables.rejected).forEach(function (stringRepresentation) { function yFactory() { return thenables.rejected[stringRepresentation](sentinel); } testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, sentinel); }); }); describe("`y` is a thenable for a thenable", function () { Object.keys(thenables.fulfilled).forEach(function (outerStringRepresentation) { var outerThenableFactory = thenables.fulfilled[outerStringRepresentation]; Object.keys(thenables.fulfilled).forEach(function (innerStringRepresentation) { var innerThenableFactory = thenables.fulfilled[innerStringRepresentation]; var stringRepresentation = outerStringRepresentation + " for " + innerStringRepresentation; function yFactory() { return outerThenableFactory(innerThenableFactory(sentinel)); } testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, sentinel); }); Object.keys(thenables.rejected).forEach(function (innerStringRepresentation) { var innerThenableFactory = thenables.rejected[innerStringRepresentation]; var stringRepresentation = outerStringRepresentation + " for " + innerStringRepresentation; function yFactory() { return outerThenableFactory(innerThenableFactory(sentinel)); } testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, sentinel); }); }); }); }); describe("2.3.3.3.2: If/when `rejectPromise` is called with reason `r`, reject `promise` with `r`", function () { Object.keys(reasons).forEach(function (stringRepresentation) { testCallingRejectPromiseRejectsWith(reasons[stringRepresentation], stringRepresentation); }); }); describe("2.3.3.3.3: If both `resolvePromise` and `rejectPromise` are called, or multiple calls to the same " + "argument are made, the first call takes precedence, and any further calls are ignored.", function () { describe("calling `resolvePromise` then `rejectPromise`, both synchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { resolvePromise(sentinel); rejectPromise(other); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("calling `resolvePromise` synchronously then `rejectPromise` asynchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { resolvePromise(sentinel); setTimeout(function () { rejectPromise(other); }, 0); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("calling `resolvePromise` then `rejectPromise`, both asynchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { setTimeout(function () { resolvePromise(sentinel); }, 0); setTimeout(function () { rejectPromise(other); }, 0); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("calling `resolvePromise` with an asynchronously-fulfilled promise, then calling " + "`rejectPromise`, both synchronously", function () { function xFactory() { var tuple = pending(); setTimeout(function () { tuple.fulfill(sentinel); }, 50); return { then: function (resolvePromise, rejectPromise) { resolvePromise(tuple.promise); rejectPromise(other); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("calling `resolvePromise` with an asynchronously-rejected promise, then calling " + "`rejectPromise`, both synchronously", function () { function xFactory() { var tuple = pending(); setTimeout(function () { tuple.reject(sentinel); }, 50); return { then: function (resolvePromise, rejectPromise) { resolvePromise(tuple.promise); rejectPromise(other); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("calling `rejectPromise` then `resolvePromise`, both synchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { rejectPromise(sentinel); resolvePromise(other); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("calling `rejectPromise` synchronously then `resolvePromise` asynchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { rejectPromise(sentinel); setTimeout(function () { resolvePromise(other); }, 0); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("calling `rejectPromise` then `resolvePromise`, both asynchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { setTimeout(function () { rejectPromise(sentinel); }, 0); setTimeout(function () { resolvePromise(other); }, 0); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("calling `resolvePromise` twice synchronously", function () { function xFactory() { return { then: function (resolvePromise) { resolvePromise(sentinel); resolvePromise(other); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("calling `resolvePromise` twice, first synchronously then asynchronously", function () { function xFactory() { return { then: function (resolvePromise) { resolvePromise(sentinel); setTimeout(function () { resolvePromise(other); }, 0); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("calling `resolvePromise` twice, both times asynchronously", function () { function xFactory() { return { then: function (resolvePromise) { setTimeout(function () { resolvePromise(sentinel); }, 0); setTimeout(function () { resolvePromise(other); }, 0); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("calling `resolvePromise` with an asynchronously-fulfilled promise, then calling it again, both " + "times synchronously", function () { function xFactory() { var tuple = pending(); setTimeout(function () { tuple.fulfill(sentinel); }, 50); return { then: function (resolvePromise) { resolvePromise(tuple.promise); resolvePromise(other); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("calling `resolvePromise` with an asynchronously-rejected promise, then calling it again, both " + "times synchronously", function () { function xFactory() { var tuple = pending(); setTimeout(function () { tuple.reject(sentinel); }, 50); return { then: function (resolvePromise) { resolvePromise(tuple.promise); resolvePromise(other); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("calling `rejectPromise` twice synchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { rejectPromise(sentinel); rejectPromise(other); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("calling `rejectPromise` twice, first synchronously then asynchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { rejectPromise(sentinel); setTimeout(function () { resolvePromise(other); }, 0); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("calling `rejectPromise` twice, both times asynchronously", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { setTimeout(function () { rejectPromise(sentinel); }, 0); setTimeout(function () { resolvePromise(other); }, 0); } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("saving and abusing `resolvePromise` and `rejectPromise`", function () { var savedResolvePromise, savedRejectPromise; function xFactory() { return { then: function (resolvePromise, rejectPromise) { savedResolvePromise = resolvePromise; savedRejectPromise = rejectPromise; } }; } beforeEach(function () { savedResolvePromise = null; savedRejectPromise = null; }); testPromiseResolution(xFactory, function (promise, done) { var timesFulfilled = 0; var timesRejected = 0; promise.then( function () { ++timesFulfilled; }, function () { ++timesRejected; } ); if (savedResolvePromise && savedRejectPromise) { savedResolvePromise(dummy); savedResolvePromise(dummy); savedRejectPromise(dummy); savedRejectPromise(dummy); } setTimeout(function () { savedResolvePromise(dummy); savedResolvePromise(dummy); savedRejectPromise(dummy); savedRejectPromise(dummy); }, 4); setTimeout(function () { assert.strictEqual(timesFulfilled, 1); assert.strictEqual(timesRejected, 0); done(); }, 60); }); }); }); describe("2.3.3.3.4: If calling `then` throws an exception `e`,", function () { describe("2.3.3.3.4.1: If `resolvePromise` or `rejectPromise` have been called, ignore it.", function () { describe("`resolvePromise` was called with a non-thenable", function () { function xFactory() { return { then: function (resolvePromise) { resolvePromise(sentinel); throw other; } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("`resolvePromise` was called with an asynchronously-fulfilled promise", function () { function xFactory() { var tuple = pending(); setTimeout(function () { tuple.fulfill(sentinel); }, 50); return { then: function (resolvePromise) { resolvePromise(tuple.promise); throw other; } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("`resolvePromise` was called with an asynchronously-rejected promise", function () { function xFactory() { var tuple = pending(); setTimeout(function () { tuple.reject(sentinel); }, 50); return { then: function (resolvePromise) { resolvePromise(tuple.promise); throw other; } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("`rejectPromise` was called", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { rejectPromise(sentinel); throw other; } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("`resolvePromise` then `rejectPromise` were called", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { resolvePromise(sentinel); rejectPromise(other); throw other; } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("`rejectPromise` then `resolvePromise` were called", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { rejectPromise(sentinel); resolvePromise(other); throw other; } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); }); describe("2.3.3.3.4.2: Otherwise, reject `promise` with `e` as the reason.", function () { describe("straightforward case", function () { function xFactory() { return { then: function () { throw sentinel; } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("`resolvePromise` is called asynchronously before the `throw`", function () { function xFactory() { return { then: function (resolvePromise) { setTimeout(function () { resolvePromise(other); }, 0); throw sentinel; } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("`rejectPromise` is called asynchronously before the `throw`", function () { function xFactory() { return { then: function (resolvePromise, rejectPromise) { setTimeout(function () { rejectPromise(other); }, 0); throw sentinel; } }; } testPromiseResolution(xFactory, function (promise, done) { promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); }); }); }); describe("2.3.3.4: If `then` is not a function, fulfill promise with `x`", function () { function testFulfillViaNonFunction(then, stringRepresentation) { var x = null; beforeEach(function () { x = { then: then }; }); function xFactory() { return x; } describe("`then` is " + stringRepresentation, function () { testPromiseResolution(xFactory, function (promise, done) { promise.then(function (value) { assert.strictEqual(value, x); done(); }); }); }); } testFulfillViaNonFunction(5, "`5`"); testFulfillViaNonFunction({}, "an object"); testFulfillViaNonFunction([function () { }], "an array containing a function"); testFulfillViaNonFunction(/a-b/i, "a regular expression"); testFulfillViaNonFunction(Object.create(Function.prototype), "an object inheriting from `Function.prototype`"); }); }); ================================================ FILE: test/mocha/2.3.4.js ================================================ "use strict"; var assert = require("assert"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it describe("2.3.4: If `x` is not an object or function, fulfill `promise` with `x`", function () { function testValue(expectedValue, stringRepresentation, beforeEachHook, afterEachHook) { describe("The value is " + stringRepresentation, function () { if (typeof beforeEachHook === "function") { beforeEach(beforeEachHook); } if (typeof afterEachHook === "function") { afterEach(afterEachHook); } testFulfilled(dummy, function (promise1, done) { var promise2 = promise1.then(function onFulfilled() { return expectedValue; }); promise2.then(function onPromise2Fulfilled(actualValue) { assert.strictEqual(actualValue, expectedValue); done(); }); }); testRejected(dummy, function (promise1, done) { var promise2 = promise1.then(null, function onRejected() { return expectedValue; }); promise2.then(function onPromise2Fulfilled(actualValue) { assert.strictEqual(actualValue, expectedValue); done(); }); }); }); } testValue(undefined, "`undefined`"); testValue(null, "`null`"); testValue(false, "`false`"); testValue(true, "`true`"); testValue(0, "`0`"); testValue( true, "`true` with `Boolean.prototype` modified to have a `then` method", function () { Boolean.prototype.then = function () {}; }, function () { delete Boolean.prototype.then; } ); testValue( 1, "`1` with `Number.prototype` modified to have a `then` method", function () { Number.prototype.then = function () {}; }, function () { delete Number.prototype.then; } ); }); ================================================ FILE: test/mocha/3.2.1.js ================================================ "use strict"; var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it describe("3.2.1: Both `onFulfilled` and `onRejected` are optional arguments.", function () { describe("3.2.1.1: If `onFulfilled` is not a function, it must be ignored.", function () { function testNonFunction(nonFunction, stringRepresentation) { specify("`onFulfilled` is " + stringRepresentation, function (done) { rejected(dummy).then(nonFunction, function () { done(); }); }); } testNonFunction(undefined, "`undefined`"); testNonFunction(null, "`null`"); testNonFunction(false, "`false`"); testNonFunction(5, "`5`"); testNonFunction({}, "an object"); }); describe("3.2.1.2: If `onRejected` is not a function, it must be ignored.", function () { function testNonFunction(nonFunction, stringRepresentation) { specify("`onRejected` is " + stringRepresentation, function (done) { fulfilled(dummy).then(function () { done(); }, nonFunction); }); } testNonFunction(undefined, "`undefined`"); testNonFunction(null, "`null`"); testNonFunction(false, "`false`"); testNonFunction(5, "`5`"); testNonFunction({}, "an object"); }); }); ================================================ FILE: test/mocha/3.2.2.js ================================================ "use strict"; var assert = require("assert"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; var adapter = global.adapter; var fulfilled = adapter.fulfilled; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality describe("3.2.2: If `onFulfilled` is a function,", function () { describe("3.2.2.1: it must be called after `promise` is fulfilled, with `promise`’s fulfillment value as its " + "first argument.", function () { testFulfilled(sentinel, function (promise, done) { promise.then(function onFulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("3.2.2.2: it must not be called more than once.", function () { specify("already-fulfilled", function (done) { var timesCalled = 0; fulfilled(dummy).then(function onFulfilled() { assert.strictEqual(++timesCalled, 1); done(); }); }); specify("trying to fulfill a pending promise more than once, immediately", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled, 1); done(); }); tuple.fulfill(dummy); tuple.fulfill(dummy); }); specify("trying to fulfill a pending promise more than once, delayed", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled, 1); done(); }); setTimeout(function () { tuple.fulfill(dummy); tuple.fulfill(dummy); }, 50); }); specify("trying to fulfill a pending promise more than once, immediately then delayed", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled, 1); done(); }); tuple.fulfill(dummy); setTimeout(function () { tuple.fulfill(dummy); }, 50); }); specify("when multiple `then` calls are made, spaced apart in time", function (done) { var tuple = pending(); var timesCalled = [0, 0, 0]; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[0], 1); }); setTimeout(function () { tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[1], 1); }); }, 50); setTimeout(function () { tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[2], 1); done(); }); }, 100); setTimeout(function () { tuple.fulfill(dummy); }, 150); }); specify("when `then` is interleaved with fulfillment", function (done) { var tuple = pending(); var timesCalled = [0, 0]; tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[0], 1); }); tuple.fulfill(dummy); tuple.promise.then(function onFulfilled() { assert.strictEqual(++timesCalled[1], 1); done(); }); }); }); describe("3.2.2.3: it must not be called if `onRejected` has been called.", function () { testRejected(dummy, function (promise, done) { var onRejectedCalled = false; promise.then(function onFulfilled() { assert.strictEqual(onRejectedCalled, false); done(); }, function onRejected() { onRejectedCalled = true; }); setTimeout(function(){done();}, 100); }); specify("trying to reject then immediately fulfill", function (done) { var tuple = pending(); var onRejectedCalled = false; tuple.promise.then(function onFulfilled() { assert.strictEqual(onRejectedCalled, false); done(); }, function onRejected() { onRejectedCalled = true; }); tuple.reject(dummy); tuple.fulfill(dummy); setTimeout(function(){done();}, 100); }); specify("trying to reject then fulfill, delayed", function (done) { var tuple = pending(); var onRejectedCalled = false; tuple.promise.then(function onFulfilled() { assert.strictEqual(onRejectedCalled, false); done(); }, function onRejected() { onRejectedCalled = true; }); setTimeout(function () { tuple.reject(dummy); tuple.fulfill(dummy); }, 50); setTimeout(function(){done();}, 100); }); specify("trying to reject immediately then fulfill delayed", function (done) { var tuple = pending(); var onRejectedCalled = false; tuple.promise.then(function onFulfilled() { assert.strictEqual(onRejectedCalled, false); done(); }, function onRejected() { onRejectedCalled = true; }); tuple.reject(dummy); setTimeout(function () { tuple.fulfill(dummy); }, 50); setTimeout(function(){done();}, 100); }); }); }); ================================================ FILE: test/mocha/3.2.3.js ================================================ "use strict"; var assert = require("assert"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; var adapter = global.adapter; var rejected = adapter.rejected; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality describe("3.2.3: If `onRejected` is a function,", function () { describe("3.2.3.1: it must be called after `promise` is rejected, with `promise`’s rejection reason as its " + "first argument.", function () { testRejected(sentinel, function (promise, done) { promise.then(null, function onRejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("3.2.3.2: it must not be called more than once.", function () { specify("already-rejected", function (done) { var timesCalled = 0; rejected(dummy).then(null, function onRejected() { assert.strictEqual(++timesCalled, 1); done(); }); }); specify("trying to reject a pending promise more than once, immediately", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled, 1); done(); }); tuple.reject(dummy); tuple.reject(dummy); }); specify("trying to reject a pending promise more than once, delayed", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled, 1); done(); }); setTimeout(function () { tuple.reject(dummy); tuple.reject(dummy); }, 50); }); specify("trying to reject a pending promise more than once, immediately then delayed", function (done) { var tuple = pending(); var timesCalled = 0; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled, 1); done(); }); tuple.reject(dummy); setTimeout(function () { tuple.reject(dummy); }, 50); }); specify("when multiple `then` calls are made, spaced apart in time", function (done) { var tuple = pending(); var timesCalled = [0, 0, 0]; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[0], 1); }); setTimeout(function () { tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[1], 1); }); }, 50); setTimeout(function () { tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[2], 1); done(); }); }, 100); setTimeout(function () { tuple.reject(dummy); }, 150); }); specify("when `then` is interleaved with rejection", function (done) { var tuple = pending(); var timesCalled = [0, 0]; tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[0], 1); }); tuple.reject(dummy); tuple.promise.then(null, function onRejected() { assert.strictEqual(++timesCalled[1], 1); done(); }); }); }); describe("3.2.3.3: it must not be called if `onFulfilled` has been called.", function () { testFulfilled(dummy, function (promise, done) { var onFulfilledCalled = false; promise.then(function onFulfilled() { onFulfilledCalled = true; }, function onRejected() { assert.strictEqual(onFulfilledCalled, false); done(); }); setTimeout(function(){done();}, 100); }); specify("trying to fulfill then immediately reject", function (done) { var tuple = pending(); var onFulfilledCalled = false; tuple.promise.then(function onFulfilled() { onFulfilledCalled = true; }, function onRejected() { assert.strictEqual(onFulfilledCalled, false); done(); }); tuple.fulfill(dummy); tuple.reject(dummy); setTimeout(function(){done();}, 100); }); specify("trying to fulfill then reject, delayed", function (done) { var tuple = pending(); var onFulfilledCalled = false; tuple.promise.then(function onFulfilled() { onFulfilledCalled = true; }, function onRejected() { assert.strictEqual(onFulfilledCalled, false); done(); }); setTimeout(function () { tuple.fulfill(dummy); tuple.reject(dummy); }, 50); setTimeout(function(){done();}, 100); }); specify("trying to fulfill immediately then reject delayed", function (done) { var tuple = pending(); var onFulfilledCalled = false; tuple.promise.then(function onFulfilled() { onFulfilledCalled = true; }, function onRejected() { assert.strictEqual(onFulfilledCalled, false); done(); }); tuple.fulfill(dummy); setTimeout(function () { tuple.reject(dummy); }, 50); setTimeout(function(){done();}, 100); }); }); }); ================================================ FILE: test/mocha/3.2.4.js ================================================ "use strict"; var assert = require("assert"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it describe("3.2.4: `then` must return before `onFulfilled` or `onRejected` is called", function () { testFulfilled(dummy, function (promise, done) { var thenHasReturned = false; promise.then(function onFulfilled() { assert(thenHasReturned); done(); }); thenHasReturned = true; }); testRejected(dummy, function (promise, done) { var thenHasReturned = false; promise.then(null, function onRejected() { assert(thenHasReturned); done(); }); thenHasReturned = true; }); }); ================================================ FILE: test/mocha/3.2.5.js ================================================ "use strict"; var assert = require("assert"); var sinon = require("sinon"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var other = { other: "other" }; // a value we don't want to be strict equal to var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality var sentinel2 = { sentinel2: "sentinel2" }; var sentinel3 = { sentinel3: "sentinel3" }; function callbackAggregator(times, ultimateCallback) { var soFar = 0; return function () { if (++soFar === times) { ultimateCallback(); } }; } describe("3.2.5: `then` may be called multiple times on the same promise.", function () { describe("3.2.5.1: If/when `promise` is fulfilled, respective `onFulfilled` callbacks must execute in the order " + "of their originating calls to `then`.", function () { describe("multiple boring fulfillment handlers", function () { testFulfilled(sentinel, function (promise, done) { var handler1 = sinon.stub().returns(other); var handler2 = sinon.stub().returns(other); var handler3 = sinon.stub().returns(other); var spy = sinon.spy(); promise.then(handler1, spy); promise.then(handler2, spy); promise.then(handler3, spy); promise.then(function (value) { assert.strictEqual(value, sentinel); sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); sinon.assert.notCalled(spy); done(); }); }); }); describe("multiple fulfillment handlers, one of which throws", function () { testFulfilled(sentinel, function (promise, done) { var handler1 = sinon.stub().returns(other); var handler2 = sinon.stub().throws(other); var handler3 = sinon.stub().returns(other); var spy = sinon.spy(); promise.then(handler1, spy); promise.then(handler2, spy).caught(function(){}); promise.then(handler3, spy); promise.then(function (value) { assert.strictEqual(value, sentinel); sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); sinon.assert.notCalled(spy); done(); }); }); }); describe("results in multiple branching chains with their own fulfillment values", function () { testFulfilled(dummy, function (promise, done) { var semiDone = callbackAggregator(3, done); promise.then(function () { return sentinel; }).then(function (value) { assert.strictEqual(value, sentinel); semiDone(); }); promise.then(function () { throw sentinel2; }).then(null, function (reason) { assert.strictEqual(reason, sentinel2); semiDone(); }); promise.then(function () { return sentinel3; }).then(function (value) { assert.strictEqual(value, sentinel3); semiDone(); }); }); }); describe("`onFulfilled` handlers are called in the original order", function () { testFulfilled(dummy, function (promise, done) { var handler1 = sinon.spy(function handler1() {}); var handler2 = sinon.spy(function handler2() {}); var handler3 = sinon.spy(function handler3() {}); promise.then(handler1); promise.then(handler2); promise.then(handler3); promise.then(function () { sinon.assert.callOrder(handler1, handler2, handler3); done(); }); }); describe("even when one handler is added inside another handler", function () { testFulfilled(dummy, function (promise, done) { var handler1 = sinon.spy(function handler1() {}); var handler2 = sinon.spy(function handler2() {}); var handler3 = sinon.spy(function handler3() {}); promise.then(function () { handler1(); promise.then(handler3); }); promise.then(handler2); promise.then(function () { // Give implementations a bit of extra time to flush their internal queue, if necessary. setTimeout(function () { sinon.assert.callOrder(handler1, handler2, handler3); done(); }, 15); }); }); }); }); }); describe("3.2.5.2: If/when `promise` is rejected, respective `onRejected` callbacks must execute in the order " + "of their originating calls to `then`.", function () { describe("multiple boring rejection handlers", function () { testRejected(sentinel, function (promise, done) { var handler1 = sinon.stub().returns(other); var handler2 = sinon.stub().returns(other); var handler3 = sinon.stub().returns(other); var spy = sinon.spy(); promise.then(spy, handler1); promise.then(spy, handler2); promise.then(spy, handler3); promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); sinon.assert.notCalled(spy); done(); }); }); }); describe("multiple rejection handlers, one of which throws", function () { testRejected(sentinel, function (promise, done) { var handler1 = sinon.stub().returns(other); var handler2 = sinon.stub().throws(other); var handler3 = sinon.stub().returns(other); var spy = sinon.spy(); promise.then(spy, handler1); promise.then(spy, handler2).caught(function(){}); promise.then(spy, handler3); promise.then(null, function (reason) { assert.strictEqual(reason, sentinel); sinon.assert.calledWith(handler1, sinon.match.same(sentinel)); sinon.assert.calledWith(handler2, sinon.match.same(sentinel)); sinon.assert.calledWith(handler3, sinon.match.same(sentinel)); sinon.assert.notCalled(spy); done(); }); }); }); describe("results in multiple branching chains with their own fulfillment values", function () { testRejected(sentinel, function (promise, done) { var semiDone = callbackAggregator(3, done); promise.then(null, function () { return sentinel; }).then(function (value) { assert.strictEqual(value, sentinel); semiDone(); }); promise.then(null, function () { throw sentinel2; }).then(null, function (reason) { assert.strictEqual(reason, sentinel2); semiDone(); }); promise.then(null, function () { return sentinel3; }).then(function (value) { assert.strictEqual(value, sentinel3); semiDone(); }); }); }); describe("`onRejected` handlers are called in the original order", function () { testRejected(dummy, function (promise, done) { var handler1 = sinon.spy(function handler1() {}); var handler2 = sinon.spy(function handler2() {}); var handler3 = sinon.spy(function handler3() {}); promise.then(null, handler1); promise.then(null, handler2); promise.then(null, handler3); promise.then(null, function () { sinon.assert.callOrder(handler1, handler2, handler3); done(); }); }); describe("even when one handler is added inside another handler", function () { testRejected(dummy, function (promise, done) { var handler1 = sinon.spy(function handler1() {}); var handler2 = sinon.spy(function handler2() {}); var handler3 = sinon.spy(function handler3() {}); promise.then(null, function () { handler1(); promise.then(null, handler3); }); promise.then(null, handler2); promise.then(null, function () { // Give implementations a bit of extra time to flush their internal queue, if necessary. setTimeout(function () { sinon.assert.callOrder(handler1, handler2, handler3); done(); }, 15); }); }); }); }); }); }); ================================================ FILE: test/mocha/3.2.6.js ================================================ "use strict"; var assert = require("assert"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var pending = adapter.pending; var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality var other = { other: "other" }; // a value we don't want to be strict equal to describe("3.2.6: `then` must return a promise: `promise2 = promise1.then(onFulfilled, onRejected)`", function () { specify("is a promise", function () { var promise1 = pending().promise; var promise2 = promise1.then(); assert(typeof promise2 === "object" || typeof promise2 === "function"); assert.notStrictEqual(promise2, null); assert.strictEqual(typeof promise2.then, "function"); }); describe("3.2.6.1: If either `onFulfilled` or `onRejected` returns a value that is not a promise, `promise2` " + "must be fulfilled with that value.", function () { function testValue(expectedValue, stringRepresentation) { describe("The value is " + stringRepresentation, function () { testFulfilled(dummy, function (promise1, done) { var promise2 = promise1.then(function onFulfilled() { return expectedValue; }); promise2.then(function onPromise2Fulfilled(actualValue) { assert.strictEqual(actualValue, expectedValue); done(); }); }); testRejected(dummy, function (promise1, done) { var promise2 = promise1.then(null, function onRejected() { return expectedValue; }); promise2.then(function onPromise2Fulfilled(actualValue) { assert.strictEqual(actualValue, expectedValue); done(); }); }); }); } testValue(undefined, "`undefined`"); testValue(null, "`null`"); testValue(false, "`false`"); testValue(0, "`0`"); testValue(new Error(), "an error"); testValue(new Date(), "a date"); testValue({}, "an object"); testValue({ then: 5 }, "an object with a non-function `then` property"); }); describe("3.2.6.2: If either `onFulfilled` or `onRejected` throws an exception, `promise2` " + "must be rejected with the thrown exception as the reason.", function () { function testReason(expectedReason, stringRepresentation) { describe("The reason is " + stringRepresentation, function () { testFulfilled(dummy, function (promise1, done) { var promise2 = promise1.then(function onFulfilled() { throw expectedReason; }); promise2.then(null, function onPromise2Rejected(actualReason) { assert.strictEqual(actualReason, expectedReason); done(); }); }); testRejected(dummy, function (promise1, done) { var promise2 = promise1.then(null, function onRejected() { throw expectedReason; }); promise2.then(null, function onPromise2Rejected(actualReason) { assert.strictEqual(actualReason, expectedReason); done(); }); }); }); } testReason(undefined, "`undefined`"); testReason(null, "`null`"); testReason(false, "`false`"); testReason(0, "`0`"); testReason(new Error(), "an error"); testReason(new Date(), "a date"); testReason({}, "an object"); testReason({ then: function () { } }, "a promise-alike"); testReason(fulfilled(dummy), "a fulfilled promise"); var promise = rejected(dummy); promise.caught(function(){}); testReason(promise, "a rejected promise"); }); describe("3.2.6.3: If either `onFulfilled` or `onRejected` returns a promise (call it `returnedPromise`), " + "`promise2` must assume the state of `returnedPromise`", function () { describe("3.2.6.3.1: If `returnedPromise` is pending, `promise2` must remain pending until `returnedPromise` " + "is fulfilled or rejected.", function () { testFulfilled(dummy, function (promise1, done) { var wasFulfilled = false; var wasRejected = false; var promise2 = promise1.then(function onFulfilled() { var returnedPromise = pending().promise; return returnedPromise; }); promise2.then( function onPromise2Fulfilled() { wasFulfilled = true; }, function onPromise2Rejected() { wasRejected = true; } ); setTimeout(function () { assert.strictEqual(wasFulfilled, false); assert.strictEqual(wasRejected, false); done(); }, 100); }); testRejected(dummy, function (promise1, done) { var wasFulfilled = false; var wasRejected = false; var promise2 = promise1.then(null, function onRejected() { var returnedPromise = pending().promise; return returnedPromise; }); promise2.then( function onPromise2Fulfilled() { wasFulfilled = true; }, function onPromise2Rejected() { wasRejected = true; } ); setTimeout(function () { assert.strictEqual(wasFulfilled, false); assert.strictEqual(wasRejected, false); done(); }, 100); }); }); describe("3.2.6.3.2: If/when `returnedPromise` is fulfilled, `promise2` must be fulfilled with the same value.", function () { describe("`promise1` is fulfilled, and `returnedPromise` is:", function () { testFulfilled(sentinel, function (returnedPromise, done) { var promise1 = fulfilled(dummy); var promise2 = promise1.then(function onFulfilled() { return returnedPromise; }); promise2.then(function onPromise2Fulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); specify("a pseudo-promise", function (done) { var promise1 = fulfilled(dummy); var promise2 = promise1.then(function onFulfilled() { return { then: function (f) { f(sentinel); } }; }); promise2.then(function onPromise2Fulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); }); describe("`promise1` is rejected, and `returnedPromise` is:", function () { testFulfilled(sentinel, function (returnedPromise, done) { var promise1 = rejected(dummy); var promise2 = promise1.then(null, function onRejected() { return returnedPromise; }); promise2.then(function onPromise2Fulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); specify("a pseudo-promise", function (done) { var promise1 = rejected(dummy); var promise2 = promise1.then(null, function onRejected() { return { then: function (f) { f(sentinel); } }; }); promise2.then(function onPromise2Fulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); }); }); describe("3.2.6.3.3: If/when `returnedPromise` is rejected, `promise2` must be rejected with the same reason.", function () { describe("`promise1` is fulfilled, and `returnedPromise` is:", function () { testRejected(sentinel, function (returnedPromise, done) { var promise1 = fulfilled(dummy); var promise2 = promise1.then(function onFulfilled() { return returnedPromise; }); promise2.then(null, function onPromise2Rejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); specify("a pseudo-promise", function (done) { var promise1 = fulfilled(dummy); var promise2 = promise1.then(function onFulfilled() { return { then: function (f, r) { r(sentinel); } }; }); promise2.then(null, function onPromise2Rejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); describe("`promise1` is rejected, and `returnedPromise` is:", function () { testRejected(sentinel, function (returnedPromise, done) { var promise1 = rejected(dummy); var promise2 = promise1.then(null, function onRejected() { return returnedPromise; }); promise2.then(null, function onPromise2Rejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); specify("a pseudo-promise", function (done) { var promise1 = rejected(dummy); var promise2 = promise1.then(null, function onRejected() { return { then: function (f, r) { r(sentinel); } }; }); promise2.then(null, function onPromise2Rejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); }); }); describe("3.2.6.4: If `onFulfilled` is not a function and `promise1` is fulfilled, `promise2` must be fulfilled " + "with the same value.", function () { function testNonFunction(nonFunction, stringRepresentation) { describe("`onFulfilled` is " + stringRepresentation, function () { testFulfilled(sentinel, function (promise1, done) { var promise2 = promise1.then(nonFunction); promise2.then(function onPromise2Fulfilled(value) { assert.strictEqual(value, sentinel); done(); }); }); }); } testNonFunction(undefined, "`undefined`"); testNonFunction(null, "`null`"); testNonFunction(false, "`false`"); testNonFunction(5, "`5`"); testNonFunction({}, "an object"); testNonFunction([function () { return other; }], "an array containing a function"); }); describe("3.2.6.5: If `onRejected` is not a function and `promise1` is rejected, `promise2` must be rejected " + "with the same reason.", function () { function testNonFunction(nonFunction, stringRepresentation) { describe("`onRejected` is " + stringRepresentation, function () { testRejected(sentinel, function (promise1, done) { var promise2 = promise1.then(null, nonFunction); promise2.then(null, function onPromise2Rejected(reason) { assert.strictEqual(reason, sentinel); done(); }); }); }); } testNonFunction(undefined, "`undefined`"); testNonFunction(null, "`null`"); testNonFunction(false, "`false`"); testNonFunction(5, "`5`"); testNonFunction({}, "an object"); testNonFunction([function () { return other; }], "an array containing a function"); }); }); ================================================ FILE: test/mocha/any.js ================================================ "use strict"; /* Based on When.js tests Open Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2011 Brian Cavalier 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ var assert = require("assert"); var testUtils = require("./helpers/util.js"); var sentinel = {}; var other = {}; var RangeError = Promise.RangeError; describe("Promise.any-test", function () { specify("should reject on empty input array", function() { var a = []; return Promise.any(a) .caught(RangeError, testUtils.returnToken) .then(testUtils.assertToken); }); specify("should resolve with an input value", function() { var input = [1, 2, 3]; return Promise.any(input).then( function(result) { assert(testUtils.contains(input, result)); }, assert.fail ); }); specify("should resolve with a promised input value", function() { var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; return Promise.any(input).then( function(result) { assert(testUtils.contains([1, 2, 3], result)); }, assert.fail ); }); specify("should reject with all rejected input values if all inputs are rejected", function() { var input = [Promise.reject(1), Promise.reject(2), Promise.reject(3)]; var promise = Promise.any(input); return promise.then( assert.fail, function(result) { //Cannot use deep equality in IE8 because non-enumerable properties are not //supported assert(result[0] === 1); assert(result[1] === 2); assert(result[2] === 3); } ); }); specify("should accept a promise for an array", function() { var expected, input; expected = [1, 2, 3]; input = Promise.resolve(expected); return Promise.any(input).then( function(result) { assert.notDeepEqual(expected.indexOf(result), -1); }, assert.fail ); }); specify("should allow zero handlers", function() { var input = [1, 2, 3]; return Promise.any(input).then( function(result) { assert(testUtils.contains(input, result)); }, assert.fail ); }); specify("should resolve to empty array when input promise does not resolve to array", function() { return Promise.any(Promise.resolve(1)) .caught(TypeError, testUtils.returnToken) .then(testUtils.assertToken); }); specify("should reject when given immediately rejected promise", function() { var err = new Error(); return Promise.any(Promise.reject(err)).then(assert.fail, function(e) { assert.strictEqual(err, e); }); }); }); ================================================ FILE: test/mocha/api_exceptions.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); function assertErrorHasLongTraces(e) { assert(e.stack.indexOf("From previous event:") > -1); } function testCollection(name, a1, a2, a3) { function getPromise(obj, val) { return obj === void 0 ? Promise.resolve(val)[name](a1, a2, a3) : Promise[name](val, a1, a2, a3); } function thenable(obj) { var o = { then: function(f) { setTimeout(function(){ f(3); }, 1); } } specify("thenable for non-collection value", function() { return getPromise(obj, o) .then(assert.fail) .caught(Promise.TypeError, testUtils.returnToken) .then(testUtils.assertToken) }); }; function immediate(obj) { specify("immediate for non-collection value", function(){ return getPromise(obj, 3) .then(assert.fail) .caught(Promise.TypeError, testUtils.returnToken) .then(testUtils.assertToken) }); } function promise(obj) { var d = Promise.defer(); setTimeout(function(){ d.resolve(3); }, 1); specify("promise for non-collection value", function() { return getPromise(obj, d.promise) .then(assert.fail) .caught(Promise.TypeError, testUtils.returnToken) .then(testUtils.assertToken) }); } describe("When passing non-collection argument to Promise."+name + "() it should reject", function() { immediate(Promise); thenable(Promise); promise(Promise); }); describe("When calling ."+name + "() on a promise that resolves to a non-collection it should reject", function() { immediate(); thenable(); promise(); }); } if (Promise.hasLongStackTraces()) { describe("runtime API misuse should result in rejections", function(){ specify("returning promises circularly", function() { var d = Promise.defer(); var p = d.promise; var c = p.then(function(){ return c; }); d.fulfill(3); return c.then(assert.fail, function(e){ assert(e instanceof Promise.TypeError); }); }); specify("using illegal catchfilter", function() { var d = Promise.defer(); var p = d.promise; d.fulfill(3); return p.caught(null, function(){ }).then(assert.fail, function(e){ assert(e instanceof Promise.TypeError); }); }); specify("non-function to map", function() { return Promise.map([], []).then(assert.fail, function(e){ assert(e instanceof Promise.TypeError); }); }); specify("non-function to map inside then", function() { return Promise.resolve().then(function(){ return Promise.map([], []); }).then(assert.fail, function(e){ assert(e instanceof Promise.TypeError); assertErrorHasLongTraces(e); }); }); specify("non-function to reduce", function() { return Promise.reduce([], []).then(assert.fail, function(e){ assert(e instanceof Promise.TypeError); }); }); specify("non-function to reduce inside then", function() { return Promise.resolve().then(function(){ return Promise.reduce([], []); }).then(assert.fail, function(e){ assert(e instanceof Promise.TypeError); assertErrorHasLongTraces(e); }); }); specify("non-integer to some", function() { return Promise.some([], "asd").then(assert.fail, function(e){ assert(e instanceof Promise.TypeError); }); }); specify("non-integer to some inside then", function() { return Promise.resolve().then(function(){ return Promise.some([], "asd") }).then(assert.fail, function(e){ assert(e instanceof Promise.TypeError); assertErrorHasLongTraces(e); }); }); specify("non-array to all", function() { Promise.all(3, 3).then(assert.fail, function(e){ assert(e instanceof Promise.TypeError); }); }); specify("non-array to all inside then", function() { return Promise.resolve().then(function(){ return Promise.all(3, 3); }).then(assert.fail, function(e) { assert(e instanceof Promise.TypeError); assertErrorHasLongTraces(e); }); }); }); describe("static API misuse should just throw right away", function(){ specify("non-function to promise constructor", function() { try { new Promise(); assert.fail(); } catch (e) { assert(e instanceof Promise.TypeError); } }); specify("non-function to coroutine", function() { try { Promise.coroutine(); assert.fail(); } catch (e) { assert(e instanceof Promise.TypeError); } }); specify("non-object to promisifyAll", function() { try { Promise.promisifyAll(); assert.fail(); } catch (e) { assert(e instanceof Promise.TypeError); } }); specify("non-function to promisify", function() { try { Promise.promisify(); assert.fail(); } catch (e) { assert(e instanceof Promise.TypeError); } }); }); testCollection("race"); testCollection("all"); testCollection("settle"); testCollection("any"); testCollection("some", 1); testCollection("map", function(){}); testCollection("reduce", function(){}); testCollection("filter", function(){}); testCollection("props", function(){}); } ================================================ FILE: test/mocha/async.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Async requirement", function() { var arr = []; function a() { arr.push(1); } function b() { arr.push(2); } function c() { arr.push(3); } function assertArr() { assert.deepEqual(arr, [1,2,3]); arr.length = 0; } beforeEach(function() { arr = []; }); specify("Basic", function() { var p = new Promise(function(resolve) { resolve(); }); a(); p.then(c); b(); return p.then(assertArr); }); specify("Resolve-Before-Then", function() { var resolveP; var p = new Promise(function(resolve) { resolveP = resolve; }); a(); resolveP(); p.then(c); b(); return p.then(assertArr); }); specify("Resolve-After-Then", function() { var resolveP; var p = new Promise(function(resolve) { resolveP = resolve; }); a(); p.then(c); resolveP(); b(); return p.then(assertArr); }); specify("Then-Inside-Then", function() { var fulfilledP = Promise.resolve(); return fulfilledP.then(function() { a(); var ret = fulfilledP.then(c).then(assertArr); b(); return ret; }); }); if (typeof Error.captureStackTrace === "function") { describe("Should not grow the stack and cause eventually stack overflow.", function(){ var lim; beforeEach(function() { lim = Error.stackTraceLimit; Error.stackTraceLimit = 10000; }); afterEach(function() { Error.stackTraceLimit = lim; }); function assertStackIsNotGrowing(stack) { assert(stack.split("\n").length > 5); assert(stack.split("\n").length < 15); } specify("Already fulfilled.", function() { function test(i){ if (i <= 0){ return Promise.resolve(new Error().stack); } else { return Promise.resolve(i-1).then(test) } } return test(100).then(function(stack) { assertStackIsNotGrowing(stack); }); }); specify("Already rejected", function() { function test(i){ if (i <= 0){ return Promise.reject(new Error().stack); } else { return Promise.reject(i-1).then(assert.fail, test) } } return test(100).then(assert.fail, function(stack) { assertStackIsNotGrowing(stack); }); }); specify("Immediately fulfilled", function() { function test(i){ var deferred = Promise.defer(); if (i <= 0){ deferred.fulfill(new Error().stack); return deferred.promise; } else { deferred.fulfill(i-1); return deferred.promise.then(test) } } return test(100).then(function(stack) { assertStackIsNotGrowing(stack); }); }); specify("Immediately rejected", function() { function test(i){ var deferred = Promise.defer(); if (i <= 0){ deferred.reject(new Error().stack); return deferred.promise; } else { deferred.reject(i-1); return deferred.promise.then(assert.fail, test) } } return test(10).then(assert.fail, function(stack) { assertStackIsNotGrowing(stack); }); }); }); } if (testUtils.isRecentNode) { describe("Frees memory of old values in promise chains", function () { function getHeapUsed() { global.gc(); return process.memoryUsage().heapUsed; } var initialHeapUsed; before(function () { if (typeof global.gc !== "function") { throw new Error("These tests require the --expose-gc flag"); } initialHeapUsed = getHeapUsed(); }); specify(".then", function () { return Promise.resolve() .then(function () { assert.ok( getHeapUsed() < initialHeapUsed * 1.1, "Promise.resolve uses minimal memory" ); var rows = []; for (var i = 0; i < 1e6; i++) { rows.push(["Example " + i, i, i * 2]); } return rows; }) .then(function (rows) { assert.ok( getHeapUsed() > initialHeapUsed * 12, "large array uses a large amount of memory" ); return { len: rows.length }; }) .then(function (x) { // work around cancellation retaining previous result return x; }) .then(function (summaryResult) { assert.ok( getHeapUsed() < initialHeapUsed * 1.1, "memory used by large array is freed" ); assert.strictEqual(summaryResult.len, 1e6, "result"); }); }); specify(".catch", function () { return Promise.reject(new Error("error 1")) .catch(function () { assert.ok( getHeapUsed() < initialHeapUsed * 1.1, "Promise.reject uses minimal memory" ); var rows = []; for (var i = 0; i < 1e6; i++) { rows.push(["Example " + i, i, i * 2]); } var error = new Error("error 2"); error.result = rows; throw error; }) .catch(function (err) { assert.ok( getHeapUsed() > initialHeapUsed * 12, "large array uses a large amount of memory" ); var rows = err.result; var error = new Error("error 3"); error.result = { len: rows.length }; throw error; }) .catch(function (err) { // work around cancellation retaining previous result throw err; }) .catch(function (err) { assert.ok( getHeapUsed() < initialHeapUsed * 1.1, "memory used by large array is freed" ); var summaryResult = err.result; assert.strictEqual(summaryResult.len, 1e6, "result"); }); }); }); } }); ================================================ FILE: test/mocha/async_hooks.js ================================================ "use strict"; var assert = require("assert"); var getContextFn = Promise._getContext; Promise.config({ asyncHooks: true }); var supportsAsync = Promise._getContext !== getContextFn; Promise.config({ asyncHooks: false }); if (supportsAsync) { runTests(); } function runTests() { var async_hooks = require('async_hooks'); var tree = new Set(); var hook = async_hooks.createHook({ init: function(asyncId, type, triggerId) { if (tree.has(triggerId)) { tree.add(asyncId); } } }); var currentId = async_hooks.executionAsyncId; function getAsyncPromise() { return new Promise(function(resolve, reject) { setTimeout(function() { setTimeout(resolve, 1); }, 1); }); } describe("async_hooks", function() { beforeEach(function() { Promise.config({ asyncHooks: true }); }) afterEach(function() { tree.clear(); hook.disable(); Promise.config({ asyncHooks: false }); }); it('should preserve async context when using fromNode', function() { hook.enable() tree.add(currentId()); return new Promise(function(resolve) { var globalResolve; setImmediate(function() { hook.enable() tree.add(currentId()); resolve( new Promise(function(resolve) { globalResolve = resolve; }) .then(function() { assert.ok(tree.has(currentId())); }) ); }) setTimeout(function() { globalResolve(); }, 10); }) }); it('should preserve async context when using .map', function() { hook.enable() tree.add(currentId()); var d1 = getAsyncPromise(); return new Promise(function(resolve, reject) { resolve(Promise.map([d1, null, Promise.resolve(1), Promise.delay(1)], function() { return currentId(); }).then(function(asyncIds) { for (var i = 0; i < asyncIds.length; ++i) { assert.ok(tree.has(asyncIds[i])); } })); }); }); it('should preserve async context when using .filter', function() { hook.enable() tree.add(currentId()); var d1 = getAsyncPromise(); return new Promise(function(resolve, reject) { resolve(Promise.filter([d1, null, Promise.resolve(1), Promise.delay(1)], function() { assert.ok(tree.has(currentId())); })); }); }); it('should preserve async context when using .reduce', function() { hook.enable() tree.add(currentId()); var d1 = getAsyncPromise(); return new Promise(function(resolve, reject) { resolve(Promise.reduce([d1, null, Promise.resolve(1), Promise.delay(1)], function() { assert.ok(tree.has(currentId())); })); }); }); it('should preserve async context when using .join', function() { hook.enable() tree.add(currentId()); var d1 = getAsyncPromise(); return new Promise(function(resolve, reject) { resolve(Promise.join(d1, Promise.delay(1), function() { assert.ok(tree.has(currentId())); })); }); }); it('should preserve async context when using .each', function() { hook.enable() tree.add(currentId()); var d1 = getAsyncPromise(); return new Promise(function(resolve, reject) { resolve(Promise.each([d1, null, Promise.resolve(1), Promise.delay(1)], function() { assert.ok(tree.has(currentId())); })); }); }); it('should be able to disable AsyncResource usage', function() { Promise.config({ asyncHooks: false }); hook.enable() tree.add(currentId()); var d1 = getAsyncPromise(); return new Promise(function(resolve, reject) { resolve(d1.then(function() { assert.ok(!tree.has(currentId())); })); }); }); }); } ================================================ FILE: test/mocha/bind.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var defaultThis = function() {return this}(); function timedThenableOf(value) { return { then: function(onFulfilled) { setTimeout(function() { onFulfilled(value); }, 1); } }; } function timedPromiseOf(value) { return Promise.delay(1, value); } function immediatePromiseOf(value) { return Promise.resolve(value); } function immediateThenableOf(value) { return { then: function(onFulfilled) { onFulfilled(value); } }; } function timedRejectedThenableOf(value) { return { then: function(onFulfilled, onRejected) { setTimeout(function() { onRejected(value); }, 1); } }; } function timedRejectedPromiseOf(value) { return Promise.delay(1).then(function() { throw value; }); } function immediateRejectedPromiseOf(value) { return Promise.reject(value); } function immediateRejectedThenableOf(value) { return { then: function(onFulfilled, onRejected) { onRejected(value); } }; } function toValue(valueOrPromise) { if (valueOrPromise && typeof valueOrPromise.value === "function") { return valueOrPromise.value(); } return valueOrPromise } var THIS = {name: "this"}; function CustomError1() {} CustomError1.prototype = Object.create(Error.prototype); function CustomError2() {} CustomError2.prototype = Object.create(Error.prototype); describe("when using .bind", function() { describe("with finally", function() { describe("this should refer to the bound object", function() { specify("in straight-forward handler", function() { return Promise.resolve().bind(THIS).lastly(function(){ assert(this === THIS); }); }); specify("after promise returned from finally resolves", function() { var d = Promise.defer(); var promise = d.promise; var waited = false; setTimeout(function(){ waited = true; d.fulfill(); }, 1); return Promise.resolve().bind(THIS).lastly(function(){ return promise; }).lastly(function(){ assert(waited); assert(this === THIS); }); }); }) }); describe("with tap", function() { describe("this should refer to the bound object", function() { specify("in straight-forward handler", function() { return Promise.resolve().bind(THIS).tap(function(){ assert(this === THIS); }); }); specify("after promise returned from tap resolves", function() { var d = Promise.defer(); var promise = d.promise; var waited = false; setTimeout(function(){ waited = true; d.fulfill(); }, 1); return Promise.resolve().bind(THIS).tap(function(){ return promise; }).tap(function(){ assert(waited); assert(this === THIS); }); }); }) }); describe("with timeout", function() { describe("this should refer to the bound object", function() { specify("in straight-forward handler", function() { return Promise.resolve(3).bind(THIS).timeout(500).then(function(v) { assert(v === 3); assert(this === THIS); }); }); specify("in rejected handler", function() { return Promise.reject(3).bind(THIS).timeout(500).then(assert.fail, function(v){ assert(v === 3); assert(this === THIS); }); }); specify("in rejected handler after timeout", function() { return new Promise(function(){}) .bind(THIS).timeout(10).caught(Promise.TimeoutError, function(err){ assert(this === THIS); }); }); }) }); describe("With catch filters", function() { describe("this should refer to the bound object", function() { specify("in an immediately trapped catch handler", function() { return Promise.resolve().bind(THIS).then(function(){ assert(THIS === this); var a; a.b(); }).caught(Error, function(e){ assert(THIS === this); }); }); specify("in a later trapped catch handler", function() { return Promise.resolve().bind(THIS).then(function(){ throw new CustomError1(); }).caught(CustomError2, assert.fail) .caught(CustomError1, function(e){ assert(THIS === this); }); }); }); }); describe("With .get promises", function(){ specify("this should refer to the bound object", function() { return Promise.resolve({key: "value"}).bind(THIS).get("key").then(function(val){ assert(val === "value"); assert(this === THIS); }); }); }); describe("With .call promises", function(){ specify("this should refer to the bound object", function() { return Promise.resolve({key: function(){return "value";}}).bind(THIS).call("key").then(function(val){ assert(val === "value"); assert(this === THIS); }); }); }); describe("With .done promises", function(){ describe("this should refer to the bound object", function() { specify("when rejected", function() { return Promise.reject().bind(THIS).done(assert.fail, function(){ assert(this === THIS); }); }); specify("when fulfilled", function() { return Promise.resolve().bind(THIS).done(function(){ assert(this === THIS); }); }); }); }); describe("With .spread promises", function(){ describe("this should refer to the bound object", function() { specify("when spreading immediate array", function() { return Promise.resolve([1,2,3]).bind(THIS).spread(function(a, b, c){ assert(c === 3); assert(this === THIS); }); }); specify("when spreading eventual array", function() { var d = Promise.defer(); var promise = d.promise; setTimeout(function(){ d.fulfill([1,2,3]); }, 1); return promise.bind(THIS).spread(function(a, b, c){ assert(c === 3); assert(this === THIS); }); }); specify("when spreading eventual array of eventual values", function() { var d = Promise.defer(); var promise = d.promise; setTimeout(function(){ var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; d.fulfill([p1, p2, p3]); setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 3); }, 1); return promise.bind(THIS).all().spread(function(a, b, c){ assert(c === 3); assert(this === THIS); }); }); }); }); describe("With nodeify", function() { describe("this should refer to the bound object", function() { specify("when the callback succeeeds", function() { var spy = testUtils.getSpy(); Promise.resolve(3).bind(THIS).nodeify(spy(function(err, success){ assert(success === 3); assert(this === THIS); })); return spy.promise; }); specify("when the callback errs", function() { var spy = testUtils.getSpy(); Promise.reject(3).bind(THIS).nodeify(spy(function(err, success){ assert(err === 3); assert(this === THIS); })); return spy.promise; }); }); }); describe("With map", function() { describe("this should refer to the bound object", function() { specify("inside the mapper with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).map(function(v, i){ if (i === 2) { assert(this === THIS); } }); }); specify("inside the mapper with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).map(function(v, i){ if (i === 2) { assert(this === THIS); } }); }); specify("after the mapper with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).map(function(){ return 1; }).then(function(){ assert(this === THIS); }); }); specify("after the mapper with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).map(function(){ return 1; }).then(function(){ assert(this === THIS); }); }); specify("after the mapper with immediate values when the map returns promises", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).map(function(){ return p1; }).then(function(){ assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).map(function(){ return p1.then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); describe("With reduce", function() { describe("this should refer to the bound object", function() { specify("inside the reducer with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).reduce(function(prev, v, i){ if (i === 2) { assert(this === THIS); } }); }); specify("inside the reducer with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).reduce(function(prev, v, i){ if (i === 2) { assert(this === THIS); } }); }); specify("after the reducer with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).reduce(function(){ return 1; }).then(function(){ assert(this === THIS); }); }); specify("after the reducer with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).reduce(function(){ return 1; }).then(function(){ assert(this === THIS); }); }); specify("after the reducer with immediate values when the reducer returns promise", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).reduce(function(){ return p1; }).then(function(){ assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).reduce(function(){ return p1.then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); describe("With filter", function() { describe("this should refer to the bound object", function() { specify("inside the filterer with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).filter(function(v, i){ if (i === 2) { assert(this === THIS); } }); }); specify("inside the filterer with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).filter(function(v, i){ if (i === 2) { assert(this === THIS); } }); }); specify("after the filterer with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).filter(function(){ return 1; }).then(function(){ assert(this === THIS); }); }); specify("after the filterer with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).filter(function(){ return 1; }).then(function(){ assert(this === THIS); }); }); specify("after the filterer with immediate values when the filterer returns promises", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).filter(function(){ return p1; }).then(function(){ assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).filter(function(){ return p1.then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); describe("With all", function() { describe("this should refer to the bound object", function() { specify("after all with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).all().then(function(v){ assert(v.length === 3); assert(this === THIS); }); }); specify("after all with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).all().then(function(v){ assert(v.length === 3); assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).filter(function(){ return Promise.all([p1]).then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); describe("With any", function() { describe("this should refer to the bound object", function() { specify("after any with immediate values", function() { Promise.resolve([1,2,3]).bind(THIS).any().then(function(v){ assert(v === 1); assert(this === THIS); }); }); specify("after any with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).any().then(function(v){ assert(v === 1); assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).filter(function(){ return Promise.any([p1]).then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); describe("With race", function() { describe("this should refer to the bound object", function() { specify("after race with immediate values", function() { Promise.resolve([1,2,3]).bind(THIS).race().then(function(v){ assert(v === 1); assert(this === THIS); }); }); specify("after race with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).race().then(function(v){ assert(v === 1); assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).filter(function(){ return Promise.race([p1]).then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); describe("With delay", function() { describe("this should refer to the bound object", function() { specify("after race with immediate values", function() { Promise.resolve([1,2,3]).bind(THIS).delay(1).then(function(v){ assert(v[0] === 1); assert(this === THIS); }); }); specify("after race with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).delay(1).all().then(function(v){ assert(v[0] === 1); assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).delay(1).bind(THIS).delay(1).filter(function(){ assert(this === THIS); return Promise.delay(1).then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); describe("With settle", function() { describe("this should refer to the bound object", function() { specify("after settle with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).settle().then(function(v){ assert(v.length === 3); assert(this === THIS); }); }); specify("after settle with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).settle().then(function(v){ assert(v.length === 3); assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).filter(function(){ return Promise.settle([p1]).then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); describe("With some", function() { describe("this should refer to the bound object", function() { specify("after some with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).some(2).then(function(v){ assert.deepEqual(v, [1,2]); assert(this === THIS); }); }); specify("after some with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).some(2).then(function(v){ assert.deepEqual(v, [1,2]); assert(this === THIS); }); }); specify("after some with eventual array for eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; var dArray = Promise.defer(); var arrayPromise = dArray.promise; setTimeout(function(){ dArray.fulfill([p1, p2, p3]); setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); }, 1); return arrayPromise.bind(THIS).some(2).then(function(v){ assert.deepEqual(v, [1,2]); assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).filter(function(){ return Promise.some([p1], 1).then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); describe("With props", function() { describe("this should refer to the bound object", function() { specify("after props with immediate values", function() { return Promise.resolve([1,2,3]).bind(THIS).props().then(function(v){ assert(v[2] === 3); assert(this === THIS); }); }); specify("after props with eventual values", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.resolve([p1, p2, p3]).bind(THIS).props().then(function(v){ assert(v[2] === 3); assert(this === THIS); }); }); }); describe("this should not refer to the bound object", function() { specify("in the promises created within the handler", function() { var d1 = Promise.defer(); var p1 = d1.promise; setTimeout(function(){ d1.fulfill(1); }, 1); return Promise.resolve([1,2,3]).bind(THIS).props(function(){ return Promise.settle([p1]).then(function(){ assert(this !== THIS); return 1; }) }).then(function(){ assert(this === THIS); }); }); }); }); }); describe("When using .bind to gratuitously rebind", function() { var a = {value: 1}; var b = {value: 2}; var c = {value: 3}; function makeTest(a, b, c) { return function() { return Promise.bind(a).then(function(){ assert(this.value === 1); }).bind(b).then(function(){ assert(this.value === 2); }).bind(c).then(function(){ assert(this.value === 3); }); } } specify("should not get confused immediately", makeTest(a, b, c)); specify("should not get confused immediate thenable", makeTest(immediateThenableOf(a), immediateThenableOf(b), immediateThenableOf(c))); specify("should not get confused immediate promise", makeTest(immediatePromiseOf(a), immediatePromiseOf(b), immediatePromiseOf(c))); specify("should not get confused timed thenable", makeTest(timedThenableOf(a), timedThenableOf(b), timedThenableOf(c))); specify("should not get confused timed promise", makeTest(timedPromiseOf(a), timedPromiseOf(b), timedPromiseOf(c))); }); describe("Promised thisArg", function() { var e = {value: 1}; specify("basic case, this first", function(done) { var thisPromise = Promise.delay(1, 1); var promise = thisPromise.delay(1).thenReturn(2); promise.bind(thisPromise).then(function(val) { assert(+this === 1); assert(+val === 2); done(); }); }); specify("bound value is not changed by returned promise", function() { return Promise.resolve().then(function() { return new Promise(function(resolve) { resolve(); }).bind(THIS).then(function() {}); }).then(function() { assert.strictEqual(this, defaultThis); }); }); specify("basic case, main promise first", function() { var promise = Promise.delay(1, 2); var thisPromise = promise.thenReturn(1); return promise.bind(thisPromise).then(function(val) { assert.strictEqual(+this, 1); assert.strictEqual(+val, 2); }); }); specify("both reject, this rejects first", function(done) { var e1 = new Error(); var e2 = new Error(); var thisPromise = Promise.delay(1, 0).thenThrow(e1); var promise = Promise.delay(2, 56).thenThrow(e2); promise.bind(thisPromise).then(null, function(reason) { assert(this === defaultThis); assert(reason === e1); done(); }); }); specify("both reject, main promise rejects first", function(done) { var e1 = new Error("first"); var e2 = new Error("second"); var thisPromise = Promise.delay(56, 1).thenThrow(e1); var promise = Promise.delay(2, 0).thenThrow(e2); promise.bind(thisPromise).then(null, function(reason) { assert(this === defaultThis); assert(reason === e2); done(); }); }); specify("Immediate value waits for deferred this", function() { var t = Promise.delay(1, THIS); var t2 = {}; return Promise.resolve(t2).bind(t).then(function(value) { assert.strictEqual(this, THIS); assert.strictEqual(t2, value); }); }); specify("Immediate error waits for deferred this", function() { var t = Promise.delay(1, THIS); var err = new Error(); return Promise.reject(err).bind(t).then(assert.fail, function(e) { assert.strictEqual(this, THIS); assert.strictEqual(err, e); }); }); function makeThisArgRejectedTest(reason) { return function() { return Promise.bind(reason()).then(assert.fail, function(e) { assert(this === defaultThis); assert(e.value === 1); }) }; } specify("if thisArg is rejected timed promise, returned promise is rejected", makeThisArgRejectedTest(function() { return timedRejectedPromiseOf(e); })); specify("if thisArg is rejected immediate promise, returned promise is rejected", makeThisArgRejectedTest(function() { return immediateRejectedPromiseOf(e); })); specify("if thisArg is rejected timed thenable, returned promise is rejected", makeThisArgRejectedTest(function() { return timedRejectedThenableOf(e); })); specify("if thisArg is rejected immediate thenable, returned promise is rejected", makeThisArgRejectedTest(function() { return immediateRejectedThenableOf(e); })); function makeThisArgRejectedTestMethod(reason) { return function() { return Promise.resolve().bind(reason()).then(assert.fail, function(e) { assert(this === defaultThis); assert(e.value === 1); }) }; } specify("if thisArg is rejected timed promise, returned promise is rejected", makeThisArgRejectedTestMethod(function() { return timedRejectedPromiseOf(e); })); specify("if thisArg is rejected immediate promise, returned promise is rejected", makeThisArgRejectedTestMethod(function() { return immediateRejectedPromiseOf(e); })); specify("if thisArg is rejected timed thenable, returned promise is rejected", makeThisArgRejectedTestMethod(function() { return timedRejectedThenableOf(e); })); specify("if thisArg is rejected immediate thenable, returned promise is rejected", makeThisArgRejectedTestMethod(function() { return immediateRejectedThenableOf(e); })); }); describe("github issue", function() { specify("gh-426", function() { return Promise.all([Promise.delay(10)]).bind(THIS).spread(function() { assert.equal(this, THIS); }); }); specify("gh-702-1", function() { return Promise.bind(Promise.delay(1, THIS)).then(function() { assert.equal(this, THIS); }).then(function() { assert.equal(this, THIS); }); }); specify("gh-702-2", function() { return Promise.resolve().bind(Promise.delay(1, THIS)).then(function() { assert.equal(this, THIS); }).then(function() { assert.equal(this, THIS); }); }); }); describe("promised bind", function() { specify("works after following", function() { return Promise.bind(Promise.delay(1, THIS)).then(function() { assert.equal(this, THIS); return Promise.delay(1); }).then(function() { assert.equal(this, THIS); return Promise.delay(1); }).then(function() { assert.equal(this, THIS); }); }); specify("works with spread", function() { return Promise.bind(Promise.delay(1, THIS), [1,2,3]).spread(function() { assert.equal(this, THIS); assert.deepEqual([1,2,3], [].slice.call(arguments)); return Promise.delay(1, [].slice.call(arguments)); }).spread(function() { assert.deepEqual([1,2,3], [].slice.call(arguments)); assert.equal(this, THIS); return Promise.delay(1, [].slice.call(arguments)); }).spread(function() { assert.deepEqual([1,2,3], [].slice.call(arguments)); assert.equal(this, THIS); }); }); specify("works with immediate finally", function() { return Promise.bind(Promise.delay(1, THIS), [1,2,3]).lastly(function() { assert.equal(this, THIS); }).then(function() { assert.equal(this, THIS); }); }); specify("works with delayed finally", function() { return Promise.bind(Promise.delay(1, THIS), [1,2,3]).lastly(function() { assert.equal(this, THIS); return Promise.delay(1); }).then(function() { assert.equal(this, THIS); }); }); specify("works with immediate tap", function() { return Promise.bind(Promise.delay(1, THIS), [1,2,3]).tap(function() { assert.equal(this, THIS); }).then(function() { assert.equal(this, THIS); }); }); specify("works with delayed tap", function() { return Promise.bind(Promise.delay(1, THIS), [1,2,3]).tap(function() { assert.equal(this, THIS); return Promise.delay(1); }).then(function() { assert.equal(this, THIS); }); }); }); ================================================ FILE: test/mocha/bluebird-multiple-instances.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var isNodeJS = testUtils.isNodeJS; var OldPromise = require("./helpers/bluebird0_7_0.js"); if (isNodeJS) { var Promise1 = testUtils.addDeferred(require("../../js/debug/promise.js")()); var Promise2 = testUtils.addDeferred(require("../../js/debug/promise.js")()); var err1 = new Error(); var err2 = new Error(); describe("Separate instances of bluebird", function() { specify("Should have identical Error types", function() { assert(Promise1.CancellationError === Promise2.CancellationError); assert(Promise1.RejectionError === Promise2.RejectionError); assert(Promise1.TimeoutError === Promise2.TimeoutError); }); specify("Should not be identical", function() { assert(Promise1.onPossiblyUnhandledRejection !== Promise2.onPossiblyUnhandledRejection); assert(Promise1 !== Promise2); }); specify("Should have different unhandled rejection handlers", function() { var spy1 = testUtils.getSpy(); var spy2 = testUtils.getSpy(); Promise1.onPossiblyUnhandledRejection(spy1(function(e, promise) { assert(promise instanceof Promise1); assert(!(promise instanceof Promise2)); assert(e === err1); })); Promise2.onPossiblyUnhandledRejection(spy2(function(e, promise) { assert(promise instanceof Promise2); assert(!(promise instanceof Promise1)); assert(e === err2); })); assert(Promise1.onPossiblyUnhandledRejection !== Promise2.onPossiblyUnhandledRejection); var d1 = Promise1.defer(); var d2 = Promise2.defer(); d1.promise.then(function(){ throw err1; }); d2.promise.then(function(){ throw err2; }); setTimeout(function(){ d1.fulfill(); d2.fulfill(); setTimeout(function() { Promise1._unhandledRejectionCheck(); Promise2._unhandledRejectionCheck(); }, 100); }, 1); return Promise.all([spy1.promise, spy2.promise]); }); specify("Should use fast cast", function() { var a = Promise1.defer(); var b = Promise2.cast(a.promise); assert(a.promise._receiver0 === b); }); specify("Should use fast cast with very old version", function() { var a = OldPromise.pending(); var b = Promise1.cast(a.promise); assert(a.promise._receiver0 === b); }); specify("Should return 2 from very old promise", function() { return Promise1.resolve().then( function(){ return OldPromise.cast(0).then(function(){return 2}); }).then(function(two){ assert.equal(two, 2); }); }); specify("Should reject primitive from fast cast", function() { var a = OldPromise.pending(); var b = Promise.resolve(a.promise); a.reject(1); return b.then(assert.fail, function(e) { assert.strictEqual(e, 1); }); }); specify("Should reject object from fast cast", function() { var err = new Error(); var a = OldPromise.pending(); var b = Promise.resolve(a.promise); a.reject(err); return b.then(assert.fail, function(e) { assert.strictEqual(e, err); }); }); }); } ================================================ FILE: test/mocha/call.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var c = { val: 3, method: function() { return [].slice.call(arguments).concat(this.val); } }; describe("call", function() { specify("0 args", function() { return Promise.resolve(c).call("method").then(function(res) { assert.deepEqual([3], res); }); }); specify("1 args", function() { return Promise.resolve(c).call("method", 1).then(function(res) { assert.deepEqual([1, 3], res); }); }); specify("2 args", function() { return Promise.resolve(c).call("method", 1, 2).then(function(res) { assert.deepEqual([1, 2, 3], res); }); }); specify("3 args", function() { return Promise.resolve(c).call("method", 1, 2, 3).then(function(res) { assert.deepEqual([1, 2, 3, 3], res); }); }); specify("10 args", function() { return Promise.resolve(c).call("method", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10).then(function(res) { assert.deepEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3], res); }); }); specify("method not found", function() { var promises = [ Promise.resolve([]).call("abc").then(assert.fail, testUtils.noop), Promise.resolve([]).call("abc", 1, 2, 3, 4, 5, 6, 7).then(assert.fail, testUtils.noop), Promise.resolve([]).call("abc ").then(assert.fail, testUtils.noop), Promise.resolve(null).call("abc", 1, 2, 3, 4, 5, 6, 7).then(assert.fail, testUtils.noop), Promise.resolve(null).call("abc").then(assert.fail, testUtils.noop), Promise.resolve(null).call("abc ").then(assert.fail, testUtils.noop) ]; return Promise.all(promises).then(function(errors) { for (var i = 0; i < errors.length; ++i) { var message = errors[i].message || errors[i].toString(); assert(message.indexOf("has no method") >= 0); } }); }); }); ================================================ FILE: test/mocha/cancel.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var awaitLateQueue = testUtils.awaitLateQueue; describe("Cancellation", function() { specify("requires a function", function() { return new Promise(function(_, __, onCancel) { onCancel(); }).then(assert.fail, function(e) { assert(e instanceof Promise.TypeError); }); }); specify("can register multiple on same promise", function() { var cancelled = 0; var p = new Promise(function(_, __, onCancel) { onCancel(function() {cancelled++}); onCancel(function() {cancelled++}); onCancel(function() {cancelled++}); onCancel(function() {cancelled++}); }); p.cancel(); return awaitLateQueue(function() { assert.equal(4, cancelled); }); }); specify("follower promises' handlers are not called, registered before", function() { var cancelled = 0; var p = new Promise(function(_, __, onCancel) { onCancel(function() {cancelled++}); }); new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() {cancelled++}); }); new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() {cancelled++}); }); new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() {cancelled++}); }); p.cancel(); return awaitLateQueue(function() { assert.equal(1, cancelled); }); }); specify("follower promises' handlers are not called, registered after", function() { var cancelled = 0; var p = new Promise(function(_, __, onCancel) { onCancel(function() {cancelled++}); }); p.cancel(); new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() {cancelled++}); }).suppressUnhandledRejections(); new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() {cancelled++}); }).suppressUnhandledRejections(); new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() {cancelled++}); }).suppressUnhandledRejections(); return awaitLateQueue(function() { assert.equal(1, cancelled); }); }); specify("downstream follower promises' handlers are not called, registered before", function() { var cancelled = 0; var p = new Promise(function(_, __, onCancel) { onCancel(function() {cancelled++}); }); new Promise(function(resolve, _, onCancel) { resolve(p.then()); onCancel(function() {cancelled++}); }); new Promise(function(resolve, _, onCancel) { resolve(p.then()); onCancel(function() {cancelled++}); }); new Promise(function(resolve, _, onCancel) { resolve(p.then()); onCancel(function() {cancelled++}); }); p.cancel(); return awaitLateQueue(function() { assert.equal(1, cancelled); }); }); specify("downstream follower promises' handlers are called, registered after", function() { var cancelled = 0; var p = new Promise(function(_, __, onCancel) { onCancel(function() {cancelled++}); }); p.cancel(); new Promise(function(resolve, _, onCancel) { resolve(p.then()); onCancel(function() {cancelled++}); }).suppressUnhandledRejections(); new Promise(function(resolve, _, onCancel) { resolve(p.then()); onCancel(function() {cancelled++}); }).suppressUnhandledRejections(); new Promise(function(resolve, _, onCancel) { resolve(p.then()); onCancel(function() {cancelled++}); }).suppressUnhandledRejections(); return awaitLateQueue(function() { assert.equal(1, cancelled); }); }); specify("immediately rejected promise immediately cancelled with then in-between", function() { var error = new Error(); var resolve; var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); var p = Promise.reject(error).then().lastly(resolve); p.cancel(); p.caught(function() {}); return result; }); specify("callback is called asynchronously but fate is sealed synchronously", function() { var called = false; var promiseResolve; var promise = new Promise(function(resolve, reject, onCancel) { promiseResolve = resolve; onCancel(function() { called = true; }); }); return awaitLateQueue(function() { promise.cancel(); promiseResolve(); return Promise.resolve().then(function() { assert(called); assert(!promise.isFulfilled()); }); }); }); if (testUtils.isNodeJS) { specify("throws in process if callback throws", function() { var e = new Error(); var promise = new Promise(function(resolve, reject, onCancel) { onCancel(function onCancel() { throw e; }); }); promise.cancel(); return testUtils.awaitGlobalException(function(err) { assert.equal(e, err); }); }); } specify("cancels the promise chain", function() { var called = false; var thens = 0; var resolveChain; var root = new Promise(function(resolve, reject, onCancel) { resolveChain = resolve; onCancel(function() { called = true; }); }).then(function() { thens++; }).then(function() { thens++; }).then(function() { thens++; }); root.cancel(); resolveChain(); return awaitLateQueue(function() { assert.equal(0, thens); assert(called); }); }); specify("calls finally handlers", function() { var called = false; var thens = 0; var resolveChain; var root = new Promise(function(resolve, reject, onCancel) { resolveChain = resolve; onCancel(function() { called = true; }); }); var chain = root.lastly(function() { thens++; }).lastly(function() { thens++; }).lastly(function() { thens++; }); chain.cancel(); resolveChain(); return awaitLateQueue(function() { assert.equal(3, thens); assert(called); }); }); specify("cancels the followee", function() { var called = false; var finalled = false; var promise = new Promise(function(_, __, onCancel) { onCancel(function() { called = true; }); }); var promise2 = new Promise(function(resolve, reject, onCancel) { resolve(promise); }); var promise3 = new Promise(function(resolve, reject, onCancel) { resolve(promise2); }).lastly(function() { finalled = true; }); promise3.cancel(); return awaitLateQueue(function() { assert(called); assert(finalled); }); }); specify("cancels the followee, calling all callbacks and finally handlers", function() { var called = 0; var finalled = 0; var promise = new Promise(function(_, __, onCancel) { onCancel(function() { called++; }); }).lastly(function() { finalled++; }); var promise2 = new Promise(function(resolve, reject, onCancel) { resolve(promise); onCancel(function() { called++; }); }).lastly(function() { finalled++; }); var promise3 = new Promise(function(resolve, reject, onCancel) { resolve(promise2); onCancel(function() { called++; }); }).lastly(function() { finalled++; }); promise3.cancel(); return awaitLateQueue(function() { assert.equal(3, called); assert.equal(3, finalled); }); }); specify("cancels the followee, calling all onCancel callbacks", function() { var called = 0; var promise = new Promise(function(_, __, onCancel) { onCancel(function() { called++; }); }) var promise2 = new Promise(function(resolve, reject, onCancel) { resolve(promise); onCancel(function() { called++; }); }); var promise3 = new Promise(function(resolve, reject, onCancel) { resolve(promise2); onCancel(function() { called++; }); }); promise3.cancel(); return awaitLateQueue(function() { assert.equal(3, called); }); }); specify("can be used for breaking chains early", function() { var called = false; var p = Promise.resolve(1) .then(function(data) { p["break"](); }) .then(function() { called = true; }); return awaitLateQueue(function() { assert(!called); }); }); specify("multiple cancel calls have no effect", function() { var called = 0; var finalled = 0; var req1 = new Promise(function(resolve, _, onCancel) { resolve(); onCancel(function() { called++; }); }); var ret = req1.then(function() { return new Promise(function(_, __, onCancel) { onCancel(function() { called++; }); }); }).then(function() { return new Promise(function(_, __, onCancel) { onCancel(function() { called++; }); }); }).lastly(function() { finalled++; }); req1.then(function() { ret.cancel(); ret.cancel(); ret.cancel(); }); return awaitLateQueue(function() { assert.equal(1, called); assert.equal(1, finalled); }); }); specify("throwing in finally turns into a rejection", function() { var e = new Error(""); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { throw e; }) .caught(function(err) { assert.equal(err, e); }); promise.cancel(); return promise; }); specify("returning an immediately rejected promise in finally turns into a rejection", function() { var e = new Error(""); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { return Promise.reject(e); }) .caught(function(err) { assert.equal(err, e); }); promise.cancel(); return promise; }); specify("returning an eventually rejected promise in finally turns into a rejection", function() { var e = new Error(""); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { return new Promise(function(resolve, reject, onCancel) { Promise.delay(1).then(function() { reject(e); }); }); }) .caught(function(err) { assert.equal(err, e); }); promise.cancel(); return promise; }); specify("finally handler returned promises are awaited for", function() { var awaited = 0; var resolve; var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { return Promise.delay(1).then(function() { awaited++; }); }) .lastly(function() { return Promise.delay(1).then(function() { awaited++; }); }) .lastly(function() { return Promise.delay(1).then(function() { awaited++; }); }) .lastly(function() { resolve(); }) promise.cancel(); return result.then(function() { assert.equal(3, awaited); }); }); specify("finally handler returned promises are skipped if they are cancelled", function() { var cancelled = 0; var resolve; var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); ret.cancel(); return ret; }) .lastly(function() { var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); ret.cancel(); return ret; }) .lastly(function() { var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); ret.cancel(); return ret; }) .lastly(function() { resolve(); }) promise.suppressUnhandledRejections(); promise.cancel(); return result.then(function() { assert.equal(3, cancelled); }); }); specify("finally handler returned promises are skipped if they are eventually cancelled", function() { var cancelled = 0; var resolve; var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); Promise.delay(1).then(function() { ret.cancel(); }); return ret; }) .lastly(function() { var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); Promise.delay(1).then(function() { ret.cancel(); }); return ret; }) .lastly(function() { var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); Promise.delay(1).then(function() { ret.cancel(); }); return ret; }) .lastly(function() { resolve(); }) promise.cancel(); return result.then(function() { assert.equal(3, cancelled); }); }); specify("finally handler returned promises are skipped if theiy are eventually cancelled while following", function() { var cancelled = 0; var resolve; var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); Promise.delay(1).then(function() { ret.cancel(); }); return ret; }) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); Promise.delay(1).then(function() { ret.cancel(); }); return ret; }) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); Promise.delay(1).then(function() { ret.cancel(); }); return ret; }) .lastly(function() { resolve(); }) promise.cancel(); return result.then(function() { assert.equal(6, cancelled); }); }); specify("finally handler returned promises are skipped if theiy are immediately cancelled while following", function() { var cancelled = 0; var resolve; var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); ret.cancel(); return ret; }) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); ret.cancel(); return ret; }) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); ret.cancel(); return ret; }) .lastly(function() { resolve(); }); promise.suppressUnhandledRejections(); promise.cancel(); return result.then(function() { assert.equal(6, cancelled); }); }); specify("finally handler returned promises target are skipped if their follower is eventually cancelled", function() { var cancelled = 0; var resolve; var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); Promise.delay(1).then(function() { ret.cancel(); }); return p; }) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); Promise.delay(1).then(function() { ret.cancel(); }); return p; }) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); Promise.delay(1).then(function() { ret.cancel(); }); return p; }) .lastly(function() { resolve(); }) promise.cancel(); return result.then(function() { return awaitLateQueue(function() { assert.equal(6, cancelled); }); }); }); specify("finally handler returned promises target are skipped if their follower is immediately cancelled", function() { var cancelled = 0; var resolve; var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); var promise = new Promise(function(_, __, onCancel) {}) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); ret.cancel(); return p; }) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); ret.cancel(); return p; }) .lastly(function() { var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }); var ret = new Promise(function(resolve, _, onCancel) { resolve(p); onCancel(function() { cancelled++; }); }); ret.cancel(); return p; }) .lastly(function() { resolve(); }) promise.cancel(); promise.suppressUnhandledRejections(); return result.then(function() { assert.equal(6, cancelled); }); }); specify("attaching handler on already cancelled promise", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); return awaitLateQueue(function() { p.lastly(resolve); return result; }); }); specify("if onCancel callback causes synchronous rejection, it is ignored and cancellation wins", function() { var promisifiedXhr = function() { var xhrReject; var xhr = { then: function(resolve, reject) { xhrReject = reject; }, abort: function() { xhrReject(new Error("")); } }; return new Promise(function(resolve, _, onCancel) { resolve(xhr); onCancel(function() { xhr.abort(); }); }); }; var req = promisifiedXhr().lastly(function() { resolve(); }); req.cancel(); var resolve; return new Promise(function(_, __, onCancel) {resolve = arguments[0]}); }); specify("isCancelled() synchronously returns true after calling cancel() on pending promise", function() { var promise = new Promise(function () {}); promise.cancel(); assert(promise.isCancelled()); }); specify("isCancelled() synchronously returns true after calling cancel() on promise created from .then()", function() { var promise = new Promise(function () {}); var thenPromise = promise.then(); thenPromise.cancel(); assert(thenPromise.isCancelled()); }); specify("gh-166", function() { var f1 = false, f2 = false, f3 = false, f4 = false; var a = Promise.resolve(); a = a.then(function() { f1 = true; return Promise.delay(1); }); a = a.then(function() { f2 = true; return Promise.delay(1); }); a = a.then(function() { f3 = true; return Promise.delay(1); }).then(function() { assert(a.isCancellable()); a.cancel(); }).delay(100); a = a.then(function() { f4 = true; }); var waitingForLongDelay = a; a = a.lastly(function() { assert(f1); assert(f2); assert(f3); assert(!f4); assert(waitingForLongDelay.isCancelled()); resolve(); }); assert(a.isCancellable()); var resolve; var p = new Promise(function(_, __, onCancel) {resolve = arguments[0]}); return p; }); specify("gh-1187", function() { var a = Promise.delay(300).lastly(function() {}); a.cancel(); assert(a.isCancelled()); assert(!a.isCancellable()); }) }); describe("Cancellation with .all", function() { specify("immediately cancelled input", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); Promise.all(p).lastly(resolve); return result; }); specify("eventually cancelled input", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); Promise.all(p).lastly(resolve); return awaitLateQueue(function() { p.cancel(); return result; }); }); specify("immediately cancelled input inside array", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); Promise.all([1,2,p]).lastly(resolve); return result; }); specify("eventually cancelled input inside array", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); Promise.all([1,2,p]).lastly(resolve); return awaitLateQueue(function() { p.cancel(); return result; }); }); specify("immediately cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.all(inputs) .lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve; var result = new Promise(function() {resolve = arguments[0]}); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); specify("eventually cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.all(inputs) .lastly(function() {finalled++; resolve(); }); var resolve; var result = new Promise(function() {resolve = arguments[0]}); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); specify("immediately cancelled output while waiting on promise-for-input", function() { var cancelled = 0; var finalled = 0; var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() { finalled++; }); var all = Promise.all(input) .lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve; var result = new Promise(function() {resolve = arguments[0]}); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 1); assert.equal(finalled, 2); }); }); }); specify("eventually cancelled output while waiting on promise-for-input", function() { var cancelled = 0; var finalled = 0; var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() { finalled++; }); var all = Promise.all(input).lastly(function() {finalled++; resolve(); }); var resolve; var result = new Promise(function() {resolve = arguments[0]}); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 1); assert.equal(finalled, 2); }); }); }); }); describe("Cancellation with .props", function() { specify("immediately cancelled input", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); Promise.props(p).lastly(resolve).suppressUnhandledRejections(); return result; }); specify("eventually cancelled input", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); Promise.props(p).lastly(resolve); return awaitLateQueue(function() { p.cancel(); return result; }); }); specify("immediately cancelled input inside array", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); Promise.props([1,2,p]).lastly(resolve); return result; }); specify("eventually cancelled input inside array", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); Promise.props([1,2,p]).lastly(resolve); return awaitLateQueue(function() { p.cancel(); return result; }); }); specify("immediately cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.props(inputs).lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve; var result = new Promise(function() {resolve = arguments[0]}); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); specify("eventually cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.props(inputs).lastly(function() {finalled++; resolve(); }); var resolve; var result = new Promise(function() {resolve = arguments[0]}); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); specify("immediately cancelled output while waiting on promise-for-input", function() { var cancelled = 0; var finalled = 0; var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() { finalled++; }); var all = Promise.props(input).lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve; var result = new Promise(function() {resolve = arguments[0]}); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 1); assert.equal(finalled, 2); }); }); }); specify("eventually cancelled output while waiting on promise-for-input", function() { var cancelled = 0; var finalled = 0; var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() { finalled++; }); var all = Promise.props(input) .lastly(function() {finalled++; resolve(); }); var resolve; var result = new Promise(function() {resolve = arguments[0]}); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 1); assert.equal(finalled, 2); }); }); }); }); describe("Cancellation with .some", function() { specify("immediately cancelled input", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); Promise.some(p, 1).lastly(resolve); return result; }); specify("eventually cancelled input", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); Promise.some(p, 1).lastly(resolve); return awaitLateQueue(function() { p.cancel(); return result; }); }); specify("immediately cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.some(inputs, 1) .lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve; var result = new Promise(function() {resolve = arguments[0]}); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); specify("eventually cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.some(inputs, 1) .lastly(function() {finalled++; resolve(); }); var resolve; var result = new Promise(function() {resolve = arguments[0]}); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); specify("immediately cancelled output while waiting on promise-for-input", function() { var cancelled = 0; var finalled = 0; var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() { finalled++; }); var all = Promise.some(input, 1) .lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve; var result = new Promise(function() {resolve = arguments[0]}); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 1); assert.equal(finalled, 2); }); }); }); specify("eventually cancelled output while waiting on promise-for-input", function() { var cancelled = 0; var finalled = 0; var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() { finalled++; }); var all = Promise.some(input, 1) .lastly(function() {finalled++; resolve(); }); var resolve; var result = new Promise(function() {resolve = arguments[0]}); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 1); assert.equal(finalled, 2); }); }); }); specify("some promises are cancelled immediately", function() { var resolve; var p1 = new Promise(function(_, __, onCancel) {}); var p2 = new Promise(function(_, __, onCancel) {}); var p3 = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); p1.cancel(); p2.cancel(); resolve(1); return Promise.some([p1, p2, p3], 1).then(function(result) { assert.deepEqual([1], result); }); }); specify("some promises are cancelled eventually", function() { var resolve; var p1 = new Promise(function(_, __, onCancel) {}); var p2 = new Promise(function(_, __, onCancel) {}); var p3 = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); Promise.delay(1).then(function() { p1.cancel(); p2.cancel(); return Promise.delay(1).then(function() { resolve(1); }); }); return Promise.some([p1, p2, p3], 1).then(function(result) { assert.deepEqual([1], result); }); }); specify("promise for some promises that are cancelled immediately", function() { var resolve; var p1 = new Promise(function(_, __, onCancel) {}); var p2 = new Promise(function(_, __, onCancel) {}); var p3 = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); p1.cancel(); p2.cancel(); resolve(1); var promise = Promise.delay(1, [p1, p2, p3]); return Promise.some(promise, 1).then(function(result) { assert.deepEqual([1], result); }); }); specify("promise for some promises that are cancelled eventually", function() { var resolve; var p1 = new Promise(function(_, __, onCancel) {}); var p2 = new Promise(function(_, __, onCancel) {}); var p3 = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); Promise.delay(1).then(function() { p1.cancel(); p2.cancel(); return Promise.delay(1).then(function() { resolve(1); }); }); var promise = Promise.delay(1, [p1, p2, p3]); return Promise.some(promise, 1).then(function(result) { assert.deepEqual([1], result); }); }); specify("all promises cancel, not enough for fulfillment - immediately", function() { var resolve; var p1 = new Promise(function(_, __, onCancel) {}); var p2 = new Promise(function(_, __, onCancel) {}); var p3 = new Promise(function(_, __, onCancel) {}); var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); p1.cancel(); p2.cancel(); p3.cancel(); Promise.some([p1, p2, p3], 1).then(assert.fail, function(e) { assert(e instanceof Promise.CancellationError); resolve(); }); return result; }); specify("all promises cancel, not enough for fulfillment - eventually", function() { var resolve; var p1 = new Promise(function(_, __, onCancel) {}); var p2 = new Promise(function(_, __, onCancel) {}); var p3 = new Promise(function(_, __, onCancel) {}); var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); Promise.delay(1).then(function() { p1.cancel(); p2.cancel(); return Promise.delay(1).then(function() { p3.cancel(); }); }); Promise.some([p1, p2, p3], 1).then(assert.fail, assert.fail).lastly(resolve); return result; }); specify("some promises cancel, some reject, not enough for fulfillment - immediately", function() { var error = new Error(); var reject; var p1 = new Promise(function(_, __, onCancel) {}); var p2 = new Promise(function(_, __, onCancel) {}); var p3 = new Promise(function(_, __, onCancel) {reject = arguments[1];}); p1.cancel(); p2.cancel(); reject(error); return Promise.some([p1, p2, p3], 1).then(assert.fail, function(result) { assert(result instanceof Promise.AggregateError); assert.equal(1, result.length); assert.equal(error, result[0]); }); }); specify("some promises cancel, some reject, not enough for fulfillment - eventually", function() { var error = new Error(); var reject; var p1 = new Promise(function(_, __, onCancel) {}); var p2 = new Promise(function(_, __, onCancel) {}); var p3 = new Promise(function(_, __, onCancel) {reject = arguments[1];}); Promise.delay(1).then(function() { p1.cancel(); p2.cancel(); return Promise.delay(1).then(function() { reject(error); }); }); return Promise.some([p1, p2, p3], 1).then(assert.fail, function(result) { assert(result instanceof Promise.AggregateError); assert.equal(1, result.length); assert.equal(error, result[0]); }); }); }); describe("Cancellation with .reduce", function() { specify("initialValue immediately cancelled immediate input", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; initialValue.cancel(); Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); specify("initialValue eventually cancelled immediate input", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); return awaitLateQueue(function() { initialValue.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); }); }); specify("initialValue eventually cancelled eventual input", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = new Promise(function(_, __, onCancel) {}); Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); return awaitLateQueue(function() { initialValue.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); }); }); specify("initialValue immediately cancelled eventual input", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = new Promise(function(_, __, onCancel) {}); initialValue.cancel(); Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); specify("returned promise cancels immediately", function() { var cancelled = 0; var finalled = 0; var inputs = [1, 2, new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; Promise.reduce(inputs, function(a, b) { finalled++; var ret = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}); ret.cancel(); return ret; }).lastly(function() { finalled++; }); return awaitLateQueue(function() { assert.equal(3, finalled); assert.equal(1, cancelled); }); }); specify("returned promise cancels eventually", function() { var cancelled = 0; var finalled = 0; var inputs = [1, 2, new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; Promise.reduce(inputs, function(a, b) { finalled++; var ret = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}); awaitLateQueue(function() { ret.cancel(); }); return ret; }).lastly(function() { finalled++; }); return awaitLateQueue(function() { return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(3, finalled); assert.equal(1, cancelled); }); }); }); }); specify("input immediately cancelled while waiting initialValue", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = new Promise(function(_, __, onCancel) {}); inputs.cancel(); Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); return awaitLateQueue(function() { assert.equal(1, finalled); assert.equal(0, cancelled); }); }); specify("input eventually cancelled while waiting initialValue", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = new Promise(function(_, __, onCancel) {}); Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); return awaitLateQueue(function() { inputs.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(1, finalled); assert.equal(0, cancelled); }); }); }); }); specify("output immediately cancelled while waiting inputs", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}); var all = Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); all.cancel(); return awaitLateQueue(function() { assert.equal(3, finalled); assert.equal(2, cancelled); }); }); specify("output immediately cancelled while waiting initialValue", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); all.cancel(); return awaitLateQueue(function() { assert.equal(5, finalled); assert.equal(4, cancelled); }); }); specify("output immediately cancelled while waiting firstValue", function() { var initialValue = 1; var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); all.cancel(); return awaitLateQueue(function() { assert.equal(4, finalled); assert.equal(3, cancelled); }); }); specify("output immediately cancelled while waiting firstValue and secondValue", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.reduce(inputs, function(){ finalled++; }).lastly(function() { finalled++; }); all.cancel(); return awaitLateQueue(function() { assert.equal(4, finalled); assert.equal(3, cancelled); }); }); specify("output immediately cancelled while waiting for a result", function() { var cancelled = 0; var finalled = 0; var inputs = [1, 2, new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.reduce(inputs, function(a, b) { finalled++; return new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}); }).lastly(function() { finalled++; }); all.cancel(); return awaitLateQueue(function() { assert.equal(4, finalled); assert.equal(2, cancelled); }); }); specify("output eventually cancelled while waiting inputs", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}); var all = Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); return awaitLateQueue(function() { all.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(3, finalled); assert.equal(2, cancelled); }); }); }); }); specify("output eventually cancelled while waiting initialValue", function() { var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); return awaitLateQueue(function() { all.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(5, finalled); assert.equal(4, cancelled); }); }); }); }); specify("output eventually cancelled while waiting firstValue", function() { var initialValue = 1; var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.reduce(inputs, function(){ finalled++; }, initialValue).lastly(function() { finalled++; }); return awaitLateQueue(function() { all.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(4, finalled); assert.equal(3, cancelled); }); }); }); }); specify("output eventually cancelled while waiting firstValue and secondValue", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.reduce(inputs, function(){ finalled++; }).lastly(function() { finalled++; }); return awaitLateQueue(function() { all.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(4, finalled); assert.equal(3, cancelled); }); }); }); }); specify("output eventually cancelled while waiting for a result", function() { var cancelled = 0; var finalled = 0; var inputs = [1, 2, new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.reduce(inputs, function(a, b) { finalled++; return new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}); }).lastly(function() { finalled++; }); return awaitLateQueue(function() { all.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(4, finalled); assert.equal(2, cancelled); }); }); }); }); }); describe("Cancellation with .map", function() { specify("immediately cancelled input", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); Promise.map(p, function(){}).lastly(resolve); return result; }); specify("eventually cancelled input", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); Promise.map(p, function(){}).lastly(resolve); return awaitLateQueue(function() { p.cancel(); return result; }); }); specify("immediately cancelled input inside array", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); Promise.map([1,2,p], function(){}).lastly(resolve); return result; }); specify("eventually cancelled input inside array", function() { var resolve; var result = new Promise(function() {resolve = arguments[0]}); var p = new Promise(function(_, __, onCancel) {}); Promise.map([1,2,p], function(){}).lastly(resolve); return awaitLateQueue(function() { p.cancel(); return result; }); }); specify("immediately cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.map(inputs, function(){}) .lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve; var result = new Promise(function() {resolve = arguments[0]}); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); specify("eventually cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.map(inputs, function(){}) .lastly(function() {finalled++; resolve(); }); var resolve; var result = new Promise(function() {resolve = arguments[0]}); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); specify("immediately cancelled output while waiting on promise-for-input", function() { var cancelled = 0; var finalled = 0; var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() { finalled++; }); var all = Promise.map(input, function(){}) .lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve; var result = new Promise(function() {resolve = arguments[0]}); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 1); assert.equal(finalled, 2); }); }); }); specify("eventually cancelled output while waiting on promise-for-input", function() { var cancelled = 0; var finalled = 0; var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() { finalled++; }); var all = Promise.map(input, function(){}) .lastly(function() {finalled++; resolve(); }); var resolve; var result = new Promise(function() {resolve = arguments[0]}); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 1); assert.equal(finalled, 2); }); }); }); specify("result cancelled immediately while there are in-flight returned promises", function() { var cancelled = 0; var finalled = 0; var all = Promise.map([1, 2, 3], function(value, index) { return new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) }).lastly(function() { finalled++; }); all.cancel(); return awaitLateQueue(function() { assert.equal(4, finalled); assert.equal(3, cancelled); }); }); specify("result cancelled eventually while there are in-flight returned promises", function() { var cancelled = 0; var finalled = 0; var all = Promise.map([1, 2, 3], function(value, index) { return new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) }).lastly(function() { finalled++; }); return awaitLateQueue(function() { all.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(4, finalled); assert.equal(3, cancelled); }); }); }); }); specify("returned promise cancelled immediately while there are in-flight returned promises", function() { var cancelled = 0; var finalled = 0; Promise.map([1, 2, 3], function(value, index) { var ret = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}); if (index === 2) { ret.cancel(); } return ret; }).lastly(function() { finalled++; }); return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); specify("returned promise cancelled eventually while there are in-flight returned promises", function() { var cancelled = 0; var finalled = 0; Promise.map([1, 2, 3], function(value, index) { var ret = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}); if (index === 2) { awaitLateQueue(function() { ret.cancel(); }); } return ret; }).lastly(function() { finalled++; }); return awaitLateQueue(function() { return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); }); }); }); describe("Cancellation with .bind", function() { specify("immediately cancelled promise passed as ctx", function() { var finalled = 0; var cancelled = 0; var ctx = new Promise(function(_, __, onCancel) {}); ctx.cancel(); Promise.bind(ctx).lastly(function() { finalled++; }).suppressUnhandledRejections(); return awaitLateQueue(function() { assert.equal(1, finalled); assert.equal(0, cancelled); }); }); specify("eventually cancelled promise passed as ctx", function() { var finalled = 0; var cancelled = 0; var ctx = new Promise(function(_, __, onCancel) {}); Promise.bind(ctx).lastly(function() { finalled++; }); return awaitLateQueue(function() { ctx.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(1, finalled); assert.equal(0, cancelled); }); }); }); }); specify("main promise is immediately cancelled while waiting on binding", function() { var finalled = 0; var cancelled = 0; var resolve; var ctx = new Promise(function(_, __, onCancel) {resolve = arguments[0];}); var main = new Promise(function(_, __, onCancel) {}); main.cancel(); main.bind(ctx).lastly(function() { finalled++; }).suppressUnhandledRejections(); return awaitLateQueue(function() { resolve(); return ctx; }).then(function() { return awaitLateQueue(function() { assert.equal(1, finalled); assert.equal(0, cancelled); }); }); }); specify("main promise is eventually cancelled while waiting on binding", function() { var finalled = 0; var cancelled = 0; var ctx = new Promise(function(_, __, onCancel) {}); var main = new Promise(function(_, __, onCancel) {}); main.bind(ctx).lastly(function() { finalled++; }); return awaitLateQueue(function() { main.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(1, finalled); assert.equal(0, cancelled); }); }); }); }); specify("main promise is immediately cancelled with immediate binding", function() { var finalled = 0; var cancelled = 0; var ctx = {}; var main = new Promise(function(_, __, onCancel) {}); main.bind(ctx).lastly(function() { assert.equal(this, ctx); finalled++; }); main.cancel(); return awaitLateQueue(function() { assert.equal(1, finalled); assert.equal(0, cancelled); }); }); specify("main promise is eventually cancelled with immediate binding", function() { var finalled = 0; var cancelled = 0; var ctx = {}; var main = new Promise(function(_, __, onCancel) {}); main.bind(ctx).lastly(function() { assert.equal(this, ctx); finalled++; }); return awaitLateQueue(function() { main.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(1, finalled); assert.equal(0, cancelled); }); }); }); }); specify("result is immediately cancelled while waiting for binding", function() { var finalled = 0; var cancelled = 0; var ctx = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }).lastly(function() { finalled++; }); var result = Promise.bind(ctx).lastly(function() { finalled++; }); result.cancel(); return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); specify("result is eventually cancelled while waiting for binding", function() { var finalled = 0; var cancelled = 0; var ctx = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }).lastly(function() { finalled++; }); var result = Promise.bind(ctx).lastly(function() { finalled++; }); return awaitLateQueue(function() { result.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); }); }); specify("result is immediately cancelled while waiting for main promise", function() { var finalled = 0; var cancelled = 0; var ctx = {}; var main = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }).lastly(function() { finalled++; }); var result = main.bind(ctx).lastly(function() { assert.equal(this, ctx); finalled++; }); result.cancel(); return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); specify("result is eventually cancelled while waiting for main promise", function() { var finalled = 0; var cancelled = 0; var ctx = {}; var main = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }).lastly(function() { finalled++; }); var result = main.bind(ctx).lastly(function() { assert.equal(this, ctx); finalled++; }); return awaitLateQueue(function() { result.cancel(); return Promise.resolve().then(function() { return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(1, cancelled); }); }); }); }); }); describe("Cancellation with .join", function() { specify("immediately cancelled input inside array", function() { var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); Promise.join(1,2,p, assert.fail).then(reject, function(e) { assert(e instanceof Promise.CancellationError); resolve(); }); return result; }); specify("eventually cancelled input inside array", function() { var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); var p = new Promise(function(_, __, onCancel) {}); Promise.join(1,2,p, assert.fail).then(reject, reject).lastly(resolve); return awaitLateQueue(function() { p.cancel(); return result; }); }); specify("immediately cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.join(inputs[0], inputs[1], inputs[2], assert.fail) .then(reject, reject) .lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); specify("eventually cancelled output", function() { var cancelled = 0; var finalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) ]; var all = Promise.join(inputs[0], inputs[1], inputs[2], assert.fail) .then(reject, reject) .lastly(function() {finalled++; resolve(); }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); }); }); }); }); describe("Cancellation with .reflect", function() { specify("immediately cancelled", function() { var promise = new Promise(function(_, __, onCancel) {}); promise.cancel(); return promise.reflect().then(function(value) { assert(!value.isFulfilled()); assert(!value.isRejected()); assert(!value.isPending()); assert(value.isCancelled()); }); }); specify("eventually cancelled", function() { var promise = new Promise(function(_, __, onCancel) {}); var ret = promise.reflect().then(function(value) { assert(!value.isFulfilled()); assert(!value.isRejected()); assert(!value.isPending()); assert(value.isCancelled()); }); Promise.delay(1).then(function() { promise.cancel(); }); return ret; }); }); describe("Cancellation with .using", function() { specify("immediately cancelled input", function() { var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); var p = new Promise(function(_, __, onCancel) {}); p.cancel(); var disposerCalled = false; var disposable = new Promise(function(_, __, onCancel) { setTimeout(arguments[0], 1); }).disposer(function() { disposerCalled = true; }); Promise.using(1, disposable, p, assert.fail).then(reject, function(e) { assert(e instanceof Promise.CancellationError); assert(disposerCalled); resolve(); }); return result; }); specify("eventually cancelled input", function() { var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); var p = new Promise(function(_, __, onCancel) {}); var disposerCalled = false; var disposable = new Promise(function(_, __, onCancel) { setTimeout(arguments[0], 1); }).disposer(function() { disposerCalled = true; }); Promise.using(1, disposable, p, assert.fail).then(reject, reject).lastly(function() { assert(disposerCalled); resolve(); }); return awaitLateQueue(function() { p.cancel(); return result; }); }); specify("eventually cancelled input with 1 fulfilled disposer", function() { var resolve, reject; var fulfillResource; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); var p = new Promise(function(_, __, onCancel) {}); var disposerCalled = false; var disposable = new Promise(function(_, __, onCancel) {fulfillResource = arguments[0];}).disposer(function() { disposerCalled = true; }); Promise.using(1,disposable,p, assert.fail).then(reject, reject).lastly(function() { assert(disposerCalled); resolve(); }); return awaitLateQueue(function() { fulfillResource({}); p.cancel(); return result; }); }); specify("immediately cancelled output", function() { var cancelled = 0; var finalled = 0; var disposerCalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) .disposer(function() { disposerCalled++; }), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) .disposer(function() { disposerCalled++; }), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) .disposer(function() { disposerCalled++; }) ]; var all = Promise.using(inputs[0], inputs[1], inputs[2], assert.fail) .then(reject, reject) .lastly(function() {finalled++; resolve(); }); all.cancel(); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); assert.equal(disposerCalled, 0); }); }); }); specify("eventually cancelled output", function() { var cancelled = 0; var finalled = 0; var disposerCalled = 0; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) .disposer(function() { disposerCalled++; }), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) .disposer(function() { disposerCalled++; }), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) .disposer(function() { disposerCalled++; }) ]; var all = Promise.using(inputs[0], inputs[1], inputs[2], assert.fail) .then(reject, reject) .lastly(function() {finalled++; resolve(); }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); Promise.delay(1).then(function() { all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 3); assert.equal(finalled, 4); assert.equal(disposerCalled, 0); }); }); }); specify("eventually cancelled output with 1 disposer fulfilled", function() { var cancelled = 0; var finalled = 0; var disposerCalled = 0; var fulfillResource; var inputs = [ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) .disposer(function() { disposerCalled++; }), new Promise(function(_, __, onCancel) {fulfillResource = arguments[0];}) .disposer(function() { disposerCalled++; }), new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }) .lastly(function() {finalled++}) .disposer(function() { disposerCalled++; }) ]; var all = Promise.using(inputs[0], inputs[1], inputs[2], assert.fail) .then(reject, reject) .lastly(function() {finalled++; resolve(); }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); Promise.delay(1).then(function() { fulfillResource({}) all.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 2); assert.equal(finalled, 3); assert.equal(disposerCalled, 1); }); }); }); specify("result immediately cancelled when inside handler", function() { var disposerCalled = 0; var cancelled = 0; var finalled = 0; var resource1 = Promise.resolve().disposer(function() { disposerCalled++; }); var resource2 = Promise.resolve().disposer(function() { disposerCalled++; }); var all = Promise.using(resource1, resource2, function(res1, res2) { var ret = new Promise(function(_, __, onCancel) {}); all.cancel(); return ret; }).then(reject, reject) .lastly(function() {finalled++; resolve(); }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 0); assert.equal(finalled, 1); assert.equal(disposerCalled, 2); }); }); }); specify("result eventually cancelled when inside handler", function() { var disposerCalled = 0; var cancelled = 0; var finalled = 0; var resource1 = Promise.resolve().disposer(function() { disposerCalled++; }); var resource2 = Promise.resolve().disposer(function() { disposerCalled++; }); var all = Promise.using(resource1, resource2, function(res1, res2) { var ret = new Promise(function(_, __, onCancel) {}); Promise.delay(1).then(function() { all.cancel(); }); return ret; }).then(reject, reject) .lastly(function() {finalled++; resolve(); }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 0); assert.equal(finalled, 1); assert.equal(disposerCalled, 2); }); }); }); specify("promise returned from handler immediately cancelled", function() { var disposerCalled = 0; var cancelled = 0; var finalled = 0; var resource1 = Promise.resolve().disposer(function() { disposerCalled++; }); var resource2 = Promise.resolve().disposer(function() { disposerCalled++; }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); var all = Promise.using(resource1, resource2, function(res1, res2) { var ret = new Promise(function(_, __, onCancel) {}); ret.cancel(); return ret; }).then(reject, function(e) { if(!(e instanceof Promise.CancellationError)) reject(new Error()); }).lastly(function() {finalled++; resolve(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 0); assert.equal(finalled, 1); assert.equal(disposerCalled, 2); }); }); }); specify("promise returned from handler eventually cancelled", function() { var disposerCalled = 0; var cancelled = 0; var finalled = 0; var resource1 = Promise.resolve().disposer(function() { disposerCalled++; }); var resource2 = Promise.resolve().disposer(function() { disposerCalled++; }); var all = Promise.using(resource1, resource2, function(res1, res2) { var ret = new Promise(function(_, __, onCancel) {}); Promise.delay(1).then(function() { ret.cancel(); }); return ret; }).then(reject, reject) .lastly(function() {finalled++; resolve(); }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); return result.then(function() { return awaitLateQueue(function() { assert.equal(cancelled, 0); assert.equal(finalled, 1); assert.equal(disposerCalled, 2); }); }); }); }); describe("Multi-branch cancellation", function() { specify("3 branches, 1 cancels", function() { var successCalls = 0; var rootGotCancelled = false; var resolveRoot; var root = new Promise(function(resolve, __, onCancel) { onCancel(function() { rootGotCancelled = true; }); resolveRoot = resolve; }); var a = root.then(function() { successCalls++; }); var b = root.then(function() { successCalls++; }); var c = root.then(function() { successCalls++; }); return awaitLateQueue(function() { b.cancel(); }).then(function() { return awaitLateQueue(resolveRoot); }).then(function() { return awaitLateQueue(function() { assert(!rootGotCancelled); assert.equal(2, successCalls); }); }); }); specify("3 branches, 3 cancels", function() { var successCalls = 0; var rootGotCancelled = false; var resolveRoot; var root = new Promise(function(resolve, __, onCancel) { onCancel(function() { rootGotCancelled = true; }); resolveRoot = resolve; }); var a = root.then(function() { successCalls++; }); var b = root.then(function() { successCalls++; }); var c = root.then(function() { successCalls++; }); return awaitLateQueue(function() { a.cancel(); b.cancel(); c.cancel(); }).then(function() { return awaitLateQueue(resolveRoot); }).then(function() { return awaitLateQueue(function() { assert(rootGotCancelled); assert.equal(0, successCalls); }); }); }); specify("3 branches, root cancels", function() { var successCalls = 0; var rootGotCancelled = false; var resolveRoot; var root = new Promise(function(resolve, __, onCancel) { onCancel(function() { rootGotCancelled = true; }); resolveRoot = resolve; }); var a = root.then(function() { successCalls++; }); var b = root.then(function() { successCalls++; }); var c = root.then(function() { successCalls++; }); return awaitLateQueue(function() { root.cancel(); }).then(function() { return awaitLateQueue(resolveRoot); }).then(function() { return awaitLateQueue(function() { assert(rootGotCancelled); assert.equal(0, successCalls); }); }); }); specify("3 branches, each have 3 branches, all children of b cancel", function() { var successCalls = 0; var rootGotCancelled = false; var resolveRoot; var root = new Promise(function(resolve, __, onCancel) { onCancel(function() { rootGotCancelled = true; }); resolveRoot = resolve; }); var a = root.then(function() { successCalls++; }); var a1 = a.then(function() { successCalls++; }); var a2 = a.then(function() { successCalls++; }); var a3 = a.then(function() { successCalls++; }); var b = root.then(function() { successCalls++; }); var b1 = b.then(function() { successCalls++; }); var b2 = b.then(function() { successCalls++; }); var b3 = b.then(function() { successCalls++; }); var c = root.then(function() { successCalls++; }); var c1 = c.then(function() { successCalls++; }); var c2 = c.then(function() { successCalls++; }); var c3 = c.then(function() { successCalls++; }); return awaitLateQueue(function() { b1.cancel(); b2.cancel(); b3.cancel(); }).then(function() { return awaitLateQueue(resolveRoot); }).then(function() { return awaitLateQueue(function() { assert(!rootGotCancelled); assert.equal(8, successCalls); assert(b.isCancelled()); assert(b1.isCancelled()); assert(b2.isCancelled()); assert(b3.isCancelled()); }); }); }); specify("3 branches, each have 3 branches, all grand children cancel", function() { var successCalls = 0; var rootGotCancelled = false; var resolveRoot; var root = new Promise(function(resolve, __, onCancel) { onCancel(function() { rootGotCancelled = true; }); resolveRoot = resolve; }); var a = root.then(function() { successCalls++; }); var a1 = a.then(function() { successCalls++; }); var a2 = a.then(function() { successCalls++; }); var a3 = a.then(function() { successCalls++; }); var b = root.then(function() { successCalls++; }); var b1 = b.then(function() { successCalls++; }); var b2 = b.then(function() { successCalls++; }); var b3 = b.then(function() { successCalls++; }); var c = root.then(function() { successCalls++; }); var c1 = c.then(function() { successCalls++; }); var c2 = c.then(function() { successCalls++; }); var c3 = c.then(function() { successCalls++; }); return awaitLateQueue(function() { a1.cancel(); a2.cancel(); a3.cancel(); b1.cancel(); b2.cancel(); b3.cancel(); c1.cancel(); c2.cancel(); c3.cancel(); }).then(function() { return awaitLateQueue(resolveRoot); }).then(function() { return awaitLateQueue(function() { assert(rootGotCancelled); assert.equal(0, successCalls); assert(a.isCancelled()); assert(a1.isCancelled()); assert(a2.isCancelled()); assert(a3.isCancelled()); assert(b.isCancelled()); assert(b1.isCancelled()); assert(b2.isCancelled()); assert(b3.isCancelled()); assert(c.isCancelled()); assert(c1.isCancelled()); assert(c2.isCancelled()); assert(c3.isCancelled()); }); }); }); }); if (testUtils.isNodeJS) { describe("issues", function() { specify("cancels the promise chain within a domain GH963", function() { var called = 0; var thens = 0; var resolveChain; var Domain = require("domain"); var domain = Domain.create(); domain.enter(); var root = new Promise(function(resolve, reject, onCancel) { resolveChain = resolve; onCancel(function() { called++; }); }).then(function() { thens++; }).then(function() { thens++; }).then(function() { thens++; }).lastly(function() { called++; }); root.cancel(); resolveChain(); return awaitLateQueue(function() { assert.equal(0, thens); assert.equal(2, called); domain.exit(); }); }); }); describe("GH926", function() { var clear, set; var clears = 0; before(function() { clears = 0; set = setTimeout; clear = clearTimeout; setTimeout = function() { return set.apply(this, arguments); }; clearTimeout = function() { clears++; return clear.apply(this, arguments); }; }); after(function() { clears = 0; setTimeout = set; clearTimeout = clear; }); specify("GH926", function() { var calls = 0; var p = new Promise(function(resolve, reject, onCancel) { onCancel(function() { calls++; }); }) .timeout(10000000) .lastly(function() { calls++; }); p.cancel(); return awaitLateQueue(function() { assert.equal(2, calls); assert.equal(1, clears); }); }); }); describe("GH1000", function() { var clear, set; var clears = 0, sets = 0; beforeEach(function() { clears = 0; sets = 0; set = setTimeout; clear = clearTimeout; setTimeout = function() { sets++; return set.apply(this, arguments); }; clearTimeout = function() { clears++; return clear.apply(this, arguments); }; }); afterEach(function() { clears = 0; sets = 0; setTimeout = set; clearTimeout = clear; }); specify("delay", function() { var calls = 0, never = 0; var p = Promise .delay(10000000) .then(function () { never++; }); p.lastly(function() { if (p.isCancelled()) { calls++; } }); p.cancel(); return awaitLateQueue(function() { assert.equal(0, never); assert.equal(1, calls); assert.equal(1, clears); }); }); specify("delay with value", function() { var calls = 0, never = 0; var p = Promise .delay(10000000, true) .then(function () { never++; }); p.lastly(function() { if (p.isCancelled()) { calls++; } }); return Promise.delay(10) .then(function () { p.cancel(); return awaitLateQueue(function() { assert.equal(0, never); assert.equal(1, calls); assert.equal(1, clears); }); }); }); specify("cancel delay cancels inner promise", function() { var calls = 0, never = 0; var pInner = Promise.delay(1000) .then(function () { never++; }); pInner.lastly(function() { if (pInner.isCancelled()) { calls++; } }); var pOuter = Promise .delay(10000000, pInner) .then(function () { never++; }); pOuter.lastly(function() { if (pOuter.isCancelled()) { calls++; } }); pOuter.cancel(); return awaitLateQueue(function() { assert.equal(0, never); assert.equal(2, calls); }); }); specify("cancel inner promise cancels delay", function() { var calls = 0, never = 0; var pInner = Promise.delay(1000) .then(function () { never++; }); pInner.lastly(function() { if (pInner.isCancelled()) { calls++; } }); var pOuter = Promise .delay(10000000, pInner) .then(function () { never++; }); pOuter.lastly(function() { if (pOuter.isCancelled()) { calls++; } }); pInner.cancel(); return awaitLateQueue(function() { assert.equal(0, never); assert.equal(2, calls); }); }); }); } ================================================ FILE: test/mocha/catch_filter.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var CustomError = function(){}; CustomError.prototype = new Error(); var predicateFilter = function(e) { return (/invalid/).test(e.message); } function BadError(msg) { this.message = msg; return this; } function predicatesUndefined(e) { return e === void 0; } function predicatesPrimitiveString(e) { return /^asd$/.test(e); } var token = {}; var returnToken = function() { return token; }; var assertToken = function(val) { assert.strictEqual(token, val); }; describe("A promise handler that throws a TypeError must be caught", function() { specify("in a middle.caught filter", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ a.b.c.d() }).then(assert.fail).caught(SyntaxError, function(e){ assert.fail(); }).caught(Promise.TypeError, returnToken) .then(assertToken); }); specify("in a generic.caught filter that comes first", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ a.b.c.d() }).then(assert.fail, returnToken).caught(SyntaxError, function(e){ assert.fail(); }).caught(Promise.TypeError, function(e){ assert.fail(); }).then(assertToken); }); specify("in an explicitly generic.caught filter that comes first", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ a.b.c.d() }) .then(assert.fail) .caught(Error, returnToken) .caught(SyntaxError, assert.fail) .caught(Promise.TypeError, assert.fail) .then(assertToken); }); specify("in a specific handler after thrown in generic", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ a.b.c.d() }).then(assert.fail, function(e){ throw e }).caught(SyntaxError, assert.fail) .then(assert.fail) .caught(Promise.TypeError, returnToken) .then(assertToken); }); specify("in a multi-filter handler", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ a.b.c.d() }) .then(assert.fail) .caught(SyntaxError, TypeError, returnToken) .then(assertToken); }); specify("in a specific handler after non-matching multi.caught handler", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ a.b.c.d() }) .then(assert.fail) .caught(SyntaxError, CustomError, assert.fail) .caught(Promise.TypeError, returnToken) .then(assertToken) }); }); describe("A promise handler that throws a custom error", function() { specify("Is filtered if inheritance was done even remotely properly", function() { var a = Promise.defer(); var b = new CustomError(); a.fulfill(3); return a.promise.then(function(){ throw b; }) .then(assert.fail) .caught(SyntaxError, assert.fail) .caught(Promise.TypeError, assert.fail) .caught(CustomError, function(e){ assert.equal(e, b); return token; }) .then(assertToken); }); specify("Is filtered along with built-in errors", function() { var a = Promise.defer(); var b = new CustomError(); a.fulfill(3); return a.promise.then(function(){ throw b; }) .then(assert.fail) .caught(Promise.TypeError, SyntaxError, CustomError, returnToken) .caught(assert.fail) .then(assertToken) }); specify("Throws after matched type handler throws", function() { var err = new Promise.TypeError(); var err2 = new Error(); return Promise.reject(err).caught(Promise.TypeError, function() { throw err2; }).then(assert.fail, function(e) { assert.strictEqual(err2, e); }); }); }); describe("A promise handler that throws a CustomError must be caught", function() { specify("in a middle.caught filter", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new CustomError() }) .caught(SyntaxError, assert.fail) .caught(CustomError, returnToken) .then(assertToken); }); specify("in a generic.caught filter that comes first", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new CustomError() }).then(assert.fail, returnToken) .caught(SyntaxError, assert.fail) .caught(CustomError, assert.fail) .then(assertToken) }); specify("in an explicitly generic.caught filter that comes first", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new CustomError() }) .caught(Error, returnToken) .caught(SyntaxError, assert.fail) .caught(CustomError, assert.fail) .then(assertToken); }); specify("in a specific handler after thrown in generic", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new CustomError() }).then(assert.fail, function(e){ throw e }) .caught(SyntaxError, assert.fail) .caught(CustomError, returnToken) .then(assertToken); }); specify("in a multi-filter handler", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new CustomError() }) .caught(SyntaxError, CustomError, returnToken) .then(assertToken) }); specify("in a specific handler after non-matching multi.caught handler", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new CustomError() }) .caught(SyntaxError, TypeError, assert.fail) .caught(CustomError, returnToken) .then(assertToken); }); }); describe("A promise handler that is caught in a filter", function() { specify("is continued normally after returning a promise in filter", function() { var a = Promise.defer(); var c = Promise.defer(); var b = new CustomError(); a.fulfill(3); setTimeout(function(){ c.fulfill(3); }, 1); return a.promise.then(function(){ throw b; }).caught(SyntaxError, function(e){ assert.fail(); }).caught(Promise.TypeError, function(e){ assert.fail(); }).caught(CustomError, function(e){ assert.equal(e, b); return c.promise.thenReturn(token); }).then(assertToken, assert.fail, assert.fail); }); specify("is continued normally after returning a promise in original handler", function() { var a = Promise.defer(); var c = Promise.defer(); a.fulfill(3); setTimeout(function(){ c.fulfill(3); }, 1); return a.promise.then(function(){ return c.promise; }).caught(SyntaxError, function(e){ assert.fail(); }).caught(Promise.TypeError, function(e){ assert.fail(); }).caught(CustomError, function(e){ assert.fail(); }); }); specify("should throw type error for not passing function", function() { try { var a = Promise.reject(new Error("asd")); a.caught(Promise.TypeError, "string"); throw new Error("fail"); } catch (e) { if (e instanceof Promise.TypeError) { return true; } else { throw new Error("fail"); } } }); }); describe("A promise handler with a predicate filter", function() { specify("will catch a thrown thing matching the filter", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new Error("horrible invalid error string"); }).caught(predicateFilter, returnToken) .then(assertToken); }); specify("will NOT catch a thrown thing not matching the filter", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new Error("horrible valid error string"); }).caught(predicateFilter, function(e){ assert.fail(); }).then(assert.fail, function(){}) }); specify("will assert.fail when a predicate is a bad error class", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new Error("horrible custom error"); }).caught(BadError, function(e){ assert.fail(); }).then(assert.fail, returnToken) .then(assertToken); }); specify("will catch a thrown undefiend", function(){ var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw void 0; }).caught(function(e) { return false }, function(e){ assert.fail(); }).caught(predicatesUndefined, returnToken) .then(assertToken); }); specify("will catch a thrown string", function(){ var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw "asd"; }).caught(function(e) { return false }, function(e){ assert.fail(); }).caught(predicatesPrimitiveString, returnToken) .then(assertToken); }); specify("will assert.fail when a predicate throws", function() { var a = Promise.defer(); a.fulfill(3); return a.promise.then(function(){ throw new CustomError("error happens"); }).then(assert.fail, function(e) { return e.f.g; }, function(e){ assert.fail(); }).caught(TypeError, returnToken) .then(assertToken); }); }); describe("object property predicates", function() { specify("matches 1 property loosely", function() { var e = new Error(); e.code = "3"; return Promise.resolve() .then(function() { throw e; }) .caught({code: 3}, function(err) { assert.equal(e, err); }); }); specify("matches 2 properties loosely", function() { var e = new Error(); e.code = "3"; e.code2 = "3"; return Promise.resolve() .then(function() { throw e; }) .caught({code: 3, code2: 3}, function(err) { assert.equal(e, err); }); }); specify("doesn't match inequal properties", function() { var e = new Error(); e.code = "3"; e.code2 = "4"; return Promise.resolve() .then(function() { throw e; }) .caught({code: 3, code2: 3}, function(err) { assert.fail(); }) .caught(function(v) {return v === e}, function() {}); }); specify("doesn't match primitives even if the property matches", function() { var e = "string"; var length = e.length; return Promise.resolve() .then(function() { throw e; }) .caught({length: length}, function(err) { assert.fail(); }) .caught(function(v) {return typeof v === "string"}, function(err) { assert.equal(e, err); }); }); }); ================================================ FILE: test/mocha/collections_thenables.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); function Thenable(value, defer, reject) { this.value = value; this.defer = !!defer; this.reject = !!reject; } Thenable.prototype.then = function Then$then(onFulfilled, onRejected) { var fn = this.reject ? onRejected : onFulfilled; var value = this.value; if (this.defer) { setTimeout(function(){ fn(value); }, 1) } else { fn(value); } }; function testFulfillSync(name, cb, a1, a2, a3) { var thenables = [new Thenable(1), new Thenable(2), new Thenable(3)]; specify("Promise." + name + " thenables that fulfill synchronously", function(){ return cb(Promise[name](thenables, a1, a2, a3)); }); } function testFulfillAsync(name, cb, a1, a2, a3) { var thenables = [new Thenable(1, true), new Thenable(2, true), new Thenable(3, true)]; specify("Promise." + name + " thenables that fulfill asynchronously", function(){ return cb(Promise[name](thenables, a1, a2, a3)); }); } function testRejectSync(name, cb, a1, a2, a3) { var thenables = [new Thenable(1, false, true), new Thenable(2, false, true), new Thenable(3, false, true)]; specify("Promise." + name + " thenables that reject synchronously", function(){ return cb(Promise[name](thenables, a1, a2, a3)); }); } function testRejectAsync(name, cb, a1, a2, a3) { var thenables = [new Thenable(1, true, true), new Thenable(2, true, true), new Thenable(3, true, true)]; specify("Promise." + name + " thenables that reject asynchronously", function(){ return cb(Promise[name](thenables, a1, a2, a3)); }); } describe("Using collection methods with thenables", function() { var name = "race"; testFulfillSync(name, function(promise) { return promise.then(function(v){ assert(v === 1); }); }); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert(v === 1); }); }); testRejectSync(name, function(promise) { return promise.then(assert.fail, function(v){ assert(v === 1); }); }); testRejectAsync(name, function(promise) { return promise.then(assert.fail, function(v){ assert(v === 1); }); }); }); describe("Using collection methods with thenables", function() { var name = "all"; testFulfillSync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, [1,2,3]); }); }); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, [1,2,3]); }); }); testRejectSync(name, function(promise) { return promise.then(assert.fail, function(v){ assert(v === 1); }); }); testRejectAsync(name, function(promise) { return promise.then(assert.fail, function(v){ assert(v === 1); }); }); }); describe("Using collection methods with thenables", function() { var name = "settle"; testFulfillSync(name, function(promise) { return promise.then(function(v) { assert(v[0].value() === 1) assert(v[1].value() === 2) assert(v[2].value() === 3) }); }); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert(v[0].value() === 1) assert(v[1].value() === 2) assert(v[2].value() === 3) }); }); testRejectSync(name, function(promise) { return promise.then(function(v){ assert(v[0].error() === 1) assert(v[1].error() === 2) assert(v[2].error() === 3) }); }); testRejectAsync(name, function(promise) { return promise.then(function(v){ assert(v[0].error() === 1) assert(v[1].error() === 2) assert(v[2].error() === 3) }); }); }); describe("Using collection methods with thenables", function() { var name = "any"; testFulfillSync(name, function(promise) { return promise.then(function(v){ assert(v === 1); }); }); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert(v === 1); }); }); testRejectSync(name, function(promise) { return promise.then(assert.fail, function(v){ assert(v[0] === 1); assert(v[1] === 2); assert(v[2] === 3); }); }); testRejectAsync(name, function(promise) { return promise.then(assert.fail, function(v){ assert(v[0] === 1); assert(v[1] === 2); assert(v[2] === 3); }); }); }); describe("Using collection methods with thenables", function() { var name = "some"; testFulfillSync(name, function(promise) { return promise.then(function(v){ assert(v[0] === 1); }); }, 1); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert(v[0] === 1); }); }, 1); testRejectSync(name, function(promise) { return promise.then(assert.fail, function(v){ assert(v[0] === 1); assert(v[1] === 2); assert(v[2] === 3); }); }, 1); testRejectAsync(name, function(promise) { return promise.then(assert.fail, function(v){ assert(v[0] === 1); assert(v[1] === 2); assert(v[2] === 3); }); }, 1); }); describe("Using collection methods with thenables", function() { var name = "join"; testFulfillSync(name, function(promise) { return promise.then(function(v){ assert(v[0][0].value === 1); assert(v[0][1].value === 2); assert(v[0][2].value === 3); assert(v[1] === 1); assert(v[2] === 2); assert(v[3] === 3); }); }, 1, 2, 3); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert(v[0][0].value === 1); assert(v[0][1].value === 2); assert(v[0][2].value === 3); assert(v[1] === 1); assert(v[2] === 2); assert(v[3] === 3); }); }, 1, 2, 3); testRejectSync(name, function(promise) { return promise.then(function(v){ assert(v[0][0].value === 1); assert(v[0][1].value === 2); assert(v[0][2].value === 3); assert(v[1] === 1); assert(v[2] === 2); assert(v[3] === 3); }); }, 1, 2, 3); testRejectAsync(name, function(promise) { return promise.then(function(v){ assert(v[0][0].value === 1); assert(v[0][1].value === 2); assert(v[0][2].value === 3); assert(v[1] === 1); assert(v[2] === 2); assert(v[3] === 3); }); }, 1, 2, 3); }); function mapper(v) { return { then: function(f) { f(v*2); } }; } function reducer(a, b) { return a + b; } function filterer(v) { return { then: function(f) { f(v > 0); } }; } describe("Using collection methods with thenables", function() { var name = "map"; testFulfillSync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, [2,4,6]); }); }, mapper); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, [2,4,6]); }); }, mapper); testRejectSync(name, function(promise) { return promise.then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }, mapper); testRejectAsync(name, function(promise) { return promise.then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }, mapper); }); describe("Using collection methods with thenables", function() { var name = "reduce"; testFulfillSync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, 6); }); }, reducer); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, 6); }); }, reducer); testRejectSync(name, function(promise) { return promise.then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }, reducer); testRejectAsync(name, function(promise) { return promise.then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }, reducer); }); describe("Using collection methods with thenables", function() { var name = "filter"; testFulfillSync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, [1,2,3]); }); }, filterer); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, [1,2,3]); }); }, filterer); testRejectSync(name, function(promise) { return promise.then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }, filterer); testRejectAsync(name, function(promise) { return promise.then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }, filterer); }); describe("Using collection methods with thenables", function() { var name = "props"; testFulfillSync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, {0: 1, 1: 2, 2: 3}); }); }, filterer); testFulfillAsync(name, function(promise) { return promise.then(function(v){ assert.deepEqual(v, {0: 1, 1: 2, 2: 3}); }); }, filterer); testRejectSync(name, function(promise) { return promise.then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }, filterer); testRejectAsync(name, function(promise) { return promise.then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }, filterer); }); ================================================ FILE: test/mocha/constructor.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); function fulfills(value, test) { specify("immediately-fulfilled", function() { return test(new Promise(function(resolve){ resolve(value); })); }); specify("eventually-fulfilled", function() { return test(new Promise(function(resolve){ setTimeout(function(){ resolve(value); }, 1); })); }); }; function rejects(reason, test) { specify("immediately-rejected", function() { return test(new Promise(function(resolve, reject){ reject(reason); })); }); specify("eventually-rejected", function() { return test(new Promise(function(resolve, reject){ setTimeout(function(){ reject(reason); }, 1); })); }); }; function testFulfilled(value, test) { describe("immediate value", function(){ fulfills(value, test); }); describe("already fulfilled promise for value", function(){ fulfills(Promise.resolve(value), test); }); describe("immediately fulfilled promise for value", function(){ var a = Promise.defer(); fulfills(a.promise, test); a.resolve(value); }); describe("eventually fulfilled promise for value", function(){ var a = Promise.defer(); fulfills(a.promise, test); setTimeout(function(){ a.resolve(value); }, 1) }); describe("synchronous thenable for value", function () { fulfills({ then: function (f) { f(value); } }, test); }); describe("asynchronous thenable for value", function () { fulfills({ then: function (f) { setTimeout(function () { f(value); }, 1); } }, test); }); } function testRejected(reason, test) { describe("immediate reason", function(){ rejects(reason, test); }); } describe("Promise constructor", function() { it("should throw type error when called as function", function() { try { Promise(function(){}); } catch (e) { return; } assert.fail(); }); it("should throw type error when passed non-function", function() { try { new Promise({}); } catch (e) { return; } assert.fail(); }); var defaultThis = (function(){ return this; })(); it("calls the resolver as a function", function(){ new Promise(function() { assert(this === defaultThis); }); }); it("passes arguments even if parameters are not defined", function(){ new Promise(function() { assert(arguments.length === 2 || arguments.length === 3); }); }); it("should reject with any thrown error", function() { var e = new Error(); return new Promise(function(){ throw e; }).then(assert.fail, function(err) { assert(err === e) }); }); it("should call the resolver function synchronously", function() { var e = new Error(); var a = 0; new Promise(function(){ a = 1; }); assert(a === 1); }); describe("resolves the promise with the given object value", function() { var value = {}; testFulfilled(value, function(promise) { return promise.then(function(v){ assert(v === value); }); }); }); describe("resolves the promise with the given primitive value", function() { var value = 3; testFulfilled(value, function(promise) { return promise.then(function(v){ assert(v === value); }); }); }); describe("resolves the promise with the given undefined value", function() { var value = void 0; testFulfilled(value, function(promise) { return promise.then(function(v){ assert(v === value); }); }); }); describe("rejects the promise with the given object reason", function() { var reason = {}; testRejected(reason, function(promise) { return promise.then(assert.fail, function(v){ assert(v === reason); }); }); }); describe("rejects the promise with the given primitive reason", function() { var reason = 3; testRejected(reason, function(promise) { return promise.then(assert.fail, function(v){ assert(v === reason); }); }); }); describe("rejects the promise with the given undefined reason", function() { var reason = void 0; testRejected(reason, function(promise) { return promise.then(assert.fail, function(v){ assert(v === reason); }); }); }); }); ================================================ FILE: test/mocha/cycles.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var helpers = require("./helpers/testThreeCases.js"); var TypeError = Promise.TypeError; describe("Cyclical promises should throw TypeError when", function(){ describe("returning from fulfill", function() { helpers.testFulfilled(3, function(promise) { var self = promise.then(function() { return self; }); return self.then(assert.fail).caught(TypeError, testUtils.noop); }); }); describe("returning from reject", function() { helpers.testRejected(3, function(promise) { var self = promise.then(assert.fail, function() { return self; }); return self.then(assert.fail).caught(TypeError, testUtils.noop); }); }); describe("fulfill with itself when using a ", function() { specify("deferred", function() { var d = Promise.defer(); d.fulfill(d.promise); return d.promise.then(assert.fail).caught(TypeError, testUtils.noop); }); specify("constructor", function() { var resolve; var p = new Promise(function(r) { resolve = r; }); resolve(p); return p.then(assert.fail).caught(TypeError, testUtils.noop); }); }); describe("reject with itself when using a ", function() { specify("deferred", function() { var d = Promise.defer(); d.reject(d.promise); return d.promise.then(assert.fail).caught(function(v) { assert.equal(d.promise, v); }); }); specify("constructor", function() { var reject; var p = new Promise(function(f, r) { reject = r; }); reject(p); return p.then(assert.fail).caught(function(v) { assert.equal(p, v); }); }); }); }); ================================================ FILE: test/mocha/direct_resolving.js ================================================ "use strict"; "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var helpers = require("./helpers/testThreeCases.js"); var TypeError = Promise.TypeError; function passthru(fn) { return function() { fn(); }; } function wrap(fn, val) { var args = [].slice.call(arguments, 1); return function() { return fn.apply(this, args); } } function returnValue(value) { helpers.testFulfilled(void 0, function(promise) { return promise.thenReturn(value).then(function(v){ assert(v === value); }); }); } function throwValue(value) { helpers.testFulfilled(void 0, function(promise) { return promise.thenThrow(value).then(assert.fail, function(v) { assert(v === value); }); }); } function returnThenable(thenable, expected) { helpers.testFulfilled(void 0, function(promise) { return promise.thenReturn(thenable).then(function(v){ assert(v === expected); }); }); } function returnThenableReject(thenable, expected) { helpers.testFulfilled(void 0, function(promise) { return promise.thenReturn(thenable).then(assert.fail, function(v){ assert(v === expected); }); }); } describe("thenReturn", function () { describe("primitives", function() { describe("null", wrap(returnValue, null)); describe("undefined", wrap(returnValue, void 0)); describe("string", wrap(returnValue, "asd")); describe("number", wrap(returnValue, 3)); describe("boolean", wrap(returnValue, true)); }); describe("objects", function() { describe("plain", wrap(returnValue, {})); describe("function", wrap(returnValue, function(){})); describe("built-in function", wrap(returnValue, Array)); describe("built-in object", wrap(returnValue, Math)); }); describe("thenables", function() { describe("which fulfill", function() { describe("immediately", wrap(returnThenable, { then: function(f) { f(10); } }, 10)); describe("eventually", wrap(returnThenable, { then: function(f) { setTimeout(function() { f(10); }, 1); } }, 10)); }); describe("which reject", function(){ describe("immediately", wrap(returnThenableReject, { then: function(f, r) { r(10); } }, 10)); describe("eventually", wrap(returnThenableReject, { then: function(f, r) { setTimeout(function() { r(10); }, 1); } }, 10)); }); }); describe("promises", function() { describe("which fulfill", function() { var d1 = Promise.defer(); var d2 = Promise.defer(); describe("already", wrap(returnThenable, Promise.resolve(10), 10)); describe("immediately", wrap(returnThenable, d1.promise, 10)); describe("eventually", wrap(returnThenable, d2.promise, 10)); d1.fulfill(10); setTimeout(function(){ d2.fulfill(10); }, 1); }); describe("which reject", function() { var d1 = Promise.defer(); var d2 = Promise.defer(); var alreadyRejected = Promise.reject(10); alreadyRejected.then(assert.fail, function(){}); describe("already", wrap(returnThenableReject, alreadyRejected, 10)); describe("immediately", wrap(returnThenableReject, d1.promise, 10)); describe("eventually", wrap(returnThenableReject, d2.promise, 10)); d1.reject(10); setTimeout(function(){ d2.reject(10); }, 1); d1.promise.caught(function(){}); d2.promise.caught(function(){}); }); }); describe("doesn't swallow errors", function() { var e = {}; helpers.testRejected(e, function(promise){ return promise.thenReturn(3).then(assert.fail, function(err) { assert(err = e); }); }); }); }); describe("thenThrow", function () { describe("primitives", function() { describe("null", wrap(throwValue, null)); describe("undefined", wrap(throwValue, void 0)); describe("string", wrap(throwValue, "asd")); describe("number", wrap(throwValue, 3)); describe("boolean", wrap(throwValue, true)); }); describe("objects", function() { describe("plain", wrap(throwValue, {})); describe("function", wrap(throwValue, function(){})); describe("built-in function", wrap(throwValue, Array)); describe("built-in object", wrap(throwValue, Math)); }); describe("doesn't swallow errors", function() { var e = {}; helpers.testRejected(e, function(promise){ return promise.thenThrow(3).then(assert.fail, function(err) { assert(err = e); }); }); }); }); describe("catchReturn", function () { specify("catches and returns", function() { return Promise.reject(3).catchReturn(1).then(function(val) { assert.strictEqual(1, val); }); }); specify("doesn't catch succesful promise", function() { return Promise.resolve(3).catchReturn(1).then(function(val) { assert.strictEqual(3, val); }); }); specify("supports 1 error type", function() { var e = new Error(); e.prop = 3; var predicate = function(e) {return e.prop === 3}; return Promise.reject(e) .catchReturn(TypeError, 1) .catchReturn(predicate, 2) .then(function(val) { assert.strictEqual(2, val); }); }); }); describe("catchThrow", function () { specify("catches and throws", function() { return Promise.reject(3).catchThrow(1).then(assert.fail, function(val) { assert.strictEqual(1, val); }); }); specify("doesn't catch succesful promise", function() { return Promise.resolve(3).catchThrow(1).then(function(val) { assert.strictEqual(3, val); }); }); specify("supports 1 error type", function() { var e = new Error(); e.prop = 3; var predicate = function(e) {return e.prop === 3}; return Promise.reject(e) .catchThrow(TypeError, 1) .catchThrow(predicate, 2) .then(assert.fail, function(val) { assert.strictEqual(2, val); }); }); }); describe("gh-627", function() { it("can return undefined", function() { return Promise.bind(42) .thenReturn(undefined) .then(function (value) { assert.strictEqual(value, undefined); }); }); it("can throw undefined", function() { return Promise.bind(42) .thenThrow(undefined) .then(assert.fail, function (reason) { assert.strictEqual(reason, undefined); }); }); it("can catch return undefined", function() { return Promise.bind(42).thenThrow(new Error()) .catchReturn() .then(function (value) { assert.strictEqual(value, undefined); }); }); it("can catch throw undefined", function() { return Promise.bind(42).thenThrow(new Error()) .catchThrow() .then(assert.fail, function (reason) { assert.strictEqual(reason, undefined); }); }); }); ================================================ FILE: test/mocha/domain.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); if (testUtils.isRecentNode) { describe("domain", function() { afterEach(function() { Promise.onPossiblyUnhandledRejection(null); }); specify("gh-148", function() { var called = false; var e = new Error("the error"); Promise.resolve(23).then(function(){called = true}); return testUtils.awaitDomainException(function(E) { assert.equal(e, E); assert(called); }, function() { Promise.onPossiblyUnhandledRejection(function(error) { throw error; }); var P = new Promise(function(_, reject){reject(e);}); }); }); specify("gh-521-promisified", function() { return new Promise(function(resolve, reject) { var domain = require('domain').create(); var data = {}; function callsBack(cb) { setTimeout(function() { cb(null, 1); }, 1); } var promisified = Promise.promisify(callsBack); domain.on('error', reject); domain.run(function() { process.domain.data = data; resolve(promisified().then(function() { assert.strictEqual(process.domain.data, data); assert.strictEqual(process.domain, domain); })); }); }); }); specify("gh-521-constructed", function() { return new Promise(function(resolve, reject) { var domain = require('domain').create(); var data = {asd: 3}; domain.on('error', reject); domain.run(function() { var promise = new Promise(function(resolve) { setTimeout(resolve, 1); }); process.domain.data = data; resolve(promise.then(function() { assert.strictEqual(process.domain.data, data); assert.strictEqual(process.domain, domain); })); }); }); }); }); describe("domain preservation" , function() { var Domain = require("domain"); function createGroupDone(limit, next) { return function done(err) { if (err) { return next(err); } if (--limit <= 0) { next(); } }; } before(function () { var current; while((current = process.domain)) { current.exit(); } }); afterEach(function () { var current; while((current = process.domain)) { current.exit(); } }); it("should preserve empty domain and this function", function(done) { var deferred = new Promise.defer(); var p = deferred.promise; p.then(function shouldBeEmpty() { assert.equal(false, !!process.domain); }).bind({ ref: 'foo' }).then(function shouldKeepThisAndEmptyDomain() { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); }).caught(done); deferred.resolve("ok"); }); it("should preserve empty domain, nodeify", function(done) { done = createGroupDone(3, done); var deferred = new Promise.defer(); var p = deferred.promise; p.then(function shouldBeEmpty() { assert.equal(false, !!process.domain); done(); }).bind({ ref: 'foo' }).then(function shouldKeepThisAndEmptyDomain() { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); }).nodeify(function shouldKeepThisAndEmptyDomain() { try { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); } catch (err) { done(err); } }).caught(done); deferred.resolve("ok"); }); it("should preserve corresponding state of domain", function(done) { done = createGroupDone(6, done); var deferred = new Promise.defer(); var p = deferred.promise; p.then(function shouldBeEmpty() { assert.equal(false, !!process.domain); done(); }).bind({ ref: 'foo' }).then(function shouldKeepThisAndEmptyDomain() { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); }).nodeify(function shouldKeepThisAndEmptyDomain() { try { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); } catch (err) { done(err); } }).caught(done); var domain = Domain.create(); domain.run(function () { p.then(function shouldNoBeEmpty() { assert.equal(domain, process.domain); done(); }).bind({ ref: 'bar' }).then(function shouldKeepThisAndDomain() { assert.equal(domain, process.domain); assert.equal('bar', this.ref); done(); }).caught(done).nodeify(function shouldKeepThisAndDomain() { try { assert.equal(domain, process.domain); assert.equal('bar', this.ref); done(); } catch (err) { done(err); } }); }); deferred.resolve("ok"); }); it('should preserve corresponding state of domain, complex', function(done) { done = createGroupDone(9, done); var deferred = new Promise.defer(); var p = deferred.promise; p.then(function shouldBeEmpty() { assert.equal(false, !!process.domain); done(); }).bind({ ref: 'foo' }).then(function shouldKeepThisAndEmptyDomain() { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); }).caught(done).nodeify(function shouldKeepThisAndEmptyDomain() { try { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); } catch (err) { done(err); } }, done); var domain1 = Domain.create(); domain1.run(function () { p.then(function shouldNoBeEmpty() { assert.equal(domain1, process.domain); done(); }).bind({ ref: 'bar' }).then(function shouldKeepThisAndDomain() { assert.equal(domain1, process.domain); assert.equal('bar', this.ref); done(); }).caught(done).nodeify(function shouldKeepThisAndDomain() { try { assert.equal(domain1, process.domain); assert.equal('bar', this.ref); done(); } catch (err) { done(err); } }, done); }); var domain2 = Domain.create(); domain2.run(function () { p.then(function shouldNoBeEmpty() { assert.equal(domain2, process.domain); done(); }).bind({ ref: 'qaz' }).then(function shouldKeepThisAndDomain() { assert.equal(domain2, process.domain); assert.equal('qaz', this.ref); done(); }).caught(done).nodeify(function shouldKeepThisAndDomain() { try { assert.equal(domain2, process.domain); assert.equal('qaz', this.ref); done(); } catch (err) { done(err); } }); }); deferred.resolve("ok"); }); it('should preserve corresponding state of domain in reject', function(done) { done = createGroupDone(4, done); var deferred = new Promise.defer(); var p = deferred.promise; p.bind({ ref: 'foo' }).caught(function shouldKeepThisAndEmptyDomain() { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); }).caught(done).nodeify(function shouldKeepThisAndEmptyDomain() { try { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); } catch (err) { done(err); } }); var domain = Domain.create(); domain.run(function () { p.bind({ ref: 'bar' }).caught(function shouldNoBeEmpty() { assert.equal(true, !!process.domain); assert.equal('bar', this.ref); done(); }).caught(done).nodeify(function shouldKeepThisAndDomain(err) { try { assert.equal(true, !!process.domain); assert.equal('bar', this.ref); done(); } catch (err) { done(err); } }).caught(done); }); deferred.reject('bad'); }); it('should preserve corresponding state of domain in reject, complex', function(done) { done = createGroupDone(6, done); var deferred = new Promise.defer(); var p = deferred.promise; p.bind({ ref: 'foo' }).caught(function shouldBeEmpty() { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); }).caught(done).nodeify(function shouldKeepThisAndEmptyDomain() { try { assert.equal(false, !!process.domain); assert.equal('foo', this.ref); done(); } catch (err) { done(err); } }); var domain1 = Domain.create(); domain1.run(function () { p.bind({ ref: 'bar' }).caught(function shouldNoBeEmpty() { assert.equal(domain1, process.domain); assert.equal('bar', this.ref); done(); }).caught(done).nodeify(function shouldKeepThisAndDomain() { try { assert.equal(domain1, process.domain); assert.equal('bar', this.ref); done(); } catch (err) { done(err); } }); }); var domain2 = Domain.create(); domain2.run(function () { p.bind({ ref: 'qaz' }).caught(function shouldNoBeEmpty() { assert.equal(domain2, process.domain); assert.equal('qaz', this.ref); done(); }).caught(done).nodeify(function shouldKeepThisAndDomain() { try { assert.equal(domain2, process.domain); assert.equal('qaz', this.ref); done(); } catch (err) { done(err); } }); }); deferred.reject('bad'); }); it('should preserve domain when using .join', function() { var domain = Domain.create(); var d1 = new Promise(function(resolve, reject) { Domain.create().run(function() { setTimeout(resolve, 1); }); }); var d2 = new Promise(function(resolve, reject) { Domain.create().run(function() { setTimeout(resolve, 1); }); }); return new Promise(function(resolve, reject) { domain.on("error", reject); domain.run(function() { resolve(Promise.join(d1, d2, function() { assert.strictEqual(domain, process.domain); })); }); }); }); it('should preserve domain when using .using', function() { var domain = Domain.create(); var d1 = new Promise(function(resolve, reject) { Domain.create().run(function() { setTimeout(resolve, 1); }); }); var d2 = new Promise(function(resolve, reject) { Domain.create().run(function() { setTimeout(resolve, 1); }); }); return new Promise(function(resolve, reject) { domain.on("error", reject); domain.run(function() { resolve(Promise.using(d1, d2, function() { assert.strictEqual(domain, process.domain); })); }); }); }); it('should preserve domain when using .map', function() { var domain = Domain.create(); var d1 = new Promise(function(resolve, reject) { Domain.create().run(function() { setTimeout(resolve, 1); }); }); return new Promise(function(resolve, reject) { domain.on("error", reject); domain.run(function() { resolve(Promise.map([d1, null, Promise.resolve(1), Promise.delay(1)], function() { return process.domain; }).then(function(domains) { assert.deepEqual([domain, domain, domain, domain], domains); assert.equal(process.domain, domain); })); }); }); }); it('should preserve domain when using .filter', function() { var domain = Domain.create(); var d1 = new Promise(function(resolve, reject) { Domain.create().run(function() { setTimeout(resolve, 1); }); }); return new Promise(function(resolve, reject) { domain.on("error", reject); domain.run(function() { resolve(Promise.filter([d1, null, Promise.resolve(1), Promise.delay(1)], function() { assert.equal(process.domain, domain); })); }); }); }); it('should preserve domain when using .reduce', function() { var domain = Domain.create(); var d1 = new Promise(function(resolve, reject) { Domain.create().run(function() { setTimeout(resolve, 1); }); }); return new Promise(function(resolve, reject) { domain.on("error", reject); domain.run(function() { resolve(Promise.reduce([d1, null, Promise.resolve(1), Promise.delay(1)], function() { assert.equal(process.domain, domain); })); }); }); }); it('should preserve domain when using .each', function() { var domain = Domain.create(); var d1 = new Promise(function(resolve, reject) { Domain.create().run(function() { setTimeout(resolve, 1); }); }); return new Promise(function(resolve, reject) { domain.on("error", reject); domain.run(function() { resolve(Promise.each([d1, null, Promise.resolve(1), Promise.delay(1)], function() { assert.equal(process.domain, domain); })); }); }); }); it("should not crash with already rejected promise", function() { return new Promise(function(resolve) { Domain.create().run(function() { Promise.resolve(1).timeout(200).then(function() { resolve(); }) }); }); }) }); } ================================================ FILE: test/mocha/done.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var isNodeJS = testUtils.isNodeJS; /*! * Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ describe("done", function () { var errCount = 0; var safeError = new Error("safe_error"); describe("when the promise is fulfilled", function () { describe("and the callback does not throw", function () { it("should call the callback and return nothing", function () { var called = false; var promise = Promise.resolve(); var returnValue = promise.done(function () { called = true; }); return promise.lastly(function () { assert.equal(called,true); assert.equal(returnValue,undefined); }); }); }); if (isNodeJS) { describe("and the callback throws", function () { it("should rethrow that error in the next turn and return nothing", function() { var turn = 0; process.nextTick(function () { ++turn; }); var returnValue = Promise.resolve().done( function () { throw safeError; } ); return testUtils.awaitProcessExit(function(e) { assert.equal(turn,1); assert.equal(returnValue,undefined); }); }); }); } }); describe("when the promise is rejected", function () { describe("and the errback handles it", function () { it("should call the errback and return nothing", function () { var called = false; var promise = Promise.reject("unsafe_error"); var returnValue = promise.done( function () { }, function () { called = true; } ); return promise.caught(function(){}).lastly(function () { assert.equal(called,true); assert.equal(returnValue,undefined); }); }); }); if (isNodeJS) { describe("and the errback throws", function () { it("should rethrow that error in the next turn and return nothing", function() { var turn = 0; process.nextTick(function () { ++turn; }); var returnValue = Promise.reject("unsafe_error").done( null, function () { throw safeError; } ); return testUtils.awaitProcessExit(function(e) { assert.equal(turn,1); assert.equal(returnValue,undefined); }); }); }); describe("and there is no errback", function () { it("should throw the original error in the next turn", function() { var turn = 0; process.nextTick(function () { ++turn; }); var returnValue = Promise.reject(safeError).done(); return testUtils.awaitProcessExit(function(e) { assert.equal(turn,1); assert.equal(returnValue,undefined); }); }); }); } }); }); ================================================ FILE: test/mocha/each.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); function promised(val) { return new Promise(function(f) { setTimeout(function() { f(val); }, 1); }); } function thenabled(val, arr) { return { then: function(f){ setTimeout(function() { if (arr) arr.push(val); f(val); }, 1); } }; } describe("Promise.each", function() { it("should return the array's values mapped", function() { var a = [promised(1), promised(2), promised(3)]; var b = []; return Promise.resolve(a).mapSeries(function(val) { b.push(3-val); return val + 2; }).then(function(ret) { assert.deepEqual(ret, [3,4,5]); assert.deepEqual(b, [2, 1, 0]); }); }); it("takes value, index and length", function() { var a = [promised(1), promised(2), promised(3)]; var b = []; return Promise.resolve(a).each(function(value, index, length) { b.push(value, index, length); }).then(function(ret) { assert.deepEqual(b, [1, 0, 3, 2, 1, 3, 3, 2, 3]); }); }); it("waits for returned promise before proceeding next", function() { var a = [promised(1), promised(2), promised(3)]; var b = []; return Promise.resolve(a).each(function(value) { b.push(value); return Promise.delay(1).then(function(){ b.push(value*2); }); }).then(function(ret) { assert.deepEqual(b, [1,2,2,4,3,6]); }); }); it("waits for returned thenable before proceeding next", function() { var b = [1, 2, 3]; var a = [thenabled(1), thenabled(2), thenabled(3)]; return Promise.resolve(a).each(function(val) { b.push(val * 50); return thenabled(val * 500, b); }).then(function(ret) { assert.deepEqual(b, [1, 2, 3, 50, 500, 100, 1000, 150, 1500]); }); }); it("doesnt iterate with an empty array", function() { return Promise.each([], function(val) { throw new Error(); }).then(function(ret) { assert.deepEqual(ret, []); }); }); it("iterates with an array of single item", function() { var b = []; return Promise.each([promised(1)], function(val) { b.push(val); return thenabled(val*2, b); }).then(function(ret) { assert.deepEqual(b, [1,2]); }); }); }); describe("Promise.prototype.each", function() { it("should return the array's values", function() { var a = [promised(1), promised(2), promised(3)]; var b = []; return Promise.resolve(a).each(function(val) { b.push(3-val); return val; }).then(function(ret) { assert.deepEqual(ret, [1,2,3]); assert.deepEqual(b, [2, 1, 0]); }); }); it("takes value, index and length", function() { var a = [promised(1), promised(2), promised(3)]; var b = []; return Promise.resolve(a).each(function(value, index, length) { b.push(value, index, length); }).then(function(ret) { assert.deepEqual(b, [1, 0, 3, 2, 1, 3, 3, 2, 3]); }); }); it("waits for returned promise before proceeding next", function() { var a = [promised(1), promised(2), promised(3)]; var b = []; return Promise.resolve(a).each(function(value) { b.push(value); return Promise.delay(1).then(function(){ b.push(value*2); }); }).then(function(ret) { assert.deepEqual(b, [1,2,2,4,3,6]); }); }); it("waits for returned thenable before proceeding next", function() { var b = [1, 2, 3]; var a = [thenabled(1), thenabled(2), thenabled(3)]; return Promise.resolve(a).each(function(val) { b.push(val * 50); return thenabled(val * 500, b); }).then(function(ret) { assert.deepEqual(b, [1, 2, 3, 50, 500, 100, 1000, 150, 1500]); }); }); it("doesnt iterate with an empty array", function() { return Promise.resolve([]).each(function(val) { throw new Error(); }).then(function(ret) { assert.deepEqual(ret, []); }); }); it("iterates with an array of single item", function() { var b = []; return Promise.resolve([promised(1)]).each(function(val) { b.push(val); return thenabled(val*2, b); }).then(function(ret) { assert.deepEqual(b, [1,2]); }); }); }); describe("mapSeries and each", function() { it("is mixed", function() { return Promise.mapSeries([1, 2, 3], function(value) { return value * 2; }).then(function(result) { assert.deepEqual(result, [2, 4, 6]); }).then(function() { return Promise.each([1, 2, 3], function(value) { return value * 2; }).then(function(result) { assert.deepEqual(result, [1, 2, 3]); }); }).thenReturn([1, 2, 3]).mapSeries(function(value) { return value * 2; }).then(function(result) { assert.deepEqual(result, [2, 4, 6]); }).thenReturn([1, 2, 3]).each(function(value) { return value * 2; }).then(function(result) { assert.deepEqual(result, [1, 2, 3]); }); }) }); ================================================ FILE: test/mocha/error.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.prototype.error", function(){ describe("catches stuff originating from explicit rejections", function() { specify("using callback", function() { var e = new Promise.TypeError("sup"); function callsback(a, b, c, fn) { fn(e); } callsback = Promise.promisify(callsback); return callsback(1, 2, 3).error(function(err) { assert(err === e); }); }); }); describe("does not catch stuff originating from thrown errors", function() { specify("using constructor", function() { var e = new Error("sup"); return new Promise(function(resolve, reject) { throw e; }).error(function(err) { assert.fail(); }).then(assert.fail, function(err){ assert(err === e); }); }); specify("using thenable", function() { var e = new Error("sup"); var thenable = { then: function(resolve, reject){ reject(e); } }; return Promise.cast(thenable).error(function(err) { console.error(err); assert.fail(); }).then(assert.fail, function(err) { assert(err === e); }); }); specify("using callback", function() { var e = new Error("sup"); function callsback(a, b, c, fn) { throw e; } callsback = Promise.promisify(callsback); return callsback(1, 2, 3).error(function(err) { assert.fail(); }).then(assert.fail, function(err){ assert(err === e); }); }); }); }) if (testUtils.ecmaScript5) { describe("Weird errors", function() { specify("unwritable stack", function() { var e = new Error(); var stack = e.stack; Object.defineProperty(e, "stack", { configurable: true, get: function() {return stack;}, set: function() {throw new Error("cannot set");} }); return new Promise(function(_, reject) { setTimeout(function() { reject(e); }, 1); }).caught(function(err) { assert.equal(e, err); }); }); }); } describe("Error constructors", function() { describe("OperationalError", function() { it("should work without new", function() { var a = Promise.OperationalError("msg"); assert.strictEqual(a.message, "msg"); assert(a instanceof Error); }); it("should work with new", function() { var a = new Promise.OperationalError("msg"); assert.strictEqual(a.message, "msg"); assert(a instanceof Error); }); it("should retain custom properties", function() { var message; var name; function f(cb) { var err = new Error("custom message"); message = err.message; name = err.name; err.code = "ENOENT"; err.path = "C:\\"; cb(err); } return Promise.promisify(f)().error(function(e) { assert.strictEqual(e.message, message); assert.strictEqual(e.name, name); assert(e instanceof Promise.OperationalError); assert.strictEqual(e.code, "ENOENT"); assert.strictEqual(e.path, "C:\\"); }); }); }); describe("CancellationError", function() { it("should work without new", function() { var a = Promise.CancellationError("msg"); assert.strictEqual(a.message, "msg"); assert(a instanceof Error); }); it("should work with new", function() { var a = new Promise.CancellationError("msg"); assert.strictEqual(a.message, "msg"); assert(a instanceof Error); }); }); describe("TimeoutError", function() { it("should work without new", function() { var a = Promise.TimeoutError("msg"); assert.strictEqual(a.message, "msg"); assert(a instanceof Error); }); it("should work with new", function() { var a = new Promise.TimeoutError("msg"); assert.strictEqual(a.message, "msg"); assert(a instanceof Error); }); }); describe("AggregateError", function() { it("should work without new", function() { var a = Promise.AggregateError("msg"); assert.strictEqual(a.message, "msg"); assert(a instanceof Error); }); it("should work with new", function() { var a = new Promise.AggregateError("msg"); assert.strictEqual(a.message, "msg"); assert(a instanceof Error); }); if (testUtils.isNodeJS) { it("should stringify without circular errors", function() { var a = Promise.AggregateError(); a.push(new Error("1")); a.push(new Error("2")); a.push(new Error("3")); a = a.toString(); assert(a.indexOf("Error: 1") >= 0); assert(a.indexOf("Error: 2") >= 0); assert(a.indexOf("Error: 3") >= 0); }); it("should stringify with circular errors", function() { var a = Promise.AggregateError(); a.push(new Error("1")); a.push(a); a.push(new Error("3")); a = a.toString(); assert(a.indexOf("Error: 1") >= 0); assert(a.indexOf("[Circular AggregateError]") >= 0); assert(a.indexOf("Error: 3") >= 0); }); } }); }); ================================================ FILE: test/mocha/filter.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise filter", function() { function ThrownError() {} var arr = [1,2,3]; function assertArr(arr) { assert(arr.length === 2); assert(arr[0] === 1); assert(arr[1] === 3); } function assertErr(e) { assert(e instanceof ThrownError); } function assertFail() { assert.fail(); } describe("should accept eventual booleans", function() { specify("immediately fulfilled", function() { return Promise.filter(arr, function(v) { return new Promise(function(r){ r(v !== 2); }); }).then(assertArr); }); specify("already fulfilled", function() { return Promise.filter(arr, function(v) { return Promise.resolve(v !== 2); }).then(assertArr); }); specify("eventually fulfilled", function() { return Promise.filter(arr, function(v) { return new Promise(function(r){ setTimeout(function(){ r(v !== 2); }, 1); }); }).then(assertArr); }); specify("immediately rejected", function() { return Promise.filter(arr, function(v) { return new Promise(function(v, r){ r(new ThrownError()); }); }).then(assertFail, assertErr); }); specify("already rejected", function() { return Promise.filter(arr, function(v) { return Promise.reject(new ThrownError()); }).then(assertFail, assertErr); }); specify("eventually rejected", function() { return Promise.filter(arr, function(v) { return new Promise(function(v, r){ setTimeout(function(){ r(new ThrownError()); }, 1); }); }).then(assertFail, assertErr); }); specify("immediately fulfilled thenable", function() { return Promise.filter(arr, function(v) { return { then: function(f, r) { f(v !== 2); } }; }).then(assertArr); }); specify("eventually fulfilled thenable", function() { return Promise.filter(arr, function(v) { return { then: function(f, r) { setTimeout(function(){ f(v !== 2); }, 1); } }; }).then(assertArr); }); specify("immediately rejected thenable", function() { return Promise.filter(arr, function(v) { return { then: function(f, r) { r(new ThrownError()); } }; }).then(assertFail, assertErr); }); specify("eventually rejected thenable", function() { return Promise.filter(arr, function(v) { return { then: function(f, r) { setTimeout(function(){ r(new ThrownError()); }, 1); } }; }).then(assertFail, assertErr); }); }); }); ================================================ FILE: test/mocha/finally.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); /*! * Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ describe("finally", function () { var exception1 = new Error("boo!"); var exception2 = new Promise.TypeError("evil!"); describe("when nothing is passed", function() { it("should do nothing", function() { return Promise.resolve("foo") .lastly() .lastly() .lastly() .lastly() .then(function(val){ assert(val === "foo"); }) }); }); describe("when the promise is fulfilled", function () { it("should call the callback", function() { var called = false; return Promise.resolve("foo") .lastly(function () { called = true; }) .then(function () { assert.equal(called,true); }); }); it("should fulfill with the original value", function() { return Promise.resolve("foo") .lastly(function () { return "bar"; }) .then(function (result) { assert.equal(result,"foo"); }); }); describe("when the callback returns a promise", function () { describe("that is fulfilled", function () { it("should fulfill with the original reason after that promise resolves", function() { var promise = Promise.delay(1); return Promise.resolve("foo") .lastly(function () { return promise; }) .then(function (result) { assert.equal(promise.isPending(),false); assert.equal(result,"foo"); }); }); }); describe("that is rejected", function () { it("should reject with this new rejection reason", function() { return Promise.resolve("foo") .lastly(function () { return Promise.reject(exception1); }) .then(function () { assert.equal(false,true); }, function (exception) { assert.equal(exception,exception1); }); }); }); }); describe("when the callback throws an exception", function () { it("should reject with this new exception", function() { return Promise.resolve("foo") .lastly(function () { throw exception1; }) .then(function () { assert.equal(false,true); }, function (exception) { assert.equal(exception,exception1); }); }); }); }); describe("when the promise is rejected", function () { it("should call the callback", function() { var called = false; return Promise.reject(exception1) .lastly(function () { called = true; }) .then(function () { assert.fail(); }, function () { assert.equal(called,true); }); }); it("should reject with the original reason", function() { return Promise.reject(exception1) .lastly(function () { return "bar"; }) .then(function () { assert.equal(false,true); }, function (exception) { assert.equal(exception,exception1); }); }); describe("when the callback returns a promise", function () { describe("that is fulfilled", function () { it("should reject with the original reason after that promise resolves", function() { var promise = Promise.delay(1); return Promise.reject(exception1) .lastly(function () { return promise; }) .then(function () { assert.equal(false,true); }, function (exception) { assert.equal(exception,exception1); assert.equal(promise.isPending(),false); }); }); }); describe("that is rejected", function () { it("should reject with the new reason", function() { return Promise.reject(exception1) .lastly(function () { return Promise.reject(exception2); }) .then(function () { assert.equal(false,true); }, function (exception) { assert.equal(exception,exception2); }); }); }); }); describe("when the callback throws an exception", function () { it("should reject with this new exception", function() { return Promise.reject(exception1) .lastly(function () { throw exception2; }) .then(function () { assert.equal(false,true); }, function (exception) { assert.equal(exception,exception2); }); }); }); }); describe("when the callback returns a thenable", function () { describe("that will fulfill", function () { it("should reject with the original reason after that", function() { var promise = { then: function(fn) { setTimeout(function(){ fn(15); }, 1); } }; return Promise.reject(exception1) .lastly(function () { return promise; }) .then(function () { assert.equal(false,true); }, function (exception) { assert.equal(exception,exception1); }); }); }); describe("that is rejected", function () { it("should reject with the new reason", function() { var promise = { then: function(f, fn) { setTimeout(function(){ fn(exception2); }, 1); } }; return Promise.reject(exception1) .lastly(function () { return promise; }) .then(function () { assert.equal(false,true); }, function (exception) { assert.equal(exception,exception2); }); }); it("should reject with the new primitive reason", function() { var primitive = 3; var promise = { then: function(f, fn) { setTimeout(function(){ fn(primitive); }, 1); } }; return Promise.reject(exception1) .lastly(function () { return promise; }) .then(function () { assert.equal(false,true); }, function (exception) { assert.strictEqual(exception, primitive); }); }); }); }); }); ================================================ FILE: test/mocha/following.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Using deferreds", function() { describe("a promise A that is following a promise B", function() { specify("Must not react to fulfill/reject/ that don't come from promise B", function() { var deferred = Promise.defer(); var promiseA = deferred.promise; var promiseB = Promise.defer().promise; var called = 0; function incrementCalled() { called++; } promiseA.then( incrementCalled, incrementCalled ); deferred.fulfill(promiseB); deferred.fulfill(1); deferred.reject(1); return Promise.delay(1).then(function() { assert.equal(0, called); assert.equal(promiseA.isPending(), true); assert.equal(promiseB.isPending(), true); }); }); specify("Must not start following another promise C", function() { var deferred = Promise.defer(); var promiseA = deferred.promise; var promiseB = Promise.defer().promise; var deferredC = Promise.defer(); var promiseC = deferredC.promise; var called = 0; function incrementCalled() { called++; } promiseA.then( incrementCalled, incrementCalled ); deferred.fulfill(promiseB); deferred.fulfill(promiseC); deferredC.fulfill(1); deferredC.reject(1); return promiseC.then(function() { assert.equal(called, 0); assert.equal(promiseA.isPending(), true); assert.equal(promiseB.isPending(), true); assert.equal(promiseC.isPending(), false); }); }); specify("Must react to fulfill/reject that come from promise B", function() { var deferred = Promise.defer(); var promiseA = deferred.promise; var deferredFollowee = Promise.defer(); var promiseB = deferredFollowee.promise; var called = 0; function incrementCalled() { called++; } var c = 0; var ret = promiseA.then(function(v){ c++; assert.equal(c, 1); assert.equal(called, 0); }, incrementCalled); deferred.fulfill(promiseB); deferredFollowee.fulfill(1); deferredFollowee.reject(1); return ret; }); }); }); describe("Using static immediate methods", function() { describe("a promise A that is following a promise B", function() { specify("Should be instantly fulfilled with Bs fulfillment value if B was fulfilled", function() { var val = {}; var B = Promise.resolve(val); var A = Promise.resolve(B); assert.equal(A.value(), val); assert.equal(A.value(), B.value()); }); specify("Should be instantly fulfilled with Bs parent fulfillment value if B was fulfilled with a parent", function() { var val = {}; var parent = Promise.resolve(val); var B = Promise.resolve(parent); var A = Promise.resolve(B); assert.equal(A.value(), val); assert.equal(A.value(), B.value()); assert.equal(A.value(), parent.value()); }); }); describe("Rejecting a promise A with promise B", function(){ specify("Should reject promise A with B as reason ", function() { var val = {}; var B = Promise.resolve(val); var A = Promise.reject(B); assert.equal(A.reason(), B); A.then(assert.fail, function(){}); }); }); }); describe("Using constructor", function() { describe("a promise A that is following a promise B", function() { specify("Must not react to fulfill/reject that don't come from promise B", function() { var resolveA; var rejectA; var promiseA = new Promise(function() { resolveA = arguments[0]; rejectA = arguments[1]; }); var promiseB = new Promise(function(){}); var called = 0; function incrementCalled() { called++; } promiseA.then( incrementCalled, incrementCalled ); resolveA(promiseB); resolveA(1); rejectA(1); return Promise.delay(1).then(function() { assert.equal(0, called); assert.equal(promiseA.isPending(), true); assert.equal(promiseB.isPending(), true); }); }); specify("Must not start following another promise C", function() { var resolveA; var promiseA = new Promise(function(){ resolveA = arguments[0]; }); var promiseB = new Promise(function(){}); var resolveC, rejectC; var promiseC = new Promise(function(){ resolveC = arguments[0]; rejectC = arguments[1]; }); var called = 0; function incrementCalled() { called++; } promiseA.then( incrementCalled, incrementCalled, incrementCalled ); resolveA(promiseB); resolveA(promiseC); resolveC(1); rejectC(1); return promiseC.then(function() { assert.equal(called, 0); assert.equal(promiseA.isPending(), true); assert.equal(promiseB.isPending(), true); assert.equal(promiseC.isPending(), false); }); }); }); }); ================================================ FILE: test/mocha/generator.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var assertLongTrace = require("./helpers/assert_long_trace.js"); var awaitLateQueue = testUtils.awaitLateQueue; function get(arg) { return { then: function(ful, rej) { ful(arg) } } } function fail(arg) { return { then: function(ful, rej) { rej(arg) } }; } Promise.coroutine.addYieldHandler(function(yieldedValue) { if (Array.isArray(yieldedValue)) return Promise.all(yieldedValue); }); var error = new Error("asd"); describe("yielding", function() { specify("non-promise should throw", function() { return Promise.coroutine(function*(){ var a = yield {}; assert.fail(); return 4; })().then(assert.fail).caught(function(e){ assert(e instanceof TypeError); }); }); specify("an array should implicitly Promise.all them", function() { var a = Promise.defer(); var ap = a.promise; var b = Promise.defer(); var bp = b.promise; var c = Promise.defer(); var cp = c.promise; setTimeout(function(){ a.fulfill(1); b.fulfill(2); c.fulfill(3); }, 1); return Promise.coroutine(function*(){ return yield [ap, bp, cp]; })().then(function(r) { //.spread will also implicitly use .all() so that cannot be used here var a = r[0]; var b = r[1]; var c = r[2]; assert(a === 1); assert(b === 2); assert(c === 3); }); }); specify("non-promise should throw but be catchable", function() { return Promise.coroutine(function*(){ try { var a = yield {}; assert.fail(); } catch (e){ assert(e instanceof TypeError); return 4; } })().then(function(val){ assert.equal(val, 4); }); }); specify("yielding a function should not call the function", function() { let functionWasCalled = false; return Promise.coroutine(function*(){ try { yield (function() {functionWasCalled = true;}); } catch(e){ assert(e instanceof TypeError); assert.equal(functionWasCalled, false); return 4; } })().then(function(val){ assert.equal(val, 4); }); }); }); describe("thenables", function(){ specify("when they fulfill, the yielded value should be that fulfilled value", function(){ return Promise.coroutine(function*(){ var a = yield get(3); assert.equal(a, 3); return 4; })().then(function(arg){ assert.equal(arg, 4); }); }); specify("when they reject, and the generator doesn't have try.caught, it should immediately reject the promise", function(){ return Promise.coroutine(function*(){ var a = yield fail(error); assert.fail(); })().then(assert.fail).then(assert.fail, function(e){ assert.equal(e, error); }); }); specify("when they reject, and the generator has try.caught, it should continue working normally", function(){ return Promise.coroutine(function*(){ try { var a = yield fail(error); } catch (e) { return e; } assert.fail(); })().then(function(v){ assert.equal(v, error); }); }); specify("when they fulfill but then throw, it should become rejection", function(){ return Promise.coroutine(function*(){ var a = yield get(3); assert.equal(a, 3); throw error; })().then(assert.fail, function(e){ assert.equal(e, error); }); }); }); describe("yield loop", function(){ specify("should work", function(){ return Promise.coroutine(function* () { var a = [1,2,3,4,5]; for (var i = 0, len = a.length; i < len; ++i) { a[i] = yield get(a[i] * 2); } return a; })().then(function(arr){ assert.deepEqual([2,4,6,8,10], arr); }); }); specify("inside yield should work", function(){ return Promise.coroutine(function *() { var a = [1,2,3,4,5]; return yield Promise.all(a.map(function(v){ return Promise.coroutine(function *() { return yield get(v*2); })(); })); })().then(function(arr){ assert.deepEqual([2,4,6,8,10], arr); }); }); specify("with simple map should work", function(){ return Promise.coroutine(function *() { var a = [1,2,3,4,5]; return yield Promise.map(a, function(v){ return Promise.cast(get(v*2)); }); })().then(function(arr){ assert.deepEqual([2,4,6,8,10], arr); }); }); }); describe("Promise.coroutine", function() { describe("thenables", function() { specify("when they fulfill, the yielded value should be that fulfilled value", function(){ return Promise.coroutine(function*(){ var a = yield get(3); assert.equal(a, 3); return 4; })().then(function(arg){ assert.equal(arg, 4); }); }); specify("when they reject, and the generator doesn't have try.caught, it should immediately reject the promise", function(){ return Promise.coroutine(function*(){ var a = yield fail(error); assert.fail(); })().then(assert.fail).then(assert.fail, function(e){ assert.equal(e, error); }); }); specify("when they reject, and the generator has try.caught, it should continue working normally", function(){ return Promise.coroutine(function*(){ try { var a = yield fail(error); } catch (e) { return e; } assert.fail(); })().then(function(v){ assert.equal(v, error); }); }); specify("when they fulfill but then throw, it should become rejection", function(){ return Promise.coroutine(function*(){ var a = yield get(3); assert.equal(a, 3); throw error; })().then(assert.fail).then(assert.fail, function(e){ assert.equal(e, error); }); }); specify("when they are already fulfilled, the yielded value should be returned asynchronously", function(){ var value; var promise = Promise.coroutine(function*(){ yield Promise.resolve(); value = 2; })(); value = 1; return promise.then(function(){ assert.equal(value, 2); }); }); specify("when they are already rejected, the yielded reason should be thrown asynchronously", function(){ var value; var promise = Promise.coroutine(function*(){ try { yield Promise.reject(); } catch (e) { value = 2; } })(); value = 1; return promise.then(function(){ assert.equal(value, 2); }); }); }); describe("yield loop", function(){ specify("should work", function(){ return Promise.coroutine(function* () { var a = [1,2,3,4,5]; for (var i = 0, len = a.length; i < len; ++i) { a[i] = yield get(a[i] * 2); } return a; })().then(function(arr){ assert.deepEqual([2,4,6,8,10], arr); }); }); specify("inside yield should work", function(){ return Promise.coroutine(function *() { var a = [1,2,3,4,5]; return yield Promise.all(a.map(function(v){ return Promise.coroutine(function *() { return yield get(v*2); })(); })); })().then(function(arr){ assert.deepEqual([2,4,6,8,10], arr); }); }); specify("with simple map should work", function(){ return Promise.coroutine(function *() { var a = [1,2,3,4,5]; return yield Promise.map(a, function(v){ return Promise.cast(get(v*2)); }); })().then(function(arr){ assert.deepEqual([2,4,6,8,10], arr); }); }); }); describe("when using coroutine as a method", function(){ function MyClass() { this.goblins = 3; } MyClass.prototype.spawnGoblins = Promise.coroutine(function* () { this.goblins = yield get(this.goblins+1); }); specify("generator function's receiver should be the instance too", function() { var a = new MyClass(); var b = new MyClass(); return Promise.join(a.spawnGoblins().then(function(){ return a.spawnGoblins() }), b.spawnGoblins()).then(function(){ assert.equal(a.goblins, 5); assert.equal(b.goblins, 4); }); }); }); }); describe("Spawn", function() { it("should work", function() { return Promise.spawn(function*() { return yield Promise.resolve(1); }).then(function(value) { assert.strictEqual(value, 1); }); }); it("should return rejected promise when passed non function", function() { return Promise.spawn({}).then(assert.fail, function(err) { assert(err instanceof Promise.TypeError); }); }); }); describe("custom yield handlers", function() { specify("should work with timers", function() { var n = 0; Promise.coroutine.addYieldHandler(function(v) { if (typeof v === "number") { n = 1; return Promise.resolve(n); } }); return Promise.coroutine(function*() { return yield 50; })().then(function(value) { assert.equal(value, 1); assert.equal(n, 1); }); }); var _ = (function() { var promise = null; Promise.coroutine.addYieldHandler(function(v) { if (v === void 0 && promise != null) { return promise; } promise = null; }); return function() { var cb; promise = Promise.fromNode(function(callback) { cb = callback; }); return cb; }; })(); specify("Should work with callbacks", function(){ var callbackApiFunction = function(a, b, c, cb) { setTimeout(function(){ cb(null, [a, b, c]); }, 1); }; return Promise.coroutine(function*() { return yield callbackApiFunction(1, 2, 3, _()); })().then(function(result) { assert(result.length === 3); assert(result[0] === 1); assert(result[1] === 2); assert(result[2] === 3); }); }); specify("should work with thunks", function(){ Promise.coroutine.addYieldHandler(function(v) { if (typeof v === "function") { var cb; var promise = Promise.fromNode(function(callback) { cb = callback; }); try { v(cb); } catch (e) { cb(e); } return promise; } }); var thunk = function(a) { return function(callback) { setTimeout(function(){ callback(null, a*a); }, 1); }; }; return Promise.coroutine(function*() { return yield thunk(4); })().then(function(result) { assert(result === 16); }); }); specify("individual yield handler", function() { var dummy = {}; var yieldHandler = function(value) { if (value === dummy) return Promise.resolve(3); }; var coro = Promise.coroutine(function* () { return yield dummy; }, {yieldHandler: yieldHandler}); return coro().then(function(result) { assert(result === 3); }); }); specify("yield handler that throws", function() { var dummy = {}; var unreached = false; var err = new Error(); var yieldHandler = function(value) { if (value === dummy) throw err; }; var coro = Promise.coroutine(function* () { yield dummy; unreached = true; }, {yieldHandler: yieldHandler}); return coro().then(assert.fail, function(e) { assert.strictEqual(e, err); assert(!unreached); }); }); specify("yield handler is not a function", function() { try { Promise.coroutine.addYieldHandler({}); } catch (e) { assert(e instanceof Promise.TypeError); return; } assert.fail(); }); }); if (Promise.hasLongStackTraces()) { describe("Long stack traces with coroutines as context", function() { it("1 level", function() { return Promise.coroutine(function* () { yield Promise.delay(10); throw new Error(); })().then(assert.fail, function(e) { assertLongTrace(e, 1+1, [2]); }); }); it("4 levels", function() { var secondLevel = Promise.coroutine(function* () { yield thirdLevel(); }); var thirdLevel = Promise.coroutine(function* () { yield fourthLevel(); }); var fourthLevel = Promise.coroutine(function* () { throw new Error(); }); return Promise.coroutine(function* () { yield secondLevel(); })().then(assert.fail, function(e) { assertLongTrace(e, 4+1, [2, 2, 2, 2]); }); }); }); } describe("Cancellation with generators", function() { specify("input immediately cancelled", function() { var cancelled = 0; var finalled = 0; var unreached = 0; var p = new Promise(function(_, __, onCancel) {}); p.cancel(); var asyncFunction = Promise.coroutine(function* () { try { yield p; unreached++; } catch(e) { if (e === Promise.coroutine.returnSentinel) throw e; unreached++; } finally { yield Promise.resolve(); finalled++; } unreached++; }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); asyncFunction() .then(reject, function(e) { if(!(e instanceof Promise.CancellationError)) reject(new Error()); }) .lastly(function() { finalled++; resolve(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(0, cancelled); assert.equal(0, unreached); }); }); }); specify("input eventually cancelled", function() { var cancelled = 0; var finalled = 0; var unreached = 0; var p = new Promise(function(_, __, onCancel) {}); var asyncFunction = Promise.coroutine(function* () { try { yield p; unreached++; } catch(e) { if (e === Promise.coroutine.returnSentinel) throw e; unreached++; } finally { yield Promise.resolve(); finalled++; } unreached++; }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); asyncFunction() .then(reject, reject) .lastly(function() { finalled++; resolve(); }); Promise.delay(1).then(function() { p.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(2, finalled); assert.equal(0, cancelled); assert.equal(0, unreached); }); }); }); specify("output immediately cancelled", function() { var cancelled = 0; var finalled = 0; var unreached = 0; var p = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }).lastly(function() { finalled++; }); var asyncFunction = Promise.coroutine(function* () { try { yield p; unreached++; } catch(e) { if (e === Promise.coroutine.returnSentinel) throw e; unreached++; } finally { yield Promise.resolve() finalled++; } unreached++; }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); var output = asyncFunction() .then(reject, reject) .lastly(function() { finalled++; resolve(); }); output.cancel(); return result.then(function() { return awaitLateQueue(function() { assert.equal(3, finalled); assert.equal(1, cancelled); assert.equal(0, unreached); }); }); }); specify("output eventually cancelled", function() { var cancelled = 0; var finalled = 0; var unreached = 0; var p = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); }).lastly(function() { finalled++; }); var asyncFunction = Promise.coroutine(function* () { try { yield p; unreached++; } catch(e) { if (e === Promise.coroutine.returnSentinel) throw e; unreached++; } finally { yield Promise.resolve() finalled++; } unreached++; }); var resolve, reject; var result = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); var output = asyncFunction() .then(reject, reject) .lastly(function() { finalled++; resolve(); }); Promise.delay(1).then(function() { output.cancel(); }); return result.then(function() { return awaitLateQueue(function() { assert.equal(3, finalled); assert.equal(1, cancelled); assert.equal(0, unreached); }); }); }); specify("finally block runs before finally handler", function(done) { var finallyBlockCalled = false; var asyncFn = Promise.coroutine(function* () { try { yield Promise.delay(100); } finally { yield Promise.delay(100); finallyBlockCalled = true; } }); var p = asyncFn(); Promise.resolve().then(function() { p.cancel(); }); p.finally(function() { assert.ok(finallyBlockCalled, "finally block should have been called before finally handler"); done(); }).catch(done); }); }); ================================================ FILE: test/mocha/get.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var join = Promise.join; describe("indexed getter", function() { var p = Promise.resolve([0, 1, 2, 3, 4, 5, 7, 5,10]); specify("gets positive index", function() { var first = p.get(0); var fourth = p.get(3); var last = p.get(8); return join(first, fourth, last, function(a, b, c) { assert(a === 0); assert(b === 3); assert(c === 10); }); }); specify("gets negative index", function() { var last = p.get(-1); var first = p.get(-20); return join(last, first, function(a, b) { assert.equal(a, 10); assert.equal(b, 0); }); }); }); describe("identifier getter", function() { var p = Promise.resolve(new RegExp("", "")); specify("gets property", function() { var ci = p.get("ignoreCase"); var g = p.get("global"); var lastIndex = p.get("lastIndex"); var multiline = p.get("multiline"); return join(ci, g, lastIndex, multiline, function(ci, g, lastIndex, multiline) { assert(ci === false); assert(g === false); assert(lastIndex === 0); assert(multiline === false); }); }); specify("gets same property", function() { var o = {o: 1}; var o2 = {o: 2}; o = Promise.resolve(o).get("o"); o2 = Promise.resolve(o2).get("o"); return join(o, o2, function(one, two) { assert.strictEqual(1, one); assert.strictEqual(2, two); }); }); }); describe("non identifier getters", function() { var p = Promise.resolve({"-": "val"}); specify("get property", function() { return p.get("-").then(function(val) { assert(val === "val"); }); }); specify.skip("overflow cache", function() { var a = new Array(1024); var o = {}; for (var i = 0; i < a.length; ++i) { a[i] = "get" + i; o["get" + i] = i*2; } var b = Promise.map(a, function(item, index) { return Promise.resolve(o).get(a[index]); }).filter(function(value, index) { return value === index * 2; }).then(function(values) { assert.strictEqual(values.length, a.length); }); return b; }); }); ================================================ FILE: test/mocha/getNewLibraryCopy.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.getNewLibraryCopy", function() { it("should return an independent copy of Bluebird library", function() { var Promise2 = Promise.getNewLibraryCopy(); Promise2.x = 123; assert.equal(typeof Promise2.prototype.then, "function"); assert.notEqual(Promise2, Promise); assert.equal(Promise2.x, 123); assert.notEqual(Promise.x, 123); }); it("should return copy of Bluebird library with its own getNewLibraryCopy method", function() { var Promise2 = Promise.getNewLibraryCopy(); var Promise3 = Promise2.getNewLibraryCopy(); Promise3.x = 123; assert.equal(typeof Promise3.prototype.then, "function"); assert.notEqual(Promise3, Promise); assert.notEqual(Promise3, Promise2); assert.equal(Promise3.x, 123); assert.notEqual(Promise.x, 123); assert.notEqual(Promise2.x, 123); }); }); ================================================ FILE: test/mocha/github-2xx-76.js ================================================ "use strict"; Promise.longStackTraces(); var assert = require("assert"); var testUtils = require("./helpers/util.js"); var isNodeJS = testUtils.isNodeJS; if (isNodeJS) { describe("github276 - stack trace cleaner", function(){ specify("message with newline and a$_b should not be removed", function(){ return Promise.resolve(1).then(function() { throw new Error("Blah\n a$_b"); }).then(assert.fail, function(e) { var msg = e.stack.split('\n')[1] assert(msg.indexOf('a$_b') >= 0, 'message should contain a$_b'); }); }); }); } ================================================ FILE: test/mocha/github-3.6.4.js ================================================ "use strict"; var assert = require("assert"); var Promise = adapter; function defer() { var resolve, reject; var promise = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); return { resolve: resolve, reject: reject, promise: promise }; } describe("github-364", function() { specify("resolve between thens", function(done) { var calls = 0; var def = defer(); def.promise.then(function() { calls++ }); def.resolve(); def.promise.then(function() { calls++ }).then(function() { calls++ }).then(function() { Promise.delay(11).then(function() { assert.equal(calls, 3); done(); }); }); }); }); ================================================ FILE: test/mocha/github-3.7.3.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var Promise = adapter; describe("github-373", function() { specify("unhandled unsuccessful Promise.join should result in correct error being reported", function() { var err = new Error("test"); var rejected = Promise.delay(1).thenThrow(err); Promise.join(rejected, Promise.resolve(1), function(){}); return testUtils.onUnhandledSucceed(err); }); }); ================================================ FILE: test/mocha/github-4.1.7.js ================================================ var Promise = adapter; var assert = require("assert"); describe("Github #417", function() { specify("minimal repro", function() { var promise = new Promise(function(resolve) { resolve(Promise.resolve().then(function() { return new Promise(function(resolve) { setTimeout(resolve, 1); }); })); }); return promise.then(function() { assert(promise.isResolved()); }); }); specify("original repro", function() { var called = 0; var bar = Promise.method(function() { return Promise.bind(this) .then(Promise.method(function() { called++; })); }); var foo = Promise.method(function() { return Promise.bind(this) .then(Promise.method(function() { return bar(); })) .bind(this) .lastly(Promise.method(function() { called++; })); }); return foo().then(function() { called++; assert.equal(3, called); }); }); }); ================================================ FILE: test/mocha/github36.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("github36", function(){ specify("should work", function() { return new Promise(function(resolve, reject) { var called = 0; var donecalled = false; var _d = Promise.defer(); _d.resolve() var f1 = function() { return _d.promise.then(function() { return true; }) } var f2 = function() { var d1 = Promise.defer() setTimeout(function() { d1.resolve() }, 1) return d1.promise.then(function() { return _d.promise.then(function() { }) }); } var f3 = function() { called++; if (called > 15) { return resolve(); } var promise = f1().then(function() { f2() .then(function() { f3() }) }) promise.lastly(function() { setTimeout(function() { f3() }, 1) }) } f3(); }); }); }); ================================================ FILE: test/mocha/helpers/assert_long_trace.js ================================================ var assert = require("assert"); function assertLongTrace(error, expectedJumpCount, expectedFramesForJumpsMap) { var envFramePattern = /(?:\(node.js:|\(module.js:|\(timers.js:|\bcheckTimers\b|\bdrainQueue\b|\btimerLoop\b|\b_onImmediate\b|\b_immediateCallback\b)/; var stack = error.stack.split("\n"); var frameLinePattern = /(^\s+at|@|\s+\(No stack trace\))/; var previousEventPattern = /^From previous event/; var firstLine; for (var i = 0; i < stack.length; ++i) { if (previousEventPattern.test(stack[i])) { throw new Error("From previous event before any frames"); } if (frameLinePattern.test(stack[i])) { firstLine = i - 1; break; } } var prev = stack[firstLine - 1]; var jumpCount = 1; var jumpIndex = 0; var currentJumpFramesCount = 0; var envFramesCount = 0; for (var i = firstLine; i < stack.length; ++i) { var line = stack[i]; if (previousEventPattern.test(line)) { var jumpContainsOnlyEnvFrames = currentJumpFramesCount === 0 && envFramesCount > 0; if (!jumpContainsOnlyEnvFrames) { if (previousEventPattern.test(prev)) { throw new Error("2 consecutive From previous events"); } if (jumpIndex < expectedFramesForJumpsMap.length) { var expectedFrames = expectedFramesForJumpsMap[jumpIndex]; var expectedMessage = typeof expectedFrames === "number" ? (expectedFrames + "") : (expectedFrames[0] + "-" + expectedFrames[1]); var message = "Expected " + (jumpIndex+1) + "th jump to contain " + expectedMessage + " frames " + "but it contains " + currentJumpFramesCount + " frames"; if (typeof expectedFrames === "number") { assert(expectedFrames === currentJumpFramesCount, message); } else { assert(expectedFrames[0] <= currentJumpFramesCount && currentJumpFramesCount <= expectedFrames[1], message); } } jumpCount++; jumpIndex++; } currentJumpFramesCount = 0; envFramesCount = 0; } else if (frameLinePattern.test(line)) { if (envFramePattern.test(line)) { envFramesCount++; } else { currentJumpFramesCount++; } } prev = line; } assert.strictEqual( previousEventPattern.test(stack[stack.length - 1]), false, "The last line cannot be 'From previous event:'"); if (typeof expectedJumpCount === "number") { assert.strictEqual(expectedJumpCount, jumpCount, "Expected " + expectedJumpCount + " jumps but saw " + jumpCount + " jumps"); } else { assert(expectedJumpCount[0] <= jumpCount && jumpCount <= expectedJumpCount[1], "Expected " + expectedJumpCount[0] + "-" + expectedJumpCount[1] + " jumps but saw " + jumpCount + " jumps" ); } if (jumpCount > (expectedFramesForJumpsMap.length + 1)) { throw new Error("All jumps except the last one require an "+ "expected frame count. " + "Got expected frame counts for only " + expectedFramesForJumpsMap.length + " while " + (jumpCount-1) + " was expected"); } } module.exports = assertLongTrace; ================================================ FILE: test/mocha/helpers/bluebird0_7_0.js ================================================ /* jshint -W014, -W116, -W106 */ /* global process, unreachable */ /** * @preserve Copyright (c) 2013 Petka Antonov * * 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:

    * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ (function( global, Function, Array, Error, Object ) {"use strict"; var ASSERT = (function(){/* jshint -W014, -W116 */ var AssertionError = (function() { function AssertionError( a ) { this.constructor$( a ); this.message = a; this.name = "AssertionError"; } AssertionError.prototype = new Error(); AssertionError.prototype.constructor = AssertionError; AssertionError.prototype.constructor$ = Error; return AssertionError; })(); return function assert( boolExpr, message ) { if( boolExpr === true ) return; var ret = new AssertionError( message ); if( Error.captureStackTrace ) { Error.captureStackTrace( ret, assert ); } if( console && console.error ) { console.error( ret.stack + "" ); } throw ret; }; })(); var errorObj = {e: {}}; var rescape = /[\r\n\u2028\u2029']/g; var replacer = function( ch ) { return "\\u" + (("0000") + (ch.charCodeAt(0).toString(16))).slice(-4); }; function safeToEmbedString( str ) { return str.replace( rescape, replacer ); } function tryCatch1( fn, receiver, arg ) { ASSERT(((typeof fn) === "function"), "typeof fn === \u0022function\u0022"); try { return fn.call( receiver, arg ); } catch( e ) { errorObj.e = e; return errorObj; } } function tryCatch2( fn, receiver, arg, arg2 ) { ASSERT(((typeof fn) === "function"), "typeof fn === \u0022function\u0022"); try { return fn.call( receiver, arg, arg2 ); } catch( e ) { errorObj.e = e; return errorObj; } } function tryCatchApply( fn, args ) { ASSERT(((typeof fn) === "function"), "typeof fn === \u0022function\u0022"); try { return fn.apply( void 0, args ); } catch( e ) { errorObj.e = e; return errorObj; } } var create = Object.create || function( proto ) { function F(){} F.prototype = proto; return new F(); }; function makeNodePromisified( callback, receiver ) { function getCall(count) { var args = new Array(count); for( var i = 0, len = args.length; i < len; ++i ) { args[i] = "a" + (i+1); } var comma = count > 0 ? "," : ""; return ( receiver === void 0 ? "callback("+args.join(",")+ comma +" fn);" : "callback.call(receiver, "+args.join(",") + comma + " fn);" ) + "break;"; } return new Function("Promise", "callback", "receiver", "return function promisified( a1, a2, a3, a4, a5 ) {\"use strict\";" + "var len = arguments.length;" + "var resolver = Promise.pending( promisified );" + "" + "var fn = function fn( err, value ) {" + "if( err ) {" + "resolver.reject( err );" + "}" + "else {" + "resolver.fulfill( value );" + "}" + "};" + "switch( len ) {" + "case 5:" + getCall(5) + "case 4:" + getCall(4) + "case 3:" + getCall(3) + "case 2:" + getCall(2) + "case 1:" + getCall(1) + "case 0:" + getCall(0) + "default: callback.apply(receiver, arguments); break;" + "}" + "return resolver.promise;" + "" + "};" )(Promise, callback, receiver); } var inherits = function( Child, Parent ) { var hasProp = {}.hasOwnProperty; function T() { this.constructor = Child; this.constructor$ = Parent; for (var propertyName in Parent.prototype) { if (hasProp.call( Parent.prototype, propertyName) && propertyName.charAt(propertyName.length-1) !== "$" ) { this[ propertyName + "$"] = Parent.prototype[propertyName]; } } } T.prototype = Parent.prototype; Child.prototype = new T(); return Child.prototype; }; function subError( constructorName, nameProperty, defaultMessage ) { defaultMessage = safeToEmbedString("" + defaultMessage ); nameProperty = safeToEmbedString("" + nameProperty ); return new Function("create", "'use strict';\n" + constructorName + ".prototype = create(Error.prototype);" + constructorName + ".prototype.constructor = "+constructorName+";" + "function "+constructorName+"(msg){" + "if( Error.captureStackTrace ) {" + "Error.captureStackTrace(this, this.constructor);" + "}" + "Error.call(this, msg);" + "this.message = typeof msg === 'string'" + "? msg" + ": '"+defaultMessage+"';" + "this.name = '"+nameProperty+"';" + "} return "+constructorName+";")(create); } if( typeof global.TypeError === "undefined" ) { global.TypeError = subError( "TypeError", "TypeError" ); } var CancellationError = subError( "CancellationError", "Cancel", "cancellation error" ); var TimeoutError = subError( "TimeoutError", "Timeout", "timeout error" ); var CapturedTrace = (function() { var rignore = new RegExp( "\\b(?:Promise(?:Array)?\\$_\\w+|tryCatch(?:1|2|Apply)|setTimeout" + "|makeNodePromisified|processImmediate|nextTick" + "|Async\\$\\w+)\\b" ); var rtraceline = null; var formatStack = null; function CapturedTrace( ignoreUntil ) { ASSERT(((typeof ignoreUntil) === "function"), "typeof ignoreUntil === \u0022function\u0022"); ASSERT(((typeof ignoreUntil.name) === "string"), "typeof ignoreUntil.name === \u0022string\u0022"); ASSERT((ignoreUntil.name.length > 0), "ignoreUntil.name.length > 0"); this.captureStackTrace( ignoreUntil ); } var method = inherits( CapturedTrace, Error ); method.captureStackTrace = function CapturedTrace$captureStackTrace( ignoreUntil ) { captureStackTrace( this, ignoreUntil ); }; CapturedTrace.possiblyUnhandledRejection = function CapturedTrace$PossiblyUnhandledRejection( reason ) { if( typeof console === "object" ) { var stack = reason.stack; var message = "Possibly unhandled " + formatStack( stack, reason ); if( typeof console.error === "function" ) { console.error( message ); } else if( typeof console.log === "function" ) { console.log( message ); } } }; CapturedTrace.combine = function CapturedTrace$Combine( current, prev ) { var curLast = current.length - 1; for( var i = prev.length - 1; i >= 0; --i ) { var line = prev[i]; if( current[ curLast ] === line ) { current.pop(); curLast--; } else { break; } } var lines = current.concat( prev ); var ret = []; for( var i = 0, len = lines.length; i < len; ++i ) { if( rignore.test( lines[i] ) || ( i > 0 && !rtraceline.test( lines[i] ) ) ) { continue; } ret.push( lines[i] ); } return ret; }; CapturedTrace.isSupported = function CapturedTrace$IsSupported() { return typeof captureStackTrace === "function"; }; var captureStackTrace = (function stackDetection() { if( typeof Error.stackTraceLimit === "number" && typeof Error.captureStackTrace === "function" ) { rtraceline = /^\s*at\s*/; formatStack = function( stack, error ) { return ( typeof stack === "string" ) ? stack : error.name + ". " + error.message; }; return Error.captureStackTrace; } var err = new Error(); if( typeof err.stack === "string" && typeof "".startsWith === "function" && ( err.stack.startsWith("stackDetection@")) && stackDetection.name === "stackDetection" ) { Object.defineProperty( Error, "stackTraceLimit", { writable: true, enumerable: false, configurable: false, value: 25 }); rtraceline = /@/; var rline = /[@\n]/; formatStack = function( stack, error ) { return ( typeof stack === "string" ) ? ( error.name + ". " + error.message + "\n" + stack ) : ( error.name + ". " + error.message ); }; return function captureStackTrace(o, fn) { var name = fn.name; var stack = new Error().stack; var split = stack.split( rline ); var i, len = split.length; for (i = 0; i < len; i += 2) { if (split[i] === name) { break; } } ASSERT(((i + 2) < split.length), "i + 2 < split.length"); split = split.slice(i + 2); len = split.length - 2; var ret = ""; for (i = 0; i < len; i += 2) { ret += split[i]; ret += "@"; ret += split[i + 1]; ret += "\n"; } o.stack = ret; }; } else { return null; } })(); return CapturedTrace;})(); function GetterCache(){} function FunctionCache(){} var getterCache = new GetterCache(), functionCache = new FunctionCache(), rjsident = /^[a-zA-Z$_][a-zA-Z0-9$_]*$/, rkeyword = new RegExp( "^(?:__proto__|undefined|NaN|Infinity|this|false|true|null|eval|" + "arguments|break|case|catch|continue|debugger|default|delete|do|" + "else|finally|for|function|if|in|instanceof|new|return|switch|th" + "row|try|typeof|var|void|while|with|class|enum|export|extends|im" + "port|super|implements|interface|let|package|private|protected|pu" + "blic|static|yield)$" ), hasProp = {}.hasOwnProperty; function isJsIdentifier( val ) { return rjsident.test(val) && !rkeyword.test(val); } function formatPropertyRead( val ) { if( isJsIdentifier(val) ) { return "." + val; } else { return "['"+safeToEmbedString(val)+"']"; } } function getGetter( propertyName ) { if( hasProp.call( getterCache, propertyName ) ) { return getterCache[propertyName]; } var fn = new Function("obj", "return obj"+ formatPropertyRead(""+propertyName) +";"); getterCache[propertyName] = fn; return fn; } function getFunction( propertyName ) { if( hasProp.call( functionCache, propertyName ) ) { return functionCache[propertyName]; } var fn = new Function("obj", "return obj"+ formatPropertyRead(""+propertyName) +"();"); functionCache[propertyName] = fn; return fn; } var Async = (function() { var deferFn = typeof process !== "undefined" ? ( typeof global.setImmediate !== "undefined" ? function( fn ){ global.setImmediate( fn ); } : function( fn ) { process.nextTick( fn ); } ) : ( typeof setTimeout !== "undefined" ? function( fn ) { setTimeout( fn, 4 ); } : function( fn ) { fn(); } ) ; function Async() { this._isTickUsed = false; this._length = 0; this._backupBuffer = []; var functionBuffer = this._functionBuffer = new Array( 1000 * 3 ); var self = this; this.consumeFunctionBuffer = function Async$consumeFunctionBuffer() { self._consumeFunctionBuffer(); }; for( var i = 0, len = functionBuffer.length; i < len; ++i ) { functionBuffer[i] = void 0; } } var method = Async.prototype; method.haveItemsQueued = function Async$haveItemsQueued() { return this._length > 0; }; method.invokeLater = function Async$invokeLater( fn, receiver, arg ) { ASSERT(((typeof fn) === "function"), "typeof fn === \u0022function\u0022"); ASSERT((arguments.length === 3), "arguments.length === 3"); this._backupBuffer.push( fn, receiver, arg ); if( !this._isTickUsed ) { deferFn( this.consumeFunctionBuffer ); this._isTickUsed = true; } }; method.invoke = function Async$invoke( fn, receiver, arg ) { ASSERT(((typeof fn) === "function"), "typeof fn === \u0022function\u0022"); ASSERT((arguments.length === 3), "arguments.length === 3"); var functionBuffer = this._functionBuffer, len = functionBuffer.length, length = this._length; if( length === len ) { functionBuffer.push( fn, receiver, arg ); } else { ASSERT((length < len), "length < len"); functionBuffer[ length + 0 ] = fn; functionBuffer[ length + 1 ] = receiver; functionBuffer[ length + 2 ] = arg; } this._length = length + 3; if( !this._isTickUsed ) { deferFn( this.consumeFunctionBuffer ); this._isTickUsed = true; } }; method._consumeFunctionBuffer = function Async$_consumeFunctionBuffer() { var functionBuffer = this._functionBuffer; ASSERT(this._isTickUsed, "this._isTickUsed"); for( var i = 0; i < this._length; i += 3 ) { functionBuffer[ i + 0 ].call( functionBuffer[ i + 1 ], functionBuffer[ i + 2 ] ); functionBuffer[ i + 0 ] = functionBuffer[ i + 1 ] = functionBuffer[ i + 2 ] = void 0; } this._reset(); if( this._backupBuffer.length ) { var buffer = this._backupBuffer; for( var i = 0; i < buffer.length; i+= 3 ) { buffer[ i + 0 ].call( buffer[ i + 1 ] , buffer[ i + 2 ] ); } buffer.length = 0; } }; method._reset = function Async$_reset() { this._isTickUsed = false; this._length = 0; }; return Async;})(); var async = new Async(); var Thenable = (function() { function Thenable() { this.errorObj = errorObj; this.__id__ = 0; this.treshold = 1000; this.thenableCache = new Array( this.treshold ); this.promiseCache = new Array( this.treshold ); this._compactQueued = false; } var method = Thenable.prototype; method.couldBe = function Thenable$couldBe( ret ) { if( ret === null || typeof ret === "undefined" || typeof ret === "string" || typeof ret === "boolean" || typeof ret === "number" ) { return false; } var id = ret.__id_$thenable__; if( typeof id === "number" && this.thenableCache[id] !== void 0 ) { return true; } return ("then" in ret); }; method.is = function Thenable$is( ret, ref ) { var id = ret.__id_$thenable__; if( typeof id === "number" && this.thenableCache[id] !== void 0 ) { ref.ref = this.thenableCache[id]; ref.promise = this.promiseCache[id]; return true; } return this._thenableSlowCase( ret, ref ); }; method.addCache = function Thenable$_addCache( thenable, promise ) { var id = this.__id__; this.__id__ = id + 1; var descriptor = this._descriptor( id ); Object.defineProperty( thenable, "__id_$thenable__", descriptor ); this.thenableCache[id] = thenable; this.promiseCache[id] = promise; ASSERT((this.thenableCache[thenable.__id_$thenable__] === thenable), "this.thenableCache[ thenable.__id_$thenable__ ] === thenable"); if( this.thenableCache.length > this.treshold && !this._compactQueued) { this._compactQueued = true; async.invokeLater( this._compactCache, this, void 0 ); } }; method.deleteCache = function Thenable$deleteCache( thenable ) { var id = thenable.__id_$thenable__; ASSERT(((typeof id) === "number"), "typeof id === \u0022number\u0022"); ASSERT(((id | 0) === id), "(id | 0) === id"); if( id === -1 ) { return; } ASSERT((id > -1), "id > -1"); ASSERT((id < this.__id__), "id < this.__id__"); ASSERT((this.thenableCache[id] === thenable), "this.thenableCache[id] === thenable"); this.thenableCache[id] = void 0; this.promiseCache[id] = void 0; thenable.__id_$thenable__ = -1;}; var descriptor = { value: 0, enumerable: false, writable: true, configurable: true }; method._descriptor = function Thenable$_descriptor( id ) { descriptor.value = id; return descriptor; }; method._compactCache = function Thenable$_compactCache() { var arr = this.thenableCache; var promiseArr = this.promiseCache; var skips = 0; var j = 0; for( var i = 0, len = arr.length; i < len; ++i ) { var item = arr[ i ]; if( item === void 0 ) { skips++; } else { promiseArr[ j ] = promiseArr[ i ]; item.__id_$thenable__ = j; arr[ j++ ] = item; } } var newId = arr.length - skips; if( newId === this.__id__ ) { this.treshold *= 2; } else for( var i = newId, len = arr.length; i < len; ++i ) { promiseArr[ j ] = arr[i] = void 0; } this.__id__ = newId; this._compactQueued = false; }; method._thenableSlowCase = function Thenable$_thenableSlowCase( ret, ref ) { try { var then = ret.then; if( typeof then === "function" ) { ref.ref = then; return true; } return false; } catch(e) { this.errorObj.e = e; ref.ref = this.errorObj; return true; } }; return Thenable;})(); var CatchFilter = (function() { function CatchFilter( instances, callback ) { this._instances = instances; this._callback = callback; } var method = CatchFilter.prototype; method.doFilter = function( e ) { if( e === null || typeof e !== "object" ) { throw e; } var cb = this._callback; for( var i = 0, len = this._instances.length; i < len; ++i ) { var item = this._instances[i]; if( e instanceof item ) { var ret = tryCatch1( cb, void 0, e ); if( ret === errorObj ) { throw ret.e; } return ret; } } throw e; }; return CatchFilter;})(); var Promise = (function() { function isObject( value ) { if( value === null ) { return false; } return ( typeof value === "object" || typeof value === "function" ); } function isPromise( obj ) { if( typeof obj !== "object" ) return false; return obj instanceof Promise; } var Err = Error; function isError( obj ) { if( typeof obj !== "object" ) return false; return obj instanceof Err; } var Arr = Array; var isArray = Arr.isArray || function( obj ) { return obj instanceof Arr; }; var APPLY = {}; var thenable = new Thenable( errorObj ); function Promise( resolver ) { if( typeof resolver === "function" ) this._resolveResolver( resolver ); this._bitField = 67108864; this._fulfill0 = void 0; this._reject0 = void 0; this._progress0 = void 0; this._promise0 = void 0; this._receiver0 = void 0; this._resolvedValue = void 0; this._cancellationParent = void 0; if( longStackTraces ) this._traceParent = this._peekContext(); } var method = Promise.prototype; var longStackTraces = true; Promise.longStackTraces = function() { if( async.haveItemsQueued() && longStackTraces === false ) { throw new Error("Cannot enable long stack traces " + "after promises have been created"); } longStackTraces = true; }; method._setTrace = function _setTrace( fn ) { ASSERT((this._trace == null), "this._trace == null"); if( longStackTraces ) { this._trace = new CapturedTrace( typeof fn === "function" ? fn : _setTrace ); } return this; }; method.toString = function Promise$toString() { return "[object Promise]"; }; method.caught = method["catch"] = function Promise$catch( fn ) { var len = arguments.length; if( len > 1 ) { var catchInstances = new Array( len - 1 ), j = 0, i; for( i = 0; i < len - 1; ++i ) { var item = arguments[i]; if( typeof item === "function" && ( item.prototype instanceof Error || item === Error ) ) { catchInstances[j++] = item; } } catchInstances.length = j; fn = arguments[i]; var catchFilter = new CatchFilter( catchInstances, fn ); return this._then( void 0, catchFilter.doFilter, void 0, catchFilter, void 0, this.caught ); } return this._then( void 0, fn, void 0, void 0, void 0, this.caught ); }; method.progressed = function Promise$progressed( fn ) { return this._then( void 0, void 0, fn, void 0, void 0, this.progressed ); }; function thrower( r ) { throw r; } function slowFinally( ret, reasonOrValue ) { if( this.isFulfilled() ) { return ret._then(function() { return reasonOrValue; }, thrower, void 0, this, void 0, slowFinally ); } else { return ret._then(function() { throw reasonOrValue; }, thrower, void 0, this, void 0, slowFinally ); } } method.lastly = method["finally"] = function Promise$finally( fn ) { var r = function( reasonOrValue ) { var ret = fn( reasonOrValue ); if( isPromise( ret ) ) { return slowFinally.call( this, ret, reasonOrValue ); } if( this.isRejected() ) throw reasonOrValue; return reasonOrValue; }; return this._then( r, r, void 0, this, void 0, this.anyway ); }; method.inspect = function Promise$inspect() { return new PromiseInspection( this ); }; method.cancel = function Promise$cancel() { if( !this.isCancellable() ) return this; var cancelTarget = this; while( cancelTarget._cancellationParent !== void 0 ) { cancelTarget = cancelTarget._cancellationParent; } if( cancelTarget === this ) { var err = new CancellationError(); this._attachExtraTrace( err ); async.invoke( this._reject, this, err ); } else { async.invoke( cancelTarget.cancel, cancelTarget, void 0 ); } return this; }; method.uncancellable = function Promise$uncancellable() { var ret = new Promise(); ret._setTrace(); ret._unsetCancellable(); ret._assumeStateOf( this, true ); return ret; }; method.fork = function Promise$fork( didFulfill, didReject, didProgress ) { var ret = this._then( didFulfill, didReject, didProgress, void 0, void 0, this.fork ); ret._cancellationParent = void 0; return ret; }; method.call = function Promise$call( propertyName ) { var len = arguments.length; if( len < 2 ) { return this._callFast( propertyName ); } else { var args = new Array(len-1); for( var i = 1; i < len; ++i ) { args[ i - 1 ] = arguments[ i ]; } return this._callSlow( propertyName, args ); } }; method.get = function Promise$get( propertyName ) { return this._then( getGetter( propertyName ), void 0, void 0, void 0, void 0, this.get ); }; method.then = function Promise$then( didFulfill, didReject, didProgress ) { return this._then( didFulfill, didReject, didProgress, void 0, void 0, this.then ); }; method.spread = function Promise$spread( didFulfill, didReject ) { return this._then( didFulfill, didReject, void 0, APPLY, void 0, this.spread ); }; method.isFulfilled = function Promise$isFulfilled() { return ( this._bitField & 268435456 ) > 0; }; method.isRejected = function Promise$isRejected() { return ( this._bitField & 134217728 ) > 0; }; method.isPending = function Promise$isPending() { return !this.isResolved(); }; method.isResolved = function Promise$isResolved() { return ( this._bitField & 402653184 ) > 0; }; method.isCancellable = function Promise$isCancellable() { return !this.isResolved() && this._cancellable(); }; method.toJSON = function Promise$toJSON() { var inspection = this.inspect(); var ret = { isFulfilled: false, isRejected: false }; if( inspection.isFulfilled() ) { ret.fulfillmentValue = inspection.value(); ret.isFulfilled = true; } else if( inspection.isRejected() ) { ret.rejectionReason = inspection.error(); ret.isRejected = true; } return ret; }; method.map = function Promise$map( fn ) { return Promise.map( this, fn ); }; method.all = function Promise$all() { return Promise.all( this ); }; method.any = function Promise$any() { return Promise.any( this ); }; method.settle = function Promise$settle() { return Promise.settle( this ); }; method.some = function Promise$some( count ) { return Promise.some( this, count ); }; method.reduce = function Promise$reduce( fn, initialValue ) { return Promise.reduce( this, fn, initialValue ); }; Promise.is = isPromise; Promise.settle = function Promise$Settle( promises ) { var ret = Promise._all( promises, SettledPromiseArray ); return ret.promise(); }; Promise.all = function Promise$All( promises ) { var ret = Promise._all( promises, PromiseArray ); return ret.promise(); }; Promise.join = function Promise$Join() { var ret = new Array( arguments.length ); for( var i = 0, len = ret.length; i < len; ++i ) { ret[i] = arguments[i]; } return Promise._all( ret, PromiseArray ).promise(); }; Promise.any = function Promise$Any( promises ) { var ret = Promise._all( promises, AnyPromiseArray ); return ret.promise(); }; Promise.some = function Promise$Some( promises, howMany ) { var ret = Promise._all( promises, SomePromiseArray ); if( ( howMany | 0 ) !== howMany ) { throw new TypeError("howMany must be an integer"); } var len = ret.length(); howMany = Math.max(0, Math.min( howMany, len ) ); ret._howMany = howMany; return ret.promise(); }; function mapper( fulfilleds ) { var fn = this; var shouldDefer = false; for( var i = 0, len = fulfilleds.length; i < len; ++i ) { var fulfill = fn(fulfilleds[i]); if( !shouldDefer && isPromise( fulfill ) ) { if( fulfill.isFulfilled() ) { fulfilleds[i] = fulfill._resolvedValue; continue; } else { shouldDefer = true; } } fulfilleds[i] = fulfill; } return shouldDefer ? Promise.all( fulfilleds ) : fulfilleds; } Promise.map = function Promise$Map( promises, fn ) { if( typeof fn !== "function" ) throw new TypeError( "fn is not a function" ); return Promise.all( promises )._then( mapper, void 0, void 0, fn, void 0, Promise.all ); }; function reducer( fulfilleds, initialValue ) { var fn = this; var len = fulfilleds.length; var accum; var startIndex = 0; if( initialValue !== void 0 ) { accum = initialValue; startIndex = 0; } else { accum = len > 0 ? fulfilleds[0] : void 0; startIndex = 1; } for( var i = startIndex; i < len; ++i ) { accum = fn( accum, fulfilleds[i], i, len ); } return accum; } function slowReduce( promises, fn, initialValue ) { return Promise._all( promises, PromiseArray, slowReduce ) .promise() .then( function( fulfilleds ) { return reducer.call( fn, fulfilleds, initialValue ); }); } Promise.reduce = function Promise$Reduce( promises, fn, initialValue ) { if( typeof fn !== "function" ) throw new TypeError( "fn is not a function"); if( initialValue !== void 0 ) { return slowReduce( promises, fn, initialValue ); } return Promise .all( promises ) ._then( reducer, void 0, void 0, fn, void 0, Promise.all ); }; Promise.fulfilled = function Promise$Fulfilled( value ) { var ret = new Promise(); ret._setTrace(); if( ret._tryAssumeStateOf( value, false ) ) { return ret; } ret._cleanValues(); ret._setFulfilled(); ret._resolvedValue = value; return ret; }; Promise.rejected = function Promise$Rejected( reason ) { var ret = new Promise(); ret._setTrace(); ret._cleanValues(); ret._setRejected(); ret._resolvedValue = reason; return ret; }; Promise.pending = function Promise$Pending( caller ) { var promise = new Promise(); promise._setTrace( caller ); return new PromiseResolver( promise ); }; Promise.cast = function Promise$Cast( obj ) { var ret = cast( obj ); if( !( ret instanceof Promise ) ) { return Promise.fulfilled( ret ); } return ret; }; Promise.onPossiblyUnhandledRejection = function Promise$OnPossiblyUnhandledRejection( fn ) { if( typeof fn === "function" ) { CapturedTrace.possiblyUnhandledRejection = fn; } else { CapturedTrace.possiblyUnhandledRejection = void 0; } }; Promise.promisify = function Promise$Promisify( callback, receiver) { return makeNodePromisified( callback, receiver ); }; method._then = function Promise$_then( didFulfill, didReject, didProgress, receiver, internalData, caller ) { var haveInternalData = internalData !== void 0; var ret = haveInternalData ? internalData : new Promise(); if( longStackTraces && !haveInternalData ) { ret._traceParent = this; ret._setTrace( typeof caller === "function" ? caller : this._then ); } var callbackIndex = this._addCallbacks( didFulfill, didReject, didProgress, ret, receiver ); if( this.isResolved() ) { async.invoke( this._resolveLast, this, callbackIndex ); } else if( !haveInternalData && this.isCancellable() ) { ret._cancellationParent = this; } if( this._isDelegated() ) { this._unsetDelegated(); ASSERT((! this.isResolved()), "!this.isResolved()"); var x = this._resolvedValue; if( !this._tryThenable( x ) ) { async.invoke( this._fulfill, this, x ); } } return ret; }; method._length = function Promise$_length() { ASSERT(isPromise(this), "isPromise( this )"); ASSERT((arguments.length === 0), "arguments.length === 0"); return this._bitField & 16777215; }; method._isFollowingOrFulfilledOrRejected = function Promise$_isFollowingOrFulfilledOrRejected() { return ( this._bitField & 939524096 ) > 0; }; method._setLength = function Promise$_setLength( len ) { this._bitField = ( this._bitField & -16777216 ) | ( len & 16777215 ) ; }; method._cancellable = function Promise$_cancellable() { return ( this._bitField & 67108864 ) > 0; }; method._setFulfilled = function Promise$_setFulfilled() { this._bitField = this._bitField | 268435456; }; method._setRejected = function Promise$_setRejected() { this._bitField = this._bitField | 134217728; }; method._setFollowing = function Promise$_setFollowing() { this._bitField = this._bitField | 536870912; }; method._setDelegated = function Promise$_setDelegated() { this._bitField = this._bitField | -1073741824; }; method._isDelegated = function Promise$_isDelegated() { return ( this._bitField & -1073741824 ) === -1073741824; }; method._unsetDelegated = function Promise$_unsetDelegated() { this._bitField = this._bitField & ( ~-1073741824 ); }; method._setCancellable = function Promise$_setCancellable() { this._bitField = this._bitField | 67108864; }; method._unsetCancellable = function Promise$_unsetCancellable() { this._bitField = this._bitField & ( ~67108864 ); }; method._receiverAt = function Promise$_receiverAt( index ) { ASSERT(((typeof index) === "number"), "typeof index === \u0022number\u0022"); ASSERT((index >= 0), "index >= 0"); ASSERT((index < this._length()), "index < this._length()"); ASSERT(((index % 5) === 0), "index % CALLBACK_SIZE === 0"); if( index === 0 ) return this._receiver0; return this[ index + 4 - 5 ]; }; method._promiseAt = function Promise$_promiseAt( index ) { ASSERT(((typeof index) === "number"), "typeof index === \u0022number\u0022"); ASSERT((index >= 0), "index >= 0"); ASSERT((index < this._length()), "index < this._length()"); ASSERT(((index % 5) === 0), "index % CALLBACK_SIZE === 0"); if( index === 0 ) return this._promise0; return this[ index + 3 - 5 ]; }; method._fulfillAt = function Promise$_fulfillAt( index ) { ASSERT(((typeof index) === "number"), "typeof index === \u0022number\u0022"); ASSERT((index >= 0), "index >= 0"); ASSERT((index < this._length()), "index < this._length()"); ASSERT(((index % 5) === 0), "index % CALLBACK_SIZE === 0"); if( index === 0 ) return this._fulfill0; return this[ index + 0 - 5 ]; }; method._rejectAt = function Promise$_rejectAt( index ) { ASSERT(((typeof index) === "number"), "typeof index === \u0022number\u0022"); ASSERT((index >= 0), "index >= 0"); ASSERT((index < this._length()), "index < this._length()"); ASSERT(((index % 5) === 0), "index % CALLBACK_SIZE === 0"); if( index === 0 ) return this._reject0; return this[ index + 1 - 5 ]; }; method._progressAt = function Promise$_progressAt( index ) { ASSERT(((typeof index) === "number"), "typeof index === \u0022number\u0022"); ASSERT((index >= 0), "index >= 0"); ASSERT((index < this._length()), "index < this._length()"); ASSERT(((index % 5) === 0), "index % CALLBACK_SIZE === 0"); if( index === 0 ) return this._progress0; return this[ index + 2 - 5 ]; }; var fulfiller = new Function("p", "'use strict';return function Promise$_fulfiller(a){ p.fulfill( a ); }" ); var rejecter = new Function("p", "'use strict';return function Promise$_rejecter(a){ p.reject( a ); }" ); method._resolveResolver = function Promise$_resolveResolver( resolver ) { ASSERT(((typeof resolver) === "function"), "typeof resolver === \u0022function\u0022"); this._setTrace( this._resolveResolver ); var p = new PromiseResolver( this ); this._pushContext(); var r = tryCatch2( resolver, this, fulfiller( p ), rejecter( p ) ); this._popContext(); if( r === errorObj ) { p.reject( r.e ); } }; method._addCallbacks = function Promise$_addCallbacks( fulfill, reject, progress, promise, receiver ) { fulfill = typeof fulfill === "function" ? fulfill : void 0; reject = typeof reject === "function" ? reject : void 0; progress = typeof progress === "function" ? progress : void 0; var index = this._length(); if( index === 0 ) { this._fulfill0 = fulfill; this._reject0 = reject; this._progress0 = progress; this._promise0 = promise; this._receiver0 = receiver; this._setLength( index + 5 ); return index; } this[ index - 5 + 0 ] = fulfill; this[ index - 5 + 1 ] = reject; this[ index - 5 + 2 ] = progress; this[ index - 5 + 3 ] = promise; this[ index - 5 + 4 ] = receiver; this._setLength( index + 5 ); return index; }; method._callFast = function Promise$_callFast( propertyName ) { return this._then( getFunction( propertyName ), void 0, void 0, void 0, void 0, this.call ); }; method._callSlow = function Promise$_callSlow( propertyName, args ) { ASSERT(isArray(args), "isArray( args )"); ASSERT((args.length > 0), "args.length > 0"); return this._then( function( obj ) { return obj[propertyName].apply( obj, args ); }, void 0, void 0, void 0, void 0, this.call ); }; method._resolveLast = function Promise$_resolveLast( index ) { ASSERT(((typeof index) === "number"), "typeof index === \u0022number\u0022"); ASSERT((index >= 0), "index >= 0"); ASSERT((index < this._length()), "index < this._length()"); var promise = this._promiseAt( index ); var receiver = this._receiverAt( index ); var fn; if( this.isFulfilled() ) { fn = this._fulfillAt( index ); } else if( this.isRejected() ) { fn = this._rejectAt( index ); } else unreachable(); var obj = this._resolvedValue; var ret = obj; if( fn !== void 0 ) { this._resolvePromise( fn, receiver, obj, promise ); } else if( this.isFulfilled() ) { promise._fulfill( ret ); } else { promise._reject( ret ); } }; method._spreadSlowCase = function Promise$_spreadSlowCase( targetFn, promise, values ) { promise._assumeStateOf( Promise.all( values )._then( targetFn, void 0, void 0, APPLY, void 0, this._spreadSlowCase ), false ); }; function cast( obj ) { if( isObject( obj ) ) { if( obj instanceof Promise ) { return obj; } var ref = { ref: null, promise: null }; if( thenable.is( obj, ref ) ) { if( ref.promise != null ) { return ref.promise; } var resolver = Promise.pending(); var result = ref.ref; if( result === errorObj ) { resolver.reject( result.e ); return resolver.promise; } thenable.addCache( obj, resolver.promise ); var called = false; var ret = tryCatch2( result, obj, function t( a ) { if( called ) return; called = true; async.invoke( thenable.deleteCache, thenable, obj ); var b = cast( a ); if( b === a ) { resolver.fulfill( a ); } else { b._then( resolver.fulfill, resolver.reject, void 0, resolver, void 0, t ); } }, function t( a ) { if( called ) return; called = true; async.invoke( thenable.deleteCache, thenable, obj ); resolver.reject( a ); }); if( ret === errorObj && !called ) { resolver.reject( ret.e ); async.invoke( thenable.deleteCache, thenable, obj ); } return resolver.promise; } } return obj; } method._resolveThenable = function Promise$_resolveThenable( x, ref ) { if( ref.promise != null ) { this._assumeStateOf( ref.promise, true ); return; } if( ref.ref === errorObj ) { this._attachExtraTrace( ref.ref.e ); async.invoke( this._reject, this, ref.ref.e ); } else { thenable.addCache( x, this ); var then = ref.ref; var localX = x; var localP = this; var key = {}; var called = false; var t = function t( v ) { if( called && this !== key ) return; called = true; var b = cast( v ); if( b !== v || ( b instanceof Promise && b.isPending() ) ) { b._then( t, r, void 0, key, void 0, t); return; } var fn = localP._fulfill; if( b instanceof Promise ) { var fn = b.isFulfilled() ? localP._fulfill : localP._reject; ASSERT(b.isResolved(), "b.isResolved()"); v = v._resolvedValue; b = cast( v ); ASSERT(((b instanceof Promise) || (b === v)), "b instanceof Promise || b === v"); if( b !== v || ( b instanceof Promise && b !== v ) ) { b._then( t, r, void 0, key, void 0, t); return; } } async.invoke( fn, localP, v ); async.invoke( thenable.deleteCache, thenable, localX ); }; var r = function r( v ) { if( called && this !== key ) return; called = true; var b = cast( v ); if( b !== v || ( b instanceof Promise && b.isPending() ) ) { b._then( t, r, void 0, key, void 0, t); return; } var fn = localP._reject; if( b instanceof Promise ) { var fn = b.isFulfilled() ? localP._fulfill : localP._reject; ASSERT(b.isResolved(), "b.isResolved()"); v = v._resolvedValue; b = cast( v ); if( b !== v || ( b instanceof Promise && b.isPending() ) ) { b._then( t, r, void 0, key, void 0, t); return; } } async.invoke( fn, localP, v ); async.invoke( thenable.deleteCache, thenable, localX ); }; var threw = tryCatch2( then, x, t, r); if( threw === errorObj && !called ) { this._attachExtraTrace( threw.e ); async.invoke( this._reject, this, threw.e ); async.invoke( thenable.deleteCache, thenable, x ); } } }; method._tryThenable = function Promise$_tryThenable( x ) { var ref; if( !thenable.is( x, ref = {ref: null, promise: null} ) ) { return false; } this._resolveThenable( x, ref ); return true; }; var ignore = CatchFilter.prototype.doFilter; method._resolvePromise = function Promise$_resolvePromise( onFulfilledOrRejected, receiver, value, promise ) { if( isError( value ) ) { value.__handled = true; } if( !isPromise( promise ) ) { return onFulfilledOrRejected.call( receiver, value, promise ); } var x; if( receiver === APPLY ) { if( isArray( value ) ) { for( var i = 0, len = value.length; i < len; ++i ) { if( isPromise( value[i] ) ) { this._spreadSlowCase( onFulfilledOrRejected, promise, value ); return; } } promise._pushContext(); x = tryCatchApply( onFulfilledOrRejected, value ); } else { this._spreadSlowCase( onFulfilledOrRejected, promise, value ); return; } } else { promise._pushContext(); x = tryCatch1( onFulfilledOrRejected, receiver, value ); } promise._popContext(); if( x === errorObj ) { if( onFulfilledOrRejected !== ignore ) { promise._attachExtraTrace( x.e ); } async.invoke( promise._reject, promise, x.e ); } else if( x === promise ) { async.invoke( promise._reject, promise, new TypeError( "Circular thenable chain" ) ); } else { if( promise._tryAssumeStateOf( x, true ) ) { return; } else if( thenable.couldBe( x ) ) { if( promise._length() === 0 ) { promise._resolvedValue = x; promise._setDelegated(); return; } else if( promise._tryThenable( x ) ) { return; } } async.invoke( promise._fulfill, promise, x ); } }; method._assumeStateOf = function Promise$_assumeStateOf( promise, mustAsync ) { ASSERT(isPromise(promise), "isPromise( promise )"); ASSERT(((typeof mustAsync) === "boolean"), "typeof mustAsync === \u0022boolean\u0022"); ASSERT((this._isFollowingOrFulfilledOrRejected() === false), "this._isFollowingOrFulfilledOrRejected() === false"); this._setFollowing(); if( promise.isPending() ) { if( promise._cancellable() ) { this._cancellationParent = promise; } promise._then( this._resolveFulfill, this._resolveReject, this._resolveProgress, this, void 0, this._tryAssumeStateOf ); } else if( promise.isFulfilled() ) { if( mustAsync ) async.invoke( this._resolveFulfill, this, promise._resolvedValue ); else this._resolveFulfill( promise._resolvedValue ); } else { if( mustAsync ) async.invoke( this._resolveReject, this, promise._resolvedValue ); else this._resolveReject( promise._resolvedValue ); } if( longStackTraces && promise._traceParent == null ) { promise._traceParent = this; } }; method._tryAssumeStateOf = function Promise$_tryAssumeStateOf( value, mustAsync ) { if( !isPromise( value ) || this._isFollowingOrFulfilledOrRejected() ) return false; this._assumeStateOf( value, mustAsync ); return true; }; method._attachExtraTrace = function Promise$_attachExtraTrace( error ) { if( longStackTraces && isError( error ) ) { var promise = this; var stack = error.stack.split("\n"); var headerLineCount = 1; while( promise != null && promise._trace != null ) { stack = CapturedTrace.combine( stack, promise._trace.stack.split( "\n" ) ); promise = promise._traceParent; } var max = Error.stackTraceLimit + headerLineCount; var len = stack.length; if( len > max ) { stack.length = max; } if( stack.length <= headerLineCount ) { error.stack = "(No stack trace)"; } else { error.stack = stack.join("\n"); } } }; method._notifyUnhandledRejection = function Promise$_notifyUnhandledRejection( reason ) { if( !reason.__handled ) { reason.__handled = true; CapturedTrace.possiblyUnhandledRejection( reason ); } }; method._unhandledRejection = function Promise$_unhandledRejection( reason ) { if( !reason.__handled ) { async.invokeLater( this._notifyUnhandledRejection, this, reason ); } }; method._cleanValues = function Promise$_cleanValues() { this._cancellationParent = void 0; }; method._fulfill = function Promise$_fulfill( value ) { if( this._isFollowingOrFulfilledOrRejected() ) return; this._resolveFulfill( value ); }; method._reject = function Promise$_reject( reason ) { if( this._isFollowingOrFulfilledOrRejected() ) return; this._resolveReject( reason ); }; method._progress = function Promise$_progress( progressValue ) { if( this._isFollowingOrFulfilledOrRejected() ) return; this._resolveProgress( progressValue ); }; method._resolveFulfill = function Promise$_resolveFulfill( value ) { ASSERT(this.isPending(), "this.isPending()"); this._cleanValues(); this._setFulfilled(); this._resolvedValue = value; var len = this._length(); for( var i = 0; i < len; i+= 5 ) { var fn = this._fulfillAt( i ); var promise = this._promiseAt( i ); var receiver = this._receiverAt( i ); if( fn !== void 0 ) { this._resolvePromise( fn, receiver, value, promise ); } else { async.invoke( promise._fulfill, promise, value ); } } }; method._resolveReject = function Promise$_resolveReject( reason ) { ASSERT(this.isPending(), "this.isPending()"); this._cleanValues(); this._setRejected(); this._resolvedValue = reason; var len = this._length(); var rejectionWasHandled = false; for( var i = 0; i < len; i+= 5 ) { var fn = this._rejectAt( i ); var promise = this._promiseAt( i ); if( fn !== void 0 ) { rejectionWasHandled = true; this._resolvePromise( fn, this._receiverAt( i ), reason, promise ); } else { if( !rejectionWasHandled ) rejectionWasHandled = promise._length() > 0; async.invoke( promise._reject, promise, reason ); } } if( !rejectionWasHandled && isError( reason ) && CapturedTrace.possiblyUnhandledRejection !== void 0 ) { if( reason.__handled !== true ) { reason.__handled = false; async.invoke( this._unhandledRejection, this, reason ); } } }; method._resolveProgress = function Promise$_resolveProgress( progressValue ) { ASSERT(this.isPending(), "this.isPending()"); var len = this._length(); for( var i = 0; i < len; i += 5 ) { var fn = this._progressAt( i ); var promise = this._promiseAt( i ); if( !isPromise( promise ) ) { fn.call( this._receiverAt( i ), progressValue, promise ); continue; } var ret = progressValue; if( fn !== void 0 ) { this._pushContext(); ret = tryCatch1( fn, this._receiverAt( i ), progressValue ); this._popContext(); if( ret === errorObj ) { if( ret.e != null && ret.e.name === "StopProgressPropagation" ) { ret.e.__handled = true; } else { promise._attachExtraTrace( ret.e ); async.invoke( promise._progress, promise, ret.e ); } } else if( isPromise( ret ) ) { ret._then( promise._progress, null, null, promise, void 0, this._progress ); } else { async.invoke( promise._progress, promise, ret ); } } else { async.invoke( promise._progress, promise, ret ); } } }; var contextStack = []; method._peekContext = function Promise$_peekContext() { var lastIndex = contextStack.length - 1; if( lastIndex >= 0 ) { return contextStack[ lastIndex ]; } return void 0; }; method._pushContext = function Promise$_pushContext() { if( !longStackTraces ) return; contextStack.push( this ); }; method._popContext = function Promise$_popContext() { if( !longStackTraces ) return; contextStack.pop(); }; Promise._all = function Promise$_All( promises, PromiseArray, caller ) { ASSERT(((typeof PromiseArray) === "function"), "typeof PromiseArray === \u0022function\u0022"); if( isPromise( promises ) || isArray( promises ) ) { return new PromiseArray( promises, typeof caller === "function" ? caller : Promise$_All ); } throw new TypeError("expecting an array or a promise"); }; if( !CapturedTrace.isSupported() ) { Promise.longStackTraces = void 0; CapturedTrace.possiblyUnhandledRejection = void 0; Promise.onPossiblyUnhandledRejection = void 0; longStackTraces = false; } return Promise;})(); var PromiseArray = (function() { function nullToUndefined( val ) { return val === null ? void 0 : val; } var hasOwn = {}.hasOwnProperty; var empty = []; function isPromise( obj ) { if( typeof obj !== "object" ) return false; return obj instanceof Promise; } var Arr = Array; var isArray = Arr.isArray || function( obj ) { return obj instanceof Arr; }; function PromiseArray( values, caller ) { this._values = values; this._resolver = Promise.pending( caller ); this._length = 0; this._totalResolved = 0; this._init( void 0, empty ); } var method = PromiseArray.prototype; method.length = function PromiseArray$length() { return this._length; }; method.promise = function PromiseArray$promise() { return this._resolver.promise; }; method._init = function PromiseArray$_init( _, fulfillValueIfEmpty ) { var values = this._values; if( isPromise( values ) ) { if( values.isPending() ) { values._then( this._init, this._reject, void 0, this, fulfillValueIfEmpty, this.constructor ); return; } else if( values.isRejected() ) { this._reject( values._resolvedValue ); return; } else { values = values._resolvedValue; if( !isArray( values ) ) { this._fulfill( nullToUndefined( fulfillValueIfEmpty ) ); return; } this._values = values; } } if( !values.length ) { this._fulfill( nullToUndefined( fulfillValueIfEmpty ) ); return; } var len = values.length; var newLen = len; var newValues = new Array( len ); for( var i = 0; i < len; ++i ) { var promise = values[i]; if( promise === void 0 && !hasOwn.call( values, i ) ) { newLen--; continue; } promise = Promise.cast( promise ); promise._then( this._promiseFulfilled, this._promiseRejected, this._promiseProgressed, this, Integer.get( i ), this.constructor ); newValues[i] = promise; } this._values = newValues; this._length = newLen; }; method._isResolved = function PromiseArray$_isResolved() { return this._values === null; }; method._fulfill = function PromiseArray$_fulfill( value ) { ASSERT((! this._isResolved()), "!this._isResolved()"); this._values = null; this._resolver.fulfill( value ); }; method._reject = function PromiseArray$_reject( reason ) { ASSERT((! this._isResolved()), "!this._isResolved()"); this._values = null; this._resolver.reject( reason ); }; method._promiseProgressed = function PromiseArray$_promiseProgressed( progressValue, index ) { if( this._isResolved() ) return; ASSERT(isArray(this._values), "isArray( this._values )"); this._resolver.progress({ index: index.valueOf(), value: progressValue }); }; method._promiseFulfilled = function PromiseArray$_promiseFulfilled( value, index ) { if( this._isResolved() ) return; ASSERT(isArray(this._values), "isArray( this._values )"); ASSERT((index instanceof Integer), "index instanceof Integer"); this._values[ index.valueOf() ] = value; var totalResolved = ++this._totalResolved; if( totalResolved >= this._length ) { this._fulfill( this._values ); } }; method._promiseRejected = function PromiseArray$_promiseRejected( reason ) { if( this._isResolved() ) return; ASSERT(isArray(this._values), "isArray( this._values )"); this._totalResolved++; this._reject( reason ); }; function Integer( value ) { this._value = value; } Integer.prototype.valueOf = function Integer$valueOf() { return this._value; }; Integer.get = function Integer$get( i ) { if( i < 256 ) { return ints[i]; } return new Integer(i); }; var ints = []; for( var i = 0; i < 256; ++i ) { ints.push( new Integer(i) ); } return PromiseArray;})(); var SettledPromiseArray = (function() { function SettledPromiseArray( values, caller ) { this.constructor$( values, caller ); } var method = inherits( SettledPromiseArray, PromiseArray ); method._promiseResolved = function SettledPromiseArray$_promiseResolved( index, inspection ) { ASSERT(((typeof index) === "number"), "typeof index === \u0022number\u0022"); this._values[ index ] = inspection; var totalResolved = ++this._totalResolved; if( totalResolved >= this._length ) { this._fulfill( this._values ); } }; var throwawayPromise = new Promise()._setTrace(); method._promiseFulfilled = function SettledPromiseArray$_promiseFulfilled( value, index ) { if( this._isResolved() ) return; var ret = new PromiseInspection( throwawayPromise ); ret._bitField = 268435456; ret._resolvedValue = value; this._promiseResolved( index.valueOf(), ret ); }; method._promiseRejected = function SettledPromiseArray$_promiseRejected( reason, index ) { if( this._isResolved() ) return; var ret = new PromiseInspection( throwawayPromise ); ret._bitField = 134217728; ret._resolvedValue = reason; this._promiseResolved( index.valueOf(), ret ); }; return SettledPromiseArray;})(); var AnyPromiseArray = (function() { function AnyPromiseArray( values, caller ) { this.constructor$( values, caller ); } var method = inherits( AnyPromiseArray, PromiseArray ); method._init = function AnyPromiseArray$_init() { this._init$( void 0, null ); }; method._promiseFulfilled = function AnyPromiseArray$_promiseFulfilled( value ) { if( this._isResolved() ) return; ++this._totalResolved; this._fulfill( value ); }; method._promiseRejected = function AnyPromiseArray$_promiseRejected( reason, index ) { if( this._isResolved() ) return; var totalResolved = ++this._totalResolved; this._values[ index.valueOf() ] = reason; if( totalResolved >= this._length ) { this._reject( this._values ); } }; return AnyPromiseArray;})(); var SomePromiseArray = (function() { function SomePromiseArray( values, caller ) { this.constructor$( values, caller ); } var method = inherits( SomePromiseArray, PromiseArray ); method._init = function SomePromiseArray$_init() { this._init$( void 0, [] ); this._howMany = 0; this._rejected = 0; this._rejectionValues = new Array( this.length() ); this._resolutionValues = new Array( this.length() ); if( this._isResolved() ) return; if( this._howMany > this._canPossiblyFulfill() ) { this._reject( [] ); } }; method._canPossiblyFulfill = function SomePromiseArray$_canPossiblyFulfill() { return this._totalResolved - this._rejected + ( this.length() - this._totalResolved ); }; method._promiseFulfilled = function SomePromiseArray$_promiseFulfilled( value ) { if( this._isResolved() ) return; var totalResolved = this._totalResolved; this._resolutionValues[ totalResolved ] = value; this._totalResolved = totalResolved + 1; if( totalResolved + 1 === this._howMany ) { this._resolutionValues.length = this._howMany; this._fulfill( this._resolutionValues ); this._resolutionValues = this._rejectionValues = null; } }; method._promiseRejected = function SomePromiseArray$_promiseRejected( reason ) { if( this._isResolved() ) return; this._rejectionValues[ this._rejected ] = reason; this._rejected++; this._totalResolved++; if( this._howMany > this._canPossiblyFulfill() ) { this._rejectionValues.length = this._rejected; this._reject( this._rejectionValues ); this._resolutionValues = this._rejectionValues = null; } }; return SomePromiseArray;})(); var PromiseInspection = (function() { function PromiseInspection( promise ) { this._bitField = promise._bitField; this._resolvedValue = promise.isResolved() ? promise._resolvedValue : void 0; } var method = PromiseInspection.prototype; method.isFulfilled = function PromiseInspection$isFulfilled() { return ( this._bitField & 268435456 ) > 0; }; method.isRejected = function PromiseInspection$isRejected() { return ( this._bitField & 134217728 ) > 0; }; method.isPending = function PromiseInspection$isPending() { return ( this._bitField & 402653184 ) === 0; }; method.value = function PromiseInspection$value() { if( !this.isFulfilled() ) { throw new TypeError( "cannot get fulfillment value of a non-fulfilled promise"); } return this._resolvedValue; }; method.error = function PromiseInspection$error() { if( !this.isRejected() ) { throw new TypeError( "cannot get rejection reason of a non-rejected promise"); } return this._resolvedValue; }; return PromiseInspection;})(); var PromiseResolver = (function() { function PromiseResolver( promise ) { this.promise = promise; } var method = PromiseResolver.prototype; method.toString = function PromiseResolver$toString() { return "[object PromiseResolver]"; }; method.fulfill = function PromiseResolver$fulfill( value ) { if( this.promise._tryAssumeStateOf( value, false ) ) { return; } async.invoke( this.promise._fulfill, this.promise, value ); }; method.reject = function PromiseResolver$reject( reason ) { this.promise._attachExtraTrace( reason ); async.invoke( this.promise._reject, this.promise, reason ); }; method.progress = function PromiseResolver$progress( value ) { async.invoke( this.promise._progress, this.promise, value ); }; method.cancel = function PromiseResolver$cancel() { async.invoke( this.promise.cancel, this.promise, void 0 ); }; method.timeout = function PromiseResolver$timeout() { this.reject( new TimeoutError( "timeout" ) ); }; method.isResolved = function PromiseResolver$isResolved() { return this.promise.isResolved(); }; method.toJSON = function PromiseResolver$toJSON() { return this.promise.toJSON(); }; return PromiseResolver;})(); if( typeof module !== "undefined" && module.exports ) { module.exports = Promise; } else if( typeof define === "function" && define.amd ) { define( "Promise", [], function(){return Promise;}); } else { global.Promise = Promise; } return Promise;})( new Function("return this")(), Function, Array, Error, Object ); ================================================ FILE: test/mocha/helpers/error.js ================================================ ================================================ FILE: test/mocha/helpers/reasons.js ================================================ "use strict"; // This module exports some valid rejection reason factories, keyed by human-readable versions of their names. var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var dummy = { dummy: "dummy" }; exports["`undefined`"] = function () { return undefined; }; exports["`null`"] = function () { return null; }; exports["`false`"] = function () { return false; }; exports["`0`"] = function () { return 0; }; exports["an error"] = function () { return new Error(); }; exports["an error without a stack"] = function () { var error = new Error(); delete error.stack; return error; }; exports["a date"] = function () { return new Date(); }; exports["an object"] = function () { return {}; }; exports["an always-pending thenable"] = function () { return { then: function () { } }; }; exports["a fulfilled promise"] = function () { return fulfilled(dummy); }; exports["a rejected promise"] = function () { return rejected(dummy); }; ================================================ FILE: test/mocha/helpers/testThreeCases.js ================================================ "use strict"; var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var pending = adapter.pending; function success(done) { return function() { done(); }; } function fail(done) { return function(err) { done(err); }; } function handlePromise(val, done) { if (val && typeof val.then === "function") { val.then(success(done), fail(done)); } } exports.testFulfilled = function (value, test) { specify("already-fulfilled", function (done) { handlePromise(test(fulfilled(value), done), done); }); specify("immediately-fulfilled", function (done) { var tuple = pending(); handlePromise(test(tuple.promise, done), done); tuple.fulfill(value); }); specify("eventually-fulfilled", function (done) { var tuple = pending(); handlePromise(test(tuple.promise, done), done); setTimeout(function () { tuple.fulfill(value); }, 1); }); }; exports.testRejected = function (reason, test) { specify("already-rejected", function (done) { handlePromise(test(rejected(reason), done), done); }); specify("immediately-rejected", function (done) { var tuple = pending(); handlePromise(test(tuple.promise, done), done); tuple.reject(reason); }); specify("eventually-rejected", function (done) { var tuple = pending(); handlePromise(test(tuple.promise, done), done); setTimeout(function () { tuple.reject(reason); }, 1); }); }; ================================================ FILE: test/mocha/helpers/thenables.js ================================================ "use strict"; var adapter = global.adapter; var fulfilled = adapter.fulfilled; var rejected = adapter.rejected; var pending = adapter.pending; var other = { other: "other" }; // a value we don't want to be strict equal to exports.fulfilled = { "a synchronously-fulfilled custom thenable": function (value) { return { then: function (onFulfilled) { onFulfilled(value); } }; }, "an asynchronously-fulfilled custom thenable": function (value) { return { then: function (onFulfilled) { setTimeout(function () { onFulfilled(value); }, 1); } }; }, "a synchronously-fulfilled one-time thenable": function (value) { var numberOfTimesThenRetrieved = 0; var ret = Object.create(null, { then: { get: function () { if (numberOfTimesThenRetrieved === 0) { ++numberOfTimesThenRetrieved; return function (onFulfilled) { onFulfilled(value); }; } return null; } } }); return ret; }, "a thenable that tries to fulfill twice": function (value) { return { then: function (onFulfilled) { onFulfilled(value); onFulfilled(other); } }; }, "a thenable that fulfills but then throws": function (value) { return { then: function (onFulfilled) { onFulfilled(value); throw other; } }; }, "an already-fulfilled promise": function (value) { return fulfilled(value); }, "an eventually-fulfilled promise": function (value) { var tuple = pending(); setTimeout(function () { tuple.fulfill(value); }, 1); return tuple.promise; } }; exports.rejected = { "a synchronously-rejected custom thenable": function (reason) { return { then: function (onFulfilled, onRejected) { onRejected(reason); } }; }, "an asynchronously-rejected custom thenable": function (reason) { return { then: function (onFulfilled, onRejected) { setTimeout(function () { onRejected(reason); }, 1); } }; }, "a synchronously-rejected one-time thenable": function (reason) { var numberOfTimesThenRetrieved = 0; return Object.create(null, { then: { get: function () { if (numberOfTimesThenRetrieved === 0) { ++numberOfTimesThenRetrieved; return function (onFulfilled, onRejected) { onRejected(reason); }; } return null; } } }); }, "a thenable that immediately throws in `then`": function (reason) { return { then: function () { throw reason; } }; }, "an object with a throwing `then` accessor": function (reason) { return Object.create(null, { then: { get: function () { throw reason; } } }); }, "an already-rejected promise": function (reason) { var ret = rejected(reason); ret.caught(function(){}); return ret; }, "an eventually-rejected promise": function (reason) { var tuple = pending(); setTimeout(function () { tuple.reject(reason); }, 1); return tuple.promise; } }; ================================================ FILE: test/mocha/helpers/util.js ================================================ var assert = require("assert"); var token = {}; module.exports = { awaitGlobalException: function(fn) { function replaceListeners(by) { var single = typeof by === "function"; if (process.title === "browser") { var original = window.onerror; window.onerror = single ? function(message, file, line, column, e) { return by(e); } : by[0]; return [original]; } else { var original = process.listeners("uncaughtException"); process.removeAllListeners("uncaughtException"); if (single) by = [by]; by.forEach(function(listener) { process.on("uncaughtException", listener); }); return original; } } return new Promise(function(resolve, reject) { var listeners = replaceListeners(function(e) { var err; var ret; try { ret = fn(e); } catch (e) { err = e; } if (!err && ret === false) return; replaceListeners(listeners); Promise.delay(1).then(function() { if (err) reject(err); resolve(); }); }); }); }, awaitLateQueue: function(fn) { return new Promise(function(res, rej) { Promise._async.invokeLater(function() { try { var result = fn(); res(result); } catch(e) { rej(e); } }, null, null); }); }, awaitProcessExit: function(fn) { if (typeof process !== "undefined" && typeof process.execPath === "string") { var exit; return new Promise(function(resolve, reject) { exit = process.exit; process.exit = function(code) { try { assert(code != 0); fn(); resolve(); } catch (e) { reject(e); } }; }).lastly(function() { process.exit = exit; }); } else { return Promise.delay(1); } }, addDeferred: function(Promise) { Promise.defer = Promise.pending = function() { var ret = {}; ret.promise = new Promise(function(resolve, reject) { ret.resolve = ret.fulfill = resolve; ret.reject = reject; }); return ret; }; return Promise; }, returnToken: function() { return token; }, assertToken: function(val) { assert.strictEqual(token, val); }, getSpy: function() { var resolve, reject; var promise = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); var ret = function(fn) { ret.callback = fn; return ret.node; }; ret.promise = promise; ret.node = function() { try { ret.callback.apply(this, arguments); resolve(); } catch (e) { reject(e); } }; return ret; }, awaitDomainException: function(onError, fn) { var domain; var resolve, reject; var promise = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); domain = require('domain').create(); domain.on("error", function(e) { try { onError(e); resolve(); } catch (err) { reject(err); } }); domain.run(fn); return promise; }, onUnhandledFail: function(testFunction) { Promise._unhandledRejectionClear(); return new Promise(function(resolve, reject) { var err = new Error("Reporting handled rejection as unhandled from: " + testFunction); Promise.onPossiblyUnhandledRejection(function() { reject(err); }); Promise.delay(150).then(function() { Promise._unhandledRejectionCheck(); resolve(); }); }).lastly(function() { Promise.onPossiblyUnhandledRejection(null); }); }, onUnhandledSucceed: function(testAgainst, count) { return new Promise(function(resolveTest, reject) { var total = typeof count === "number" ? count : 1; var cur = 0; function resolve(e) { cur++; if (cur >= total) { resolveTest(e); } } Promise.onPossiblyUnhandledRejection(function(e){ if (testAgainst !== undefined) { try { if (typeof testAgainst === "function") { assert(testAgainst(e)); } else { assert.equal(testAgainst, e); } resolve(e); } catch (e) { reject(e); } } else { resolve(e); } }); Promise.delay(50).then(function() { Promise._unhandledRejectionCheck(); return Promise.delay(1); }).then(function() { var message = "Expected onPossiblyUnhandledRejection to be called " + total + " times but it was only called " + cur + " times"; reject(new Error(message)); }); }).lastly(function() { Promise.onPossiblyUnhandledRejection(null); }); }, //Used in expressions like: onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); //If strict mode is supported NFEs work, if it is not, NFEs don't work but arguments.callee does isStrictModeSupported: (function() { try { new Function("'use strict'; with({});"); return false; } catch (e) { return true; } })(), noop: function(v) { return v; }, isSubset: function(subset, superset) { var i, subsetLen; subsetLen = subset.length; if (subsetLen > superset.length) { return false; } for(i = 0; i -1; }, fakeResolved: function(val) { return { then: function(callback) { return fakeResolved(callback ? callback(val) : val); } }; }, fakeRejected: function(reason) { return { then: function(callback, errback) { return errback ? fakeResolved(errback(reason)) : fakeRejected(reason); } }; }, assertFulfilled: function(p, v) { assert.strictEqual(p.value(), v); }, assertRejected: function(p, v) { assert.strictEqual(p.error(), v); }, ecmaScript6Collections: (typeof Set === "function" && typeof Symbol !== "undefined" && Symbol.iterator && typeof ((new Set())[Symbol.iterator]().next) === "function"), ecmaScript5: (function() {"use strict" return this === undefined; })(), isNodeJS: typeof process !== "undefined" && typeof process.execPath === "string" }; if (module.exports.isNodeJS) { var version = process.versions.node.split(".").map(Number); module.exports.isRecentNode = version[0] > 0; module.exports.isOldNode = !module.exports.isRecentNode; } else { module.exports.isOldNode = false; module.exports.isRecentNode = false; } ================================================ FILE: test/mocha/is.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.is", function() { it("should return true for trusted promise", function() { assert.strictEqual(Promise.is(new Promise(function(){})), true); }); it("should return false for untrusted promise", function() { assert.strictEqual(Promise.is({ then: function() {} }), false); }); }); ================================================ FILE: test/mocha/join.js ================================================ "use strict"; /* Based on When.js tests Open Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2011 Brian Cavalier 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.join-test", function () { specify("should resolve empty input", function() { return Promise.join().then( function(result) { assert.deepEqual(result, []); }, assert.fail ); }); specify("should join values", function() { return Promise.join(1, 2, 3).then( function(results) { assert.deepEqual(results, [1, 2, 3]); }, assert.fail ); }); specify("should join promises array", function() { return Promise.join(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)).then( function(results) { assert.deepEqual(results, [1, 2, 3]); }, assert.fail ); }); specify("should join mixed array", function() { return Promise.join(Promise.resolve(1), 2, Promise.resolve(3), 4).then( function(results) { assert.deepEqual(results, [1, 2, 3, 4]); }, assert.fail ); }); specify("should reject if any input promise rejects", function() { return Promise.join(Promise.resolve(1), Promise.reject(2), Promise.resolve(3)).then( assert.fail, function(err) { assert.deepEqual(err, 2); } ); }); specify("should call last argument as a spread function", function() { return Promise.join(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3), function(a, b, c) { assert(a === 1); assert(b === 2); assert(c === 3); }); }); specify("gh-227", function() { function a() { return Promise.join(Promise.resolve(1), function () { throw new Error(); }); } return a().then(assert.fail, function(e) {}); }); specify("should not pass the callback as argument, <5 arguments", function() { return Promise.join(1, 2, 3, function() { assert.strictEqual(arguments.length, 3); }); }); specify("should not pass the callback as argument >5 arguments", function() { return Promise.join(1, 2, 3, 4, 5, 6, 7, function() { assert.strictEqual(arguments.length, 7); }); }); specify("should ensure asynchronity", function() { var sync = false; Promise.join(Promise.resolve(1), Promise.resolve(2), function() { sync = true; }); assert.strictEqual(false, sync); }) }); ================================================ FILE: test/mocha/late_buffer_safety.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var isNodeJS = testUtils.isNodeJS; function async(cb){ return Promise.resolve().nodeify(cb); } if (isNodeJS) { describe("Late buffer", function() { specify("shouldn't stop at first error but continue consumption until everything is consumed", function(){ var length = 10; var l = length; var a = 0; while (l--){ async(function(){ throw (a++); }); } var errs = []; return testUtils.awaitGlobalException(function(e) { errs.push(e); if (errs.length === length) { var a = []; for (var i = 0, len = length; i < len; ++i) { a[i] = i; } assert.deepEqual(a, errs); } else { return false; } }); }); }); } ================================================ FILE: test/mocha/long_stack_traces.js ================================================ var assert = require("assert"); var testUtils = require("./helpers/util.js"); var assertLongTrace = require("./helpers/assert_long_trace.js"); var nodeVersion = typeof process !== "undefined" && typeof process.version === "string" ? process.version.replace(/[^0-9.]/g, "").split(".").map(Number) : [-1, -1, -1]; // Node's V8 captureStackTrace is completely broken - it returns different // results on different runs and sometimes causes this test to fail if (!Promise.hasLongStackTraces() || testUtils.isOldNode) return; describe(".then as context", function() { it("1 level", function() { return Promise.resolve().then(function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels", function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { throw new Error(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); it("1 level using promise reject with no stack", function() { return Promise.resolve().then(function() { var e; try {throw new Error()} catch (err){e = err;} e.stack; delete e.stack; return Promise.reject(e); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1, 1]); }); }); it("4 levels using promise reject", function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { var e; try {throw new Error()} catch (err){e = err;} return Promise.reject(e); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); it("Circular 1 level", function() { var i = 0; return (function circle() { if (i++ > 5) throw new Error() return Promise.resolve().then(circle); })().then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("Circular 4 levels", function() { var i = 0; return (function circle() { if (i++ > 5) throw new Error() return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(circle); }); }); }); })().then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); it("followers unaffected", function() { return Promise.resolve().then(function() { return new Promise(function(res) { res(Promise.delay(13).then(function() { return new Promise(function(res) { res(Promise.delay(13).then(function() { throw new Error(); })); }); })); }).then(assert.fail, function(e) { assertLongTrace(e, 5 + 1, [1, 1, 1, 1, 1]); throw new Error(); }); }).then(assert.fail, function(e) { assertLongTrace(e, 2 + 1, [1, 1]); }); }); it("3 distinct episodes of circularity with unique frames in between", function() { var i = 0; var j = 0; var k = 0; function circle1() { if (i++ > 5) return u1_1(); return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(circle1); }); }); }); } function circle2() { if (j++ > 5) return u2_1(); return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(circle2); }); }); }); } function circle3() { if (k++ > 5) return u3_1(); return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(circle3); }); }); }); } function u1_1() { return Promise.resolve().then(u1_2); } function u1_2() { return Promise.resolve().then(circle2); } function u2_1() { return Promise.resolve().then(u2_2); } function u2_2() { return Promise.resolve().then(circle3); } function u3_1() { return Promise.resolve().then(u3_2); } function u3_2() { return Promise.resolve().then(function() { throw new Error("The error"); }); } return circle1().then(assert.fail, function(e) { assertLongTrace(e, 1 + 1 + 1 + 1, [ 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1 ]); }); }); }); describe(".spread as context", function() { it("1 level", function() { return Promise.resolve([]).spread(function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels", function() { return Promise.resolve([]).spread(function() { return Promise.resolve([]).spread(function() { return Promise.resolve([]).spread(function() { return Promise.resolve([]).spread(function() { throw new Error(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); }); describe("constructor as context", function() { it("0 level", function() { return new Promise(function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 1, []); }); }); it("1 level", function() { return new Promise(function(res) { res(new Promise(function() { throw new Error(); })) }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [2]); }); }); it("0 level, no stack property", function() { return new Promise(function(_ ,rej) { var e = new Error(); e.stack; delete e.stack; rej(e); }).then(assert.fail, function(e) { assertLongTrace(e, 1, [1]); }); }); it("1 level, no stack property", function() { return new Promise(function(res) { res(new Promise(function(_, rej) { var e = new Error(); e.stack; delete e.stack; rej(e); })) }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1, 1]); }); }); it("4 levels", function() { return new Promise(function(res) { res(new Promise(function(res) { res(new Promise(function(res) { res(new Promise(function(res) { res(new Promise(function(res) { throw new Error(); })); })); })); })); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [2, 1, 1, 1]); }); }); }); describe(".join as context", function() { it("0 level", function() { var err; try {throw new Error(); } catch(e) {err = e;}; return Promise.join(1, 2, Promise.reject(err), function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 0 + 1, []); }); }); it("1 level", function() { return Promise.join(1, 2, 3, function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels", function() { return Promise.join(1, 2, 3, function() { return Promise.join(1, 2, 3, function() { return Promise.join(1, 2, 3, function() { return Promise.join(1, 2, 3, function() { throw new Error(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); }); describe(".map as context", function() { it("1 level", function() { return Promise.map([1,2,3], function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels", function() { return Promise.map([1,2,3], function() { return Promise.map([1,2,3], function() { return Promise.map([1,2,3], function() { return Promise.map([1,2,3], function() { throw new Error(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); }); describe(".reduce as context", function() { it("1 level", function() { return Promise.reduce([1,2,3], function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels", function() { return Promise.reduce([1,2,3], function() { return Promise.reduce([1,2,3], function() { return Promise.reduce([1,2,3], function() { return Promise.reduce([1,2,3], function() { throw new Error(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); }); describe(".method as context", function() { it("1 level", function() { return Promise.method(function() { throw new Error(); })().then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels", function() { var second = Promise.method(function() { return third(); }); var third = Promise.method(function() { return fourth(); }); var fourth = Promise.method(function() { throw new Error(); }); return Promise.method(function() { return second(); })().then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [[1,2], 1, 1, 1]); }); }); }); describe(".try as context", function() { it("1 level", function() { return Promise.attempt(function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels", function() { return Promise.attempt(function() { return Promise.attempt(function() { return Promise.attempt(function() { return Promise.attempt(function() { throw new Error(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); }); describe(".using as context", function() { it("0 level", function() { var err; try {throw new Error(); } catch(e) {err = e}; return Promise.using(1, 2, Promise.reject(err), function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 0 + 1, []); }); }); it("1 level", function() { return Promise.using(1, 2, 3, function() { throw new Error(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels", function() { return Promise.using(1, 2, 3, function() { return Promise.using(1, 2, 3, function() { return Promise.using(1, 2, 3, function() { return Promise.using(1, 2, 3, function() { throw new Error(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); }); describe("Long stack traces from thenable rejections", function() { var es5 = (function(){"use strict"; return this})() === undefined; // Todo, for 100% coverage thenables should be tested with every // feature, not just then var syncRej = function() { return { then: function(_, rej) { rej(new Error()); } }; }; var asyncRej = function() { return { then: function(_, rej) { setTimeout(function() { rej(new Error()); }, 1); } }; }; var throwRej = function() { return { then: function(_, rej) { throw(new Error()); } }; }; var thenGetRej = function() { var ret = {}; Object.defineProperty(ret, "then", { get: function() { throw new Error() } }); return ret; }; it("1 level sync reject", function() { return Promise.resolve().then(function() { return syncRej(); }).then(assert.fail, function(e) { assertLongTrace(e, 1+1, [1]); }); }); it("4 levels sync reject", function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return syncRej(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); it("1 level async reject", function() { return Promise.resolve().then(function() { return asyncRej(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels async reject", function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return asyncRej(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); it("1 level throw", function() { return Promise.resolve().then(function() { return throwRej(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels throw", function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return throwRej(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); it("1 level getter throw", function() { return Promise.resolve().then(function() { return thenGetRej(); }).then(assert.fail, function(e) { assertLongTrace(e, 1 + 1, [1]); }); }); it("4 levels getter throw", function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return Promise.resolve().then(function() { return thenGetRej(); }); }); }); }).then(assert.fail, function(e) { assertLongTrace(e, 4 + 1, [1, 1, 1, 1]); }); }); }); ================================================ FILE: test/mocha/map.js ================================================ "use strict"; /* Based on When.js tests Open Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2011 Brian Cavalier 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.map-test", function () { function mapper(val) { return val * 2; } function deferredMapper(val) { return Promise.delay(1, mapper(val)); } specify("should map input values array", function() { var input = [1, 2, 3]; return Promise.map(input, mapper).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should map input promises array", function() { var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; return Promise.map(input, mapper).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should map mixed input array", function() { var input = [1, Promise.resolve(2), 3]; return Promise.map(input, mapper).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should map input when mapper returns a promise", function() { var input = [1,2,3]; return Promise.map(input, deferredMapper).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should accept a promise for an array", function() { return Promise.map(Promise.resolve([1, Promise.resolve(2), 3]), mapper).then( function(result) { assert.deepEqual(result, [2,4,6]); }, assert.fail ); }); specify("should throw a TypeError when input promise does not resolve to an array", function() { return Promise.map(Promise.resolve(123), mapper).caught(TypeError, function(e){ }); }); specify("should map input promises when mapper returns a promise", function() { var input = [Promise.resolve(1),Promise.resolve(2),Promise.resolve(3)]; return Promise.map(input, mapper).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should reject when input contains rejection", function() { var input = [Promise.resolve(1), Promise.reject(2), Promise.resolve(3)]; return Promise.map(input, mapper).then( assert.fail, function(result) { assert(result === 2); } ); }); specify("should call mapper asynchronously on values array", function() { var calls = 0; function mapper(val) { calls++; } var input = [1, 2, 3]; var p = Promise.map(input, mapper); assert(calls === 0); return p.then(function() { assert(calls === 3); }); }); specify("should call mapper asynchronously on promises array", function() { var calls = 0; function mapper(val) { calls++; } var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; var p = Promise.map(input, mapper); assert(calls === 0); return p.then(function() { assert(calls === 3); }); }); specify("should call mapper asynchronously on mixed array", function() { var calls = 0; function mapper(val) { calls++; } var input = [1, Promise.resolve(2), 3]; var p = Promise.map(input, mapper); assert(calls === 0); return p.then(function() { assert(calls === 3); }); }); }); describe("Promise.map-test with concurrency", function () { var concurrency = {concurrency: 2}; function mapper(val) { return val * 2; } function deferredMapper(val) { return Promise.delay(1, mapper(val)); } specify("should map input values array with concurrency", function() { var input = [1, 2, 3]; return Promise.map(input, mapper, concurrency).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should map input promises array with concurrency", function() { var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; return Promise.map(input, mapper, concurrency).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should map mixed input array with concurrency", function() { var input = [1, Promise.resolve(2), 3]; return Promise.map(input, mapper, concurrency).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should map input when mapper returns a promise with concurrency", function() { var input = [1,2,3]; return Promise.map(input, deferredMapper, concurrency).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should accept a promise for an array with concurrency", function() { return Promise.map(Promise.resolve([1, Promise.resolve(2), 3]), mapper, concurrency).then( function(result) { assert.deepEqual(result, [2,4,6]); }, assert.fail ); }); specify("should resolve to empty array when input promise does not resolve to an array with concurrency", function() { return Promise.map(Promise.resolve(123), mapper, concurrency).caught(TypeError, function(e){ }); }); specify("should map input promises when mapper returns a promise with concurrency", function() { var input = [Promise.resolve(1),Promise.resolve(2),Promise.resolve(3)]; return Promise.map(input, mapper, concurrency).then( function(results) { assert.deepEqual(results, [2,4,6]); }, assert.fail ); }); specify("should reject when input contains rejection with concurrency", function() { var input = [Promise.resolve(1), Promise.reject(2), Promise.resolve(3)]; return Promise.map(input, mapper, concurrency).then( assert.fail, function(result) { assert(result === 2); } ); }); specify("should not have more than {concurrency} promises in flight", function() { var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var b = []; var now = Date.now(); var immediates = []; function immediate(index) { var resolve; var ret = new Promise(function(){resolve = arguments[0]}); immediates.push([ret, resolve, index]); return ret; } var lates = []; function late(index) { var resolve; var ret = new Promise(function(){resolve = arguments[0]}); lates.push([ret, resolve, index]); return ret; } function promiseByIndex(index) { return index < 5 ? immediate(index) : late(index); } function resolve(item) { item[1](item[2]); } var ret1 = Promise.map(array, function(value, index) { return promiseByIndex(index).then(function() { b.push(value); }); }, {concurrency: 5}); var ret2 = Promise.delay(100).then(function() { assert.strictEqual(0, b.length); immediates.forEach(resolve); return immediates.map(function(item){return item[0]}); }).delay(100).then(function() { assert.deepEqual(b, [0, 1, 2, 3, 4]); lates.forEach(resolve); }).delay(100).then(function() { assert.deepEqual(b, [0, 1, 2, 3, 4, 10, 9, 8, 7, 6 ]); lates.forEach(resolve); }).thenReturn(ret1).then(function() { assert.deepEqual(b, [0, 1, 2, 3, 4, 10, 9, 8, 7, 6, 5]); }); return Promise.all([ret1, ret2]); }); }); ================================================ FILE: test/mocha/method.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var obj = {}; var error = new Error(); var thrower = Promise.method(function() { throw error; });; var identity = Promise.method(function(val) { return val; }); var array = Promise.method(function() { return [].slice.call(arguments); }); var receiver = Promise.method(function() { return this; }); describe("Promise.method", function(){ specify("should reject when the function throws", function() { var async = false; var ret = thrower().then(assert.fail, function(e) { assert(async); assert(e === error); }); async = true; return ret; }); specify("should throw when the function is not a function", function() { try { Promise.method(null); } catch (e) { assert(e instanceof TypeError); return; } assert.fail(); }); specify("should call the function with the given receiver", function(){ var async = false; var ret = receiver.call(obj).then(function(val) { assert(async); assert(val === obj); }, assert.fail); async = true; return ret; }); specify("should call the function with the given value", function(){ var async = false; var ret = identity(obj).then(function(val) { assert(async); assert(val === obj); }, assert.fail); async = true; return ret; }); specify("should apply the function if given value is array", function(){ var async = false; var ret = array(1, 2, 3).then(function(val) { assert(async); assert.deepEqual(val, [1,2,3]); }, assert.fail); async = true; return ret; }); specify("should unwrap returned promise", function(){ var d = Promise.defer(); var ret = Promise.method(function(){ return d.promise; })().then(function(v){ assert(v === 3); }) setTimeout(function(){ d.fulfill(3); }, 1); return ret; }); specify("should unwrap returned thenable", function(){ return Promise.method(function(){ return { then: function(f, v) { f(3); } } })().then(function(v){ assert(v === 3); }); }); specify("should unwrap a following promise", function() { var resolveF; var f = new Promise(function() { resolveF = arguments[0]; }); var v = new Promise(function(f) { setTimeout(function() { f(3); }, 1); }); resolveF(v); return Promise.method(function(){ return f; })().then(function(v){ assert(v === 3); }); }); specify("zero arguments length should remain zero", function() { return Promise.method(function(){ assert(arguments.length === 0); })(); }); specify("should retain binding from returned promise", function() { var THIS = {}; return Promise.method(function() { return Promise.bind(THIS, 1); })().then(function(value) { assert.strictEqual(THIS, this); assert.strictEqual(1, value); }); }); }); ================================================ FILE: test/mocha/monitoring.js ================================================ var assert = require("assert"); var util = require("../../js/debug/util"); describe("monitoring: promise lifecycle events subscriptions", function() { var numCreated = 0; var numChained = 0; var numFulfilled = 0; var numRejected = 0; var on = null; var off = null; before(function(){ Promise.config({monitoring: true}); }); after(function(){ Promise.config({monitoring: false}); }); beforeEach(function(){ numCreated = 0; numChained = 0; numFulfilled = 0; numRejected = 0; }); function nodeOn(eventName, eventHandler) { process.on.call(process, eventName, eventHandler); } function nodeOff(eventName, eventHandler) { process.removeListener.call(process, eventName, eventHandler); } function browserSimpleOn(eventName, eventHandler) { eventName = "on" + eventName.toLowerCase(); self[eventName] = eventHandler; } function browserSimpleOff(eventName, eventHandler) { eventName = "on" + eventName.toLowerCase(); assert(self[eventName] === eventHandler); delete self[eventName]; } function browserDomOn (eventName, eventHandler) { self.addEventListener.call(self, eventName.toLowerCase(), eventHandler); } function browserDomOff (eventName, eventHandler) { self.removeEventListener.call(self, eventName.toLowerCase(), eventHandler); } function testCreated(onCreated) { on("promiseCreated", onCreated); var promise = new Promise(function(resolve){resolve()}); assert(numCreated === 1); promise = promise.then(function(){}); assert(numCreated === 2); off("promiseCreated", onCreated); promise.then(function(){}); assert(numCreated === 2); } function testChained(onChained) { on("promiseChained", onChained); var promise = new Promise(function(resolve){resolve()}); assert(numChained === 0); promise = promise.then(function(){}); assert(numChained === 1); off("promiseChained", onChained); promise.then(function(){}); assert(numChained === 1); } function testRejected(onRejected) { on("promiseRejected", onRejected); assert(numRejected === 0); var promise = new Promise(function(resolve,reject){ reject(); }); assert(numRejected === 1); promise = promise.caught(function(resolve,reject){ assert(numRejected === 1); reject(); }); off("promiseRejected", onRejected); return promise.caught(function(){ assert(numRejected === 1); }); } function testFulfilled(onFulfilled) { on("promiseFulfilled", onFulfilled); assert(numFulfilled === 0); var promise = new Promise(function(resolve){resolve()}); assert(numFulfilled === 1); promise = promise.then(function(){}); assert(numFulfilled === 1); off("promiseFulfilled", onFulfilled); promise.then(function(){}); assert(numFulfilled === 1); } describe("simple events API", function() { before(function() { if (util.isNode) { on = nodeOn; off = nodeOff; } else if (typeof self !== "undefined") { on = browserSimpleOn; off = browserSimpleOff; } else { assert(1===0); } }); it("promiseCreated", function () { return testCreated(function (promise) { assert(Promise.is(promise)); numCreated++ }); }); it("promiseChained", function () { return testChained(function (promise, child) { assert(Promise.is(promise)); assert(Promise.is(child)); numChained++; }); }); it("promiseRejected", function () { return testRejected(function (promise) { assert(Promise.is(promise)); numRejected++ }); }); it("promiseFulfilled", function () { return testFulfilled(function (promise) { assert(Promise.is(promise)); numFulfilled++ }); }); }); if (!util.isNode) { describe("events API", function() { before(function() { on = browserDomOn; off = browserDomOff; }); it("promiseCreated", function () { return testCreated(function (event) { assert(event.type === "promisecreated"); assert(Promise.is(event.detail.promise)); numCreated++; }); }); it("promiseChained", function () { return testChained(function (event) { assert(event.type === "promisechained"); assert(Promise.is(event.detail.promise)); assert(Promise.is(event.detail.child)); numChained++; }); }); it("promiseRejected", function () { return testRejected(function (event) { numRejected++; assert(event.type === "promiserejected"); assert(Promise.is(event.detail.promise)); }); }); it("promiseFulfilled", function () { return testFulfilled(function (event) { numFulfilled++; assert(event.type === "promisefulfilled"); assert(Promise.is(event.detail.promise)); }); }); }); } }); ================================================ FILE: test/mocha/multiple-copies.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); if (testUtils.isNodeJS) { describe("multiple copies", function() { specify("are being loaded", function() { var a = require("../../js/debug/bluebird.js"); Object.keys(require.cache).forEach(function(key) { if (/debug/.test(key)) delete require.cache[key]; }); var b = require("../../js/debug/bluebird.js"); assert.notEqual(a, b); }); }); } ================================================ FILE: test/mocha/no_conflict.js ================================================ var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.noConflict", function() { specify("should work", function() { var glob = typeof window !== "undefined" ? window : global; var bluebird = Promise.noConflict(); assert(bluebird !== Promise); glob.Promise = null; assert.strictEqual(bluebird, bluebird.noConflict()); try { delete glob.Promise; assert.strictEqual(bluebird, bluebird.noConflict()); } catch (e) { assert.strictEqual(bluebird, bluebird.noConflict()); } glob.Promise = bluebird; assert.strictEqual(bluebird, Promise.noConflict()); glob.Promise = bluebird; }) }); ================================================ FILE: test/mocha/nodeify.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var awaitGlobalException = testUtils.awaitGlobalException; var sinon = require("sinon"); var isNodeJS = testUtils.isNodeJS; /* Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ describe("nodeify", function () { it("calls back with a resolution", function () { var spy = sinon.spy(); Promise.resolve(10).nodeify(spy); setTimeout(function(){ sinon.assert.calledOnce(spy); sinon.assert.calledWith(spy, null, 10); }, 1); }); it("calls back with an undefined resolution", function() { var spy = sinon.spy(); Promise.resolve().nodeify(spy); setTimeout(function(){ sinon.assert.calledOnce(spy); sinon.assert.calledWithExactly(spy, null); }, 1); }); it("calls back with an error", function () { var spy = sinon.spy(); Promise.reject(10).nodeify(spy); setTimeout(function(){ sinon.assert.calledOnce(spy); sinon.assert.calledWith(spy, 10); }, 1); }); it("forwards a promise", function () { return Promise.resolve(10).nodeify().then(function (ten) { assert(10 === ten); }); }); it("returns undefined when a callback is passed", function () { return 'undefined' === typeof Promise.resolve(10).nodeify(function () {}); }); }); var getSpy = testUtils.getSpy; if (isNodeJS) { describe("nodeify", function () { var h = []; var e = new Error(); function thrower() { throw e; } it("throws normally in the node process if the function throws", function() { var promise = Promise.resolve(10); var turns = 0; process.nextTick(function(){ turns++; }); promise.nodeify(thrower); return awaitGlobalException(function(err) { assert(err === e); assert(turns === 1); }); }); it("always returns promise for now", function() { return Promise.resolve(3).nodeify().then(function() { var a = 0; Promise.resolve(3).nodeify(function(){ a++; }).then(function() { assert(1 == 1); }); }); }); it("should spread arguments with spread option", function() { var spy = getSpy(); Promise.resolve([1,2,3]).nodeify(spy(function(err, a, b, c) { assert(err === null); assert(a === 1); assert(b === 2); assert(c === 3); }), {spread: true}); return spy.promise; }); describe("promise rejected with falsy values", function() { specify("no reason", function() { var spy = getSpy(); Promise.reject().nodeify(spy(function(err) { assert.strictEqual(arguments.length, 1); assert.strictEqual(err.cause, undefined); })); return spy.promise; }); specify("null reason", function() { var spy = getSpy(); Promise.reject(null).nodeify(spy(function(err) { assert.strictEqual(arguments.length, 1); assert.strictEqual(err.cause, null); })); return spy.promise; }); specify("nodefying a follewer promise", function() { var spy = getSpy(); new Promise(function(resolve, reject) { resolve(new Promise(function(_, reject) { setTimeout(function() { reject(); }, 1); })) }).nodeify(spy(function(err) { assert.strictEqual(arguments.length, 1); assert.strictEqual(err.cause, undefined); })); return spy.promise; }); specify("nodefier promise becomes follower", function() { var spy = getSpy(); Promise.resolve(1).then(function() { return new Promise(function(_, reject) { setTimeout(function() { reject(); }, 1); }); }).nodeify(spy(function(err) { assert.strictEqual(arguments.length, 1); assert.strictEqual(err.cause, undefined); })); return spy.promise; }); }); it("should wrap arguments with spread option", function() { var spy = getSpy(); Promise.resolve([1,2,3]).nodeify(spy(function(err, a, b, c) { assert(err === null); assert(a === 1); assert(b === 2); assert(c === 3); }), {spread: true}); return spy.promise; }); it("should work then result is not an array", function() { var spy = getSpy(); Promise.resolve(3).nodeify(spy(function(err, a) { assert(err === null); assert(a === 3); }), {spread: true}); return spy.promise; }); it("should work if the callback throws when spread", function() { var err = new Error(); Promise.resolve([1,2,3]).nodeify(function(_, a) { throw err; }, {spread: true}); return awaitGlobalException(function(e) { assert.strictEqual(err, e); }); }); it("should work if the callback throws when rejected", function() { var err = new Error(); Promise.reject(new Error()).nodeify(function(_, a) { throw err; }); return awaitGlobalException(function(e) { assert.strictEqual(err, e); }); }); }); } ================================================ FILE: test/mocha/promise_array.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); /*! * Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ describe("all", function () { it("fulfills when passed an empty array", function () { return Promise.all([]); }); if (testUtils.ecmaScript6Collections) { it("supports iterables", function () { return Promise.all(new Set([1, 2, 3])).then(function(v) { assert.deepEqual([1,2,3].sort(), v.sort()); }); }); } it("rejects after any constituent promise is rejected", function () { var toResolve = Promise.defer(); // never resolve var toReject = Promise.defer(); var promises = [toResolve.promise, toReject.promise]; var promise = Promise.all(promises); toReject.reject(new Error("Rejected")); promise.then(assert.fail, function(e){ //Unhandled rejection }); return Promise.delay(1) .then(function () { assert.equal(promise.isRejected(), true); }) .timeout(1000); }); it("resolves foreign thenables", function () { var normal = Promise.resolve(1); var foreign = { then: function (f) { f(2); } }; return Promise.all([normal, foreign]) .then(function (result) { assert.deepEqual(result,[1, 2]); }); }); it("fulfills when passed an sparse array", function () { var toResolve = Promise.defer(); var promises = []; promises[0] = Promise.resolve(0); promises[2] = toResolve.promise; var promise = Promise.all(promises); toResolve.resolve(2); return promise.then(function (result) { assert.deepEqual(result, [0, void 0, 2]); }); }); }); /* Based on When.js tests Open Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2011 Brian Cavalier 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ describe("Promise.all-test", function () { specify("should resolve empty input", function() { return Promise.all([]).then( function(result) { assert.deepEqual(result, []); }, assert.fail ); }); specify("should resolve values array", function() { var input = [1, 2, 3]; return Promise.all(input).then( function(results) { assert.deepEqual(results, input); }, assert.fail ); }); specify("should resolve promises array", function() { var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; return Promise.all(input).then( function(results) { assert.deepEqual(results, [1, 2, 3]); }, assert.fail ); }); specify("should not resolve sparse array input", function() { var input = [, 1, , 1, 1 ]; return Promise.all(input).then( function(results) { assert.deepEqual(results, [void 0, 1, void 0, 1, 1]); }, assert.fail ); }); specify("should reject if any input promise rejects", function() { var input = [Promise.resolve(1), Promise.reject(2), Promise.resolve(3)]; return Promise.all(input).then( assert.fail, function(err) { assert.deepEqual(err, 2); } ); }); specify("should accept a promise for an array", function() { var expected, input; expected = [1, 2, 3]; input = Promise.resolve(expected); return Promise.all(input).then( function(results) { assert.deepEqual(results, expected); }, assert.fail ); }); specify("should reject when input promise does not resolve to array", function() { return Promise.all(Promise.resolve(1)).caught(TypeError, function(e){ }); }); }); ================================================ FILE: test/mocha/promisify.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var OperationalError = Promise.OperationalError; var erroneusNode = function(a, b, c, cb) { setTimeout(function(){ cb(sentinelError); }, 1); }; var sentinel = {}; var sentinelError = new OperationalError(); var successNode = function(a, b, c, cb) { setTimeout(function(){ cb(null, a); }, 1); }; var successNodeMultipleValues = function(a, b, c, cb) { setTimeout(function(){ cb(null, a, b, c); }, 1); }; var syncErroneusNode = function(a, b, c, cb) { cb(sentinelError); }; var syncSuccessNode = function(a, b, c, cb) { cb(null, a); }; var syncSuccessNodeMultipleValues = function(a, b, c, cb) { cb(null, a, b, c); }; var errToThrow; var thrower = Promise.promisify(function(a, b, c, cb) { errToThrow = new OperationalError(); throw errToThrow; }); var tprimitive = "Where is your stack now?"; var throwsStrings = Promise.promisify(function(cb){ throw tprimitive; }); var errbacksStrings = Promise.promisify(function(cb){ cb(tprimitive); }); var errbacksStringsAsync = Promise.promisify(function(cb){ setTimeout(function(){ cb(tprimitive); }, 1); }); var THIS = {}; var error = Promise.promisify(erroneusNode); var syncError = Promise.promisify(syncErroneusNode); var success = Promise.promisify(successNode); var syncSuccess = Promise.promisify(syncSuccessNode); var successMultiArgsSingleValue = Promise.promisify(successNode, {multiArgs: true}); var successMultiOptDisabledNoReceiver = Promise.promisify(successNodeMultipleValues); var syncSuccessMultiOptDisabledNoReceiver = Promise.promisify(syncSuccessNodeMultipleValues); var successMultiOptEnabledNoReceiver = Promise.promisify(successNodeMultipleValues, {multiArgs: true}); var syncSuccessMultiOptEnabledNoReceiver = Promise.promisify(syncSuccessNodeMultipleValues, {multiArgs: true}); var successMultiOptEnabledWithReceiver = Promise.promisify(successNodeMultipleValues, {multiArgs: true, context: THIS}); var syncSccessMultiOptEnabledWithReceiver = Promise.promisify(syncSuccessNodeMultipleValues, {multiArgs: true, context: THIS}); var successMultiOptDisabledWithReceiver = Promise.promisify(successNodeMultipleValues, {context: THIS}); var syncSccessMultiOptDisabledWithReceiver = Promise.promisify(syncSuccessNodeMultipleValues, {context: THIS}); var successMulti = successMultiOptDisabledNoReceiver; var syncSuccessMulti = syncSuccessMultiOptDisabledNoReceiver; describe("when calling promisified function it should ", function(){ specify("return a promise that is pending", function() { var a = error(1,2,3); var b = success(1,2,3); var c = successMulti(1,2,3); var calls = 0; assert.equal(a.isPending(), true); assert.equal(b.isPending(), true); assert.equal(c.isPending(), true); return a.caught(testUtils.noop); }); specify("should use this if no receiver was given", function(){ var o = {}; var fn = Promise.promisify(function(cb){ cb(null, this === o); }); o.fn = fn; return o.fn().then(function(val){ assert(val); }); }); specify("do nothing when called more than 1 times", function() { var err = new Error(); var stack = err.stack; var fn = Promise.promisify(function(cb) { cb(null); cb(err); }); return fn().then(function() { return Promise.delay(1).then(function() { assert.strictEqual(stack, err.stack); }) }); }); specify("undefined as receiver", function() { return Promise.promisify(function(cb) { assert.strictEqual(this, (function(){return this;})()); cb(null, 1); }, {context: undefined})().then(function(result) { assert.strictEqual(1, result); }); }); specify("double promisification returns same function back", function() { var c = function(){}; var a = Promise.promisify(function(){}); var b = Promise.promisify(a); assert.notEqual(c, a); assert.strictEqual(a, b); }); specify("call future attached handlers later", function() { var a = error(1,2,3).then(0, testUtils.noop); var b = success(1,2,3); var c = successMulti(1,2,3); var d = syncError(1,2,3).then(0, testUtils.noop); var e = syncSuccess(1,2,3).then(0, testUtils.noop); var f = syncSuccessMulti(1,2,3).then(0, testUtils.noop); var calls = 0; return Promise.all([a, b, c, d, e, f]); }); specify("Reject with the synchronously caught reason", function(){ thrower(1, 2, 3).then(assert.fail).then(assert.fail, function(e){ assert(e === errToThrow); }); }); specify("reject with the proper reason", function() { var a = error(1,2,3); var b = syncError(1,2,3); return Promise.all([ a.then(assert.fail, function(e){ assert.equal(sentinelError, e); }), b.then(assert.fail, function(e){ assert.equal(sentinelError, e); }) ]); }); describe("multi-args behaviors", function() { specify("successMultiArgsSingleValue", function() { var a = successMultiArgsSingleValue(1, 2, 3); return a.then(function(value) { assert.deepEqual([1], value); }) }); specify("successMultiOptDisabledNoReceiver", function() { var a = successMultiOptDisabledNoReceiver(1, 2, 3); return a.then(function(value) { assert.strictEqual(value, 1); }) }); specify("syncSuccessMultiOptDisabledNoReceiver", function() { var a = syncSuccessMultiOptDisabledNoReceiver(1, 2, 3); return a.then(function(value) { assert.strictEqual(value, 1); }) }); specify("successMultiOptEnabledNoReceiver", function() { var a = successMultiOptEnabledNoReceiver(1, 2, 3); return a.then(function(value) { assert.deepEqual([1,2,3], value); }) }); specify("syncSuccessMultiOptEnabledNoReceiver", function() { var a = syncSuccessMultiOptEnabledNoReceiver(1, 2, 3); return a.then(function(value) { assert.deepEqual([1,2,3], value); }) }); specify("successMultiOptEnabledWithReceiver", function() { var a = successMultiOptEnabledWithReceiver(1, 2, 3); return a.then(function(value) { assert.deepEqual([1,2,3], value); }) }); specify("syncSccessMultiOptEnabledWithReceiver", function() { var a = syncSccessMultiOptEnabledWithReceiver(1, 2, 3); return a.then(function(value) { assert.deepEqual([1,2,3], value); }) }); specify("successMultiOptDisabledWithReceiver", function() { var a = successMultiOptDisabledWithReceiver(1, 2, 3); return a.then(function(value) { assert.strictEqual(value, 1); }) }); specify("syncSccessMultiOptDisabledWithReceiver", function() { var a = syncSccessMultiOptDisabledWithReceiver(1, 2, 3); return a.then(function(value) { assert.strictEqual(value, 1); }) }); }); }); describe("with more than 5 arguments", function(){ var o = { value: 15, f: function(a,b,c,d,e,f,g, cb) { cb(null, [a,b,c,d,e,f,g, this.value]) } } var prom = Promise.promisify(o.f, {context: o}); specify("receiver should still work", function() { return prom(1,2,3,4,5,6,7).then(function(val){ assert.deepEqual( val, [1,2,3,4,5,6,7, 15] ); }); }); }); describe("promisify on objects", function(){ var o = { value: 15, f: function(a,b,c,d,e,f,g, cb) { cb(null, [a,b,c,d,e,f,g, this.value]) } }; var objf = function(){}; objf.value = 15; objf.f = function(a,b,c,d,e,f,g, cb) { cb(null, [a,b,c,d,e,f,g, this.value]) }; function Test(data) { this.data = data; } Test.prototype.get = function(a, b, c, cb) { cb(null, a, b, c, this.data); }; Test.prototype.getMany = function(a, b, c, d, e, f, g, cb) { cb(null, a, b, c, d, e, f, g, this.data); }; Promise.promisifyAll(o); Promise.promisifyAll(objf); Promise.promisifyAll(Test.prototype); specify("should not repromisify", function() { var f = o.f; var fAsync = o.fAsync; var getOwnPropertyNames = Object.getOwnPropertyNames(o); var ret = Promise.promisifyAll(o); assert.equal(f, o.f); assert.equal(fAsync, o.fAsync); assert.deepEqual(getOwnPropertyNames, Object.getOwnPropertyNames(o)); assert.equal(ret, o); }); specify("should not repromisify function object", function() { var f = objf.f; var fAsync = objf.fAsync; var getOwnPropertyNames = Object.getOwnPropertyNames(objf); var ret = Promise.promisifyAll(objf); assert.equal(f, objf.f); assert.equal(fAsync, objf.fAsync); assert.deepEqual(getOwnPropertyNames, Object.getOwnPropertyNames(objf)); assert.equal(ret, objf); }); specify("should work on function objects too", function() { objf.fAsync(1, 2, 3, 4, 5, 6, 7).then(function(result){ assert.deepEqual(result, [1, 2, 3, 4, 5, 6, 7, 15]); }); }); specify("should work on prototypes and not mix-up the instances", function() { var a = new Test(15); var b = new Test(30); var c = new Test(45); return Promise.all([ a.getAsync(1, 2, 3).then(function(result){ assert.strictEqual(result, 1); }), b.getAsync(4, 5, 6).then(function(result){ assert.strictEqual(result, 4); }), c.getAsync(7, 8, 9).then(function(result){ assert.strictEqual(result, 7); }) ]); }); specify("should work on prototypes and not mix-up the instances with more than 5 arguments", function() { var a = new Test(15); var b = new Test(30); var c = new Test(45); return Promise.all([ a.getManyAsync(1, 2, 3, 4, 5, 6, 7).then(function(result){ assert.strictEqual(result, 1); }), b.getManyAsync(4, 5, 6, 7, 8, 9, 10).then(function(result){ assert.strictEqual(result, 4); }), c.getManyAsync(7, 8, 9, 10, 11, 12, 13).then(function(result){ assert.strictEqual(result, 7); }) ]); }); specify("Fails to promisify Async suffixed methods", function() { var o = { x: function(cb){ cb(null, 13); }, xAsync: function(cb) { cb(null, 13); }, xAsyncAsync: function(cb) { cb(null, 13) } }; try { Promise.promisifyAll(o); } catch (e) { assert(e instanceof Promise.TypeError); } }); specify("Calls overridden methods", function() { function Model() { this.save = function() {}; } Model.prototype.save = function() { throw new Error(""); }; Promise.promisifyAll(Model.prototype); var model = new Model(); model.saveAsync(); }); specify("gh-232", function() { function f() { var args = [].slice.call(arguments, 0, -1); assert.deepEqual(args, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); var cb = [].slice.call(arguments, -1)[0]; cb(null, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } var fAsync = Promise.promisify(f); return fAsync(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).then(function(result) { assert.strictEqual(result, 1); }); }); specify("Should lookup method dynamically if 'this' is given", function() { var obj = { fn: function(cb) { cb(null, 1); } }; Promise.promisifyAll(obj); obj.fn = function(cb) { cb(null, 2); }; return obj.fnAsync().then(function(val) { assert.strictEqual(2, val); }); }); specify("gh335", function() { function HasArgs() { } HasArgs.prototype.args = function(cb) { return cb(null, "ok"); }; Promise.promisifyAll(HasArgs.prototype); var a = new HasArgs(); return a.argsAsync().then(function(res) { assert.equal(res, "ok"); }); }); specify("Should not promisify Object.prototype methods", function() { var o = {}; var keys = Object.keys(o); Promise.promisifyAll(o); assert.deepEqual(keys.sort(), Object.keys(o).sort()); }); specify("Should not promisify Object.prototype methods", function() { var o = {method: function(){}}; Promise.promisifyAll(o); assert.deepEqual(["method", "methodAsync"].sort(), Object.keys(o).sort()); }); if (testUtils.ecmaScript5) { specify("Should promisify non-enumerable methods", function() { var o = {}; Object.defineProperty(o, "method", { value: function() {}, enumerable: false }); Promise.promisifyAll(o); assert.deepEqual(["method", "methodAsync"].sort(), Object.getOwnPropertyNames(o).sort()); }); } }); describe("Promisify with custom suffix", function() { it("should define methods with the custom suffix", function() { function Test() { } Test.prototype.method = function method() {}; Promise.promisifyAll(Test.prototype, {suffix: "$P"}); assert(typeof Test.prototype.method$P == "function"); }); it("should throw on invalid suffix", function() { try { Promise.promisifyAll({}, {suffix: ""}); } catch (e) { return; } assert.fail(); }); }) describe("Module promisification", function() { it("should promisify module with direct property classes", function() { function RedisClient() {} RedisClient.prototype.query = function() {}; function Multi() {} Multi.prototype.exec = function() {}; Multi.staticMethod = function() {} var redis = { RedisClient: RedisClient, Multi: Multi, moduleMethod: function() {} }; redis.Multi.staticMethod.tooDeep = function() {}; Promise.promisifyAll(redis); assert(typeof redis.moduleMethodAsync === "function"); assert(typeof redis.Multi.staticMethodAsync === "function"); assert(typeof redis.Multi.prototype.execAsync === "function"); assert(typeof redis.RedisClient.prototype.queryAsync === "function"); assert(typeof redis.Multi.staticMethod.tooDeepAsync === "undefined"); }) it("should promisify module with inherited property classes", function() { function Mongoose() {} var Model = Mongoose.prototype.Model = function() {}; Model.prototype.find = function() {}; var Document = Mongoose.prototype.Document = function() {}; Document.prototype.create = function() {}; Document.staticMethod = function() {}; var mongoose = new Mongoose(); Promise.promisifyAll(mongoose); assert(typeof mongoose.Model.prototype.findAsync === "function"); assert(typeof mongoose.Document.prototype.createAsync === "function"); assert(typeof mongoose.Document.staticMethodAsync === "function") }) it("should promisify classes that have static methods", function() { function MongoClient() {this.connect = 3;} MongoClient.connect = function() {}; var module = {}; module.MongoClient = MongoClient; Promise.promisifyAll(module); assert(typeof MongoClient.connectAsync === "function"); }); }) describe("Promisify from prototype to object", function() { var getterCalled = 0; function makeClass() { var Test = (function() { function Test() { } var method = Test.prototype; method.test = function() { }; method["---invalid---"] = function(){}; if (testUtils.ecmaScript5) { Object.defineProperty(method, "thrower", { enumerable: true, configurable: true, get: function() { throw new Error("getter called"); }, set: function() { throw new Error("setter called"); } }); Object.defineProperty(method, "counter", { enumerable: true, configurable: true, get: function() { getterCalled++; }, set: function() { throw new Error("setter called"); } }); } return Test;})(); return Test; } specify("Shouldn't touch the prototype when promisifying instance", function() { var Test = makeClass(); var origKeys = Object.getOwnPropertyNames(Test.prototype).sort(); var a = new Test(); Promise.promisifyAll(a); assert(typeof a.testAsync === "function"); assert(a.hasOwnProperty("testAsync")); assert.deepEqual(Object.getOwnPropertyNames(Test.prototype).sort(), origKeys); assert(getterCalled === 0); }); specify("Shouldn't touch the method", function() { var Test = makeClass(); var origKeys = Object.getOwnPropertyNames(Test.prototype.test).sort(); var a = new Test(); Promise.promisifyAll(a); assert(typeof a.testAsync === "function"); assert.deepEqual(Object.getOwnPropertyNames(Test.prototype.test).sort(), origKeys); assert(Promise.promisify(a.test) !== a.testAsync); assert(getterCalled === 0); }); specify("Should promisify own method even if a promisified method of same name already exists somewhere in proto chain", function(){ var Test = makeClass(); var instance = new Test(); Promise.promisifyAll(instance); var origKeys = Object.getOwnPropertyNames(Test.prototype).sort(); var origInstanceKeys = Object.getOwnPropertyNames(instance).sort(); instance.test = function() {}; Promise.promisifyAll(instance); assert.deepEqual(origKeys, Object.getOwnPropertyNames(Test.prototype).sort()); assert.notDeepEqual(origInstanceKeys, Object.getOwnPropertyNames(instance).sort()); assert(getterCalled === 0); }); specify("Shouldn promisify the method closest to the object if method of same name already exists somewhere in proto chain", function(){ //IF the implementation is for-in, this pretty much tests spec compliance var Test = makeClass(); var origKeys = Object.getOwnPropertyNames(Test.prototype).sort(); var instance = new Test(); instance.test = function() {}; Promise.promisifyAll(instance); assert.deepEqual(Object.getOwnPropertyNames(Test.prototype).sort(), origKeys); assert(instance.test === instance.test); assert(getterCalled === 0); }); }); function assertLongStackTraces(e) { assert(e.stack.indexOf("From previous event:") > -1); } if (Promise.hasLongStackTraces()) { describe("Primitive errors wrapping", function() { specify("when the node function throws it", function(){ return throwsStrings().then(assert.fail, function(e){ assert(e instanceof Error); assert(e.message == tprimitive); }); }); specify("when the node function throws it inside then", function(){ return Promise.resolve().then(function() { throwsStrings().then(assert.fail, function(e) { assert(e instanceof Error); assert(e.message == tprimitive); assertLongStackTraces(e); }); }); }); specify("when the node function errbacks it synchronously", function(){ return errbacksStrings().then(assert.fail, function(e){ assert(e instanceof Error); assert(e.message == tprimitive); }); }); specify("when the node function errbacks it synchronously inside then", function(){ return Promise.resolve().then(function(){ errbacksStrings().then(assert.fail, function(e){ assert(e instanceof Error); assert(e.message == tprimitive); assertLongStackTraces(e); }); }); }); specify("when the node function errbacks it asynchronously", function(){ return errbacksStringsAsync().then(assert.fail, function(e){ assert(e instanceof Error); assert(e.message == tprimitive); assertLongStackTraces(e); }); }); specify("when the node function errbacks it asynchronously inside then", function(){ return Promise.resolve().then(function(){ errbacksStringsAsync().then(assert.fail, function(e){ assert(e instanceof Error); assert(e.message == tprimitive); assertLongStackTraces(e); }); }); }); }); } describe("Custom promisifier", function() { var dummy = {}; var err = new Error(); var chrome = { getTab: function(tabId, callback) { setTimeout(function() { callback(dummy); }, 1); }, getTabErroneous: function(tabId, callback, errback) { setTimeout(function() { errback(err); }, 1); } }; Promise.promisifyAll(chrome, { promisifier: function(originalMethod) { return function() { var self = this; var args = [].slice.call(arguments); return new Promise(function(f, r) { args.push(f, r); originalMethod.apply(self, args); }); }; } }); specify("getTab", function() { return chrome.getTabAsync(1).then(function(result) { assert.equal(dummy, result); }); }); specify("getTabErroneous", function() { return chrome.getTabErroneousAsync(2).then(assert.fail, function(e) { assert.equal(e, err); }); }); specify("Copies custom props promisifyFirst", function() { var request = function(cb){ cb(null, 1); }; request.zero = 0; request.get = function(cb) { cb(null, 2 + this.zero); }; request.post = function(cb) { cb(null, 3); }; request = Promise.promisifyAll(Promise.promisify(request)); return Promise.all([ request(), request.getAsync(), request.postAsync() ]).then(function(a) { assert.deepEqual([1,2,3], a); }); }); specify("Copies custom props promisifyAll first", function() { var request = function(cb){ cb(null, 1); }; request.zero = 0; request.get = function(cb) { cb(null, 2 + this.zero); }; request.post = function(cb) { cb(null, 3); }; request = Promise.promisify(Promise.promisifyAll(request)); return Promise.all([ request(), request.getAsync(), request.postAsync() ]).then(function(a) { assert.deepEqual([1,2,3], a); }); }); specify("Copies custom props no this", function() { var request = function(cb){ cb(null, 1); }; request.zero = 0; request.get = function(cb) { cb(null, 2); }; request.post = function(cb) { cb(null, 3); }; request = Promise.promisify(Promise.promisifyAll(request)); var getAsync = request.getAsync; var postAsync = request.postAsync; return Promise.all([ request(), getAsync(), postAsync() ]).then(function(a) { assert.deepEqual([1,2,3], a); }); }); specify("custom promisifier enhancing default promisification", function() { var obj = { a: function(cb) { setTimeout(function() { cb(null, 1); }, 1); }, b: function(val, cb) { setTimeout(function() { cb(null, val); }, 1); } }; obj = Promise.promisifyAll(obj, { promisifier: function(originalFunction, defaultPromisifier) { var promisified = defaultPromisifier(originalFunction); return function() { var args = [].slice.call(arguments); var self = this; return Promise.all(args).then(function(awaitedArgs) { return promisified.apply(self, awaitedArgs); }); }; } }); return obj.bAsync(obj.aAsync()).then(function(val) { assert.strictEqual(val, 1); }); }); specify("multiArgs option enabled single value", function() { var o = { get: function(cb) { cb(null, 1) } }; Promise.promisifyAll(o, {multiArgs: true}); return o.getAsync().then(function(value) { assert.deepEqual([1], value); }); }); specify("multiArgs option enabled multi value", function() { var o = { get: function(cb) { cb(null, 1, 2, 3) } }; Promise.promisifyAll(o, {multiArgs: true}); return o.getAsync().then(function(value) { assert.deepEqual([1,2,3], value); }); }); specify("multiArgs option disabled single value", function() { var o = { get: function(cb) { cb(null, 1) } }; Promise.promisifyAll(o); return o.getAsync().then(function(value) { assert.strictEqual(value, 1); }); }); specify("multiArgs option disabled multi value", function() { var o = { get: function(cb) { cb(null, 1) } }; Promise.promisifyAll(o); return o.getAsync().then(function(value) { assert.strictEqual(value, 1); }); }); }); describe("OperationalError wrapping", function() { var CustomError = function(){ } CustomError.prototype = new Error(); CustomError.prototype.constructor = CustomError; function isUntypedError(obj) { return obj instanceof Error && Object.getPrototypeOf(obj) === Error.prototype; } if (!isUntypedError(new Error())) { console.log("error must be untyped"); } if (isUntypedError(new CustomError())) { console.log("customerror must be typed"); } function stringback(cb) { cb("Primitive as error"); } function errback(cb) { cb(new Error("error as error")); } function typeback(cb) { cb(new CustomError()); } function stringthrow(cb) { throw("Primitive as error"); } function errthrow(cb) { throw(new Error("error as error")); } function typethrow(cb) { throw(new CustomError()); } stringback = Promise.promisify(stringback); errback = Promise.promisify(errback); typeback = Promise.promisify(typeback); stringthrow = Promise.promisify(stringthrow); errthrow = Promise.promisify(errthrow); typethrow = Promise.promisify(typethrow); specify("should wrap stringback", function() { return stringback().error(function(e) { assert(e instanceof OperationalError); }); }); specify("should wrap errback", function() { return errback().error(function(e) { assert(e instanceof OperationalError); }); }); specify("should not wrap typeback", function() { return typeback().caught(CustomError, function(e){ }); }); specify("should not wrap stringthrow", function() { return stringthrow().error(assert.fail).then(assert.fail, function(e){ assert(e instanceof Error); }); }); specify("should not wrap errthrow", function() { return errthrow().error(assert.fail).then(assert.fail, function(e) { assert(e instanceof Error); }); }); specify("should not wrap typethrow", function() { return typethrow().error(assert.fail) .caught(CustomError, function(e){ }); }); }); describe("nodeback with multiple arguments", function() { specify("spreaded with immediate values", function() { var promise = Promise.promisify(function(cb) { cb(null, 1, 2, 3); }, {multiArgs: true})(); return promise.spread(function(a, b, c) { assert.equal(a, 1); assert.equal(b, 2); assert.equal(c, 3); }); }); specify("spreaded with thenable values should be unwrapped", function() { var a = {then: function(a){a(1)}}; var b = a; var c = a; var promise = Promise.promisify(function(cb) { cb(null, a, b, c); }, {multiArgs: true})(); return promise.spread(function(a_, b_, c_) { assert.equal(a_, 1); assert.equal(b_, 1); assert.equal(c_, 1); }); }); specify("spreaded with promise values should be unwrapped", function() { var a = Promise.resolve(1); var b = Promise.resolve(2); var c = Promise.resolve(3); var promise = Promise.promisify(function(cb) { cb(null, a, b, c); }, {multiArgs: true})(); return promise.spread(function(a_, b_, c_) { assert.strictEqual(a_, 1); assert.strictEqual(b_, 2); assert.strictEqual(c_, 3); }); }); }); describe("filter", function() { specify("gets an argument whether default filter was passed", function() { Promise.promisifyAll({ abc: function() {} }, { filter: function(_, __, ___, passesDefaultFilter) { assert.strictEqual(passesDefaultFilter, true); } }) }); specify("doesn't fail when allowing non-identifier methods", function() { var a = Promise.promisifyAll({ " a s d ": function(cb) { cb(null, 1); } }, { filter: function() { return true; } }); a[" a s d Async"]().then(function(val) { assert.strictEqual(1, val); }); }); }); var global = new Function("return this")(); var canEvaluate = (function() { if (typeof window !== "undefined" && window !== null && typeof window.document !== "undefined" && typeof navigator !== "undefined" && navigator !== null && typeof navigator.appName === "string" && window === global) { return false; } return true; })(); var canTestArity = (function(a, b, c) {}).length === 3 && canEvaluate; if (canTestArity) { describe("arity", function() { specify("should be original - 1", function() { var fn = function(a, b, c, callback) {}; assert.equal(Promise.promisify(fn).length, 3); var o = { fn: function(a, b, c, callback) { } }; assert.equal(Promise.promisifyAll(o).fnAsync.length, 3); }) }) } describe("github 680", function() { before(function() { Function.prototype.method = function() {}; }); after(function() { delete Function.prototype.method; }); specify("should not try to promisify methods from Function.prototype, native or otherwise", function() { var a = function() {}; a.fn = function() {}; Promise.promisifyAll(a); assert.strictEqual(undefined, a.methodAsync); assert.strictEqual(undefined, a.applyAsync); assert(typeof a.fnAsync === "function"); }); }); describe("github 1063", function() { specify("should not cause error when called with no arguments", function() { return Promise.promisify(function(cb) { cb(); }, { multiArgs: true})().then(function(values) { assert(Array.isArray(values)); assert.strictEqual(values.length, 0); }); }) }); describe("github 1023", function() { specify("promisify triggers custom schedulers", function() { var triggered = false; var defaultScheduler = Promise.setScheduler(function(fn) { triggered = true; setTimeout(fn, 0); }); var fnAsync = Promise.promisify(function(cb) { setTimeout(function() { cb(null, true); }, 0); }); return fnAsync().then(function(result) { assert(result); assert(triggered); }).lastly(function() { Promise.setScheduler(defaultScheduler); }); }); }) ================================================ FILE: test/mocha/props.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.props", function () { specify("should reject undefined", function() { return Promise.props().caught(TypeError, function(){ }) }); specify("should reject primitive", function() { return Promise.props("str").caught(TypeError, function(){ }) }); specify("should resolve to new object", function() { var o = {}; return Promise.props(o).then(function(v){ assert(v !== o); assert.deepEqual(o, v); }); }); specify("should resolve value properties", function() { var o = { one: 1, two: 2, three: 3 }; return Promise.props(o).then(function(v){ assert.deepEqual({ one: 1, two: 2, three: 3 }, v); }); }); specify("should resolve immediate properties", function() { var o = { one: Promise.resolve(1), two: Promise.resolve(2), three: Promise.resolve(3) }; return Promise.props(o).then(function(v){ assert.deepEqual({ one: 1, two: 2, three: 3 }, v); }); }); specify("should resolve eventual properties", function() { var d1 = Promise.defer(), d2 = Promise.defer(), d3 = Promise.defer(); var o = { one: d1.promise, two: d2.promise, three: d3.promise }; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.props(o).then(function(v){ assert.deepEqual({ one: 1, two: 2, three: 3 }, v); }); }); specify("should reject if any input promise rejects", function() { var o = { one: Promise.resolve(1), two: Promise.reject(2), three: Promise.resolve(3) }; return Promise.props(o).then(assert.fail, function(v){ assert(v === 2); }); }); specify("should accept a promise for an object", function() { var o = { one: Promise.resolve(1), two: Promise.resolve(2), three: Promise.resolve(3) }; var d1 = Promise.defer(); setTimeout(function(){ d1.fulfill(o); }, 1); return Promise.props(d1.promise).then(function(v){ assert.deepEqual({ one: 1, two: 2, three: 3 }, v); }); }); specify("should reject a promise for a primitive", function() { var d1 = Promise.defer(); setTimeout(function(){ d1.fulfill("text"); }, 1); return Promise.props(d1.promise).caught(TypeError, function(){ }); }); specify("should accept thenables in properties", function() { var t1 = {then: function(cb){cb(1);}}; var t2 = {then: function(cb){cb(2);}}; var t3 = {then: function(cb){cb(3);}}; var o = { one: t1, two: t2, three: t3 }; return Promise.props(o).then(function(v){ assert.deepEqual({ one: 1, two: 2, three: 3 }, v); }); }); specify("should accept a thenable for thenables in properties", function() { var o = { then: function (f) { f({ one: { then: function (cb) { cb(1); } }, two: { then: function (cb) { cb(2); } }, three: { then: function (cb) { cb(3); } } }); } }; return Promise.props(o).then(function(v){ assert.deepEqual({ one: 1, two: 2, three: 3 }, v); }); }); specify("treats arrays for their properties", function() { var o = [1,2,3]; return Promise.props(o).then(function(v){ assert.deepEqual({ 0: 1, 1: 2, 2: 3 }, v); }); }); if (typeof Map !== "undefined") { specify("works with es6 maps", function() { return Promise.props(new Map([ ["a", Promise.resolve(1)], ["b", Promise.resolve(2)], ["c", Promise.resolve(3)] ])).then(function(result) { assert.strictEqual(result.get("a"), 1); assert.strictEqual(result.get("b"), 2); assert.strictEqual(result.get("c"), 3); }); }); specify("doesn't await promise keys in es6 maps", function() { var a = new Promise(function() {}); var b = new Promise(function() {}); var c = new Promise(function() {}); return Promise.props(new Map([ [a, Promise.resolve(1)], [b, Promise.resolve(2)], [c, Promise.resolve(3)] ])).then(function(result) { assert.strictEqual(result.get(a), 1); assert.strictEqual(result.get(b), 2); assert.strictEqual(result.get(c), 3); }); }); specify("empty map should resolve to empty map", function() { return Promise.props(new Map()).then(function(result) { assert(result instanceof Map); }); }); } }); ================================================ FILE: test/mocha/race.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.race", function(){ it("remains forever pending when passed an empty array", function() { var p = Promise.race([]); return Promise.delay(1).then(function() { assert(p.isPending()); }); }); it("remains forever pending when passed an empty sparse array", function() { var p = Promise.race([,,,,,]); return Promise.delay(1).then(function() { assert(p.isPending()); }); }); it("fulfills when passed an immediate value", function() { return Promise.race([1,2,3]).then(function(v){ assert.deepEqual(v, 1); }); }); it("fulfills when passed an immediately fulfilled value", function() { var d1 = Promise.defer(); d1.fulfill(1); var p1 = d1.promise; var d2 = Promise.defer(); d2.fulfill(2); var p2 = d2.promise; var d3 = Promise.defer(); d3.fulfill(3); var p3 = d3.promise; return Promise.race([p1, p2, p3]).then(function(v){ assert.deepEqual(v, 1); }); }); it("fulfills when passed an eventually fulfilled value", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.fulfill(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.race([p1, p2, p3]).then(function(v){ assert.deepEqual(v, 1); }); }); it("rejects when passed an immediate value", function() { return Promise.race([Promise.reject(1), 2, 3]).then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }); it("rejects when passed an immediately rejected value", function() { var d1 = Promise.defer(); d1.reject(1); var p1 = d1.promise; var d2 = Promise.defer(); d2.fulfill(2); var p2 = d2.promise; var d3 = Promise.defer(); d3.fulfill(3); var p3 = d3.promise; return Promise.race([, p1, , p2, , , p3]).then(assert.fail, function(v){ assert.deepEqual(v, 1); }); }); it("rejects when passed an eventually rejected value", function() { var d1 = Promise.defer(); var p1 = d1.promise; var d2 = Promise.defer(); var p2 = d2.promise; var d3 = Promise.defer(); var p3 = d3.promise; setTimeout(function(){ d1.reject(1); d2.fulfill(2); d3.fulfill(3); }, 1); return Promise.race([p1, p2, p3]).then(assert.fail, function(v){ assert.deepEqual(v, 1); }) }); it("propagates bound value", function() { var o = {}; return Promise.resolve([1]).bind(o).race().then(function(v){ assert(v === 1); assert(this === o); }); }); }); ================================================ FILE: test/mocha/reduce.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); function promised(val) { return new Promise(function(f) { setTimeout(function() { f(val); }, 1); }); } function promising(val) { return function() { return promised(val); } } function promisingThen(val) { return function() { return promised(val).then(function(resolved) { return resolved; }); } } function thenabled(val) { return { then: function(f){ setTimeout(function() { f(val); }, 1); } }; } function thenabling(val) { return function() { return thenabled(val); } } function evaluate(val) { if (typeof val === 'function') { val = val(); } if (Array.isArray(val)) { val = val.map(function(member) { return evaluate(member); }); } return val; } var ACCUM_CRITERIA = [ { value: 0, desc: "that is resolved" }, { value: promising(0), desc: "as a Promise" }, { value: promisingThen(0), desc: "as a deferred Promise" }, { value: thenabling(0), desc: "as a thenable" }, ]; var VALUES_CRITERIA = [ { value: [], total: 0, desc: "and no values" }, { value: [ 1 ], total: 1, desc: "and a single resolved value" }, { value: [ 1, 2, 3 ], total: 6, desc: "and multiple resolved values" }, { value: [ promising(1) ], total: 1, desc: "and a single Promise" }, { value: [ promising(1), promising(2), promising(3) ], total: 6, desc: "and multiple Promises" }, { value: [ promisingThen(1) ], total: 1, desc: "and a single deferred Promise" }, { value: [ promisingThen(1), promisingThen(2), promisingThen(3) ], total: 6, desc: "and multiple deferred Promises" }, { value: [ thenabling(1) ], total: 1, desc: "and a single thenable" }, { value: [ thenabling(1), thenabling(2), thenabling(3) ], total: 6, desc: "and multiple thenables" }, { value: [ thenabling(1), promisingThen(2), promising(3), 4 ], total: 10, desc: "and a blend of values" }, ]; var ERROR = new Error("BOOM"); describe("Promise.prototype.reduce", function() { it("works with no values", function() { return Promise.resolve([]).reduce(function(total, value) { return total + value + 5; }).then(function(total) { assert.strictEqual(total, undefined); }); }); it("works with a single value", function() { return Promise.resolve([ 1 ]).reduce(function(total, value) { return total + value + 5; }).then(function(total) { assert.strictEqual(total, 1); }); }); it("works when the iterator returns a value", function() { return Promise.resolve([ 1, 2, 3 ]).reduce(function(total, value) { return total + value + 5; }).then(function(total) { assert.strictEqual(total, (1 + 2+5 + 3+5)); }); }); it("works when the iterator returns a Promise", function() { return Promise.resolve([ 1, 2, 3 ]).reduce(function(total, value) { return promised(5).then(function(bonus) { return total + value + bonus; }); }).then(function(total) { assert.strictEqual(total, (1 + 2+5 + 3+5)); }); }); it("works when the iterator returns a thenable", function() { return Promise.resolve([ 1, 2, 3 ]).reduce(function(total, value) { return thenabled(total + value + 5); }).then(function(total) { assert.strictEqual(total, (1 + 2+5 + 3+5)); }); }); }); describe("Promise.reduce", function() { it("should allow returning values", function() { var a = [promised(1), promised(2), promised(3)]; return Promise.reduce(a, function(total, a) { return total + a + 5; }, 0).then(function(total){ assert.equal(total, 1+5 + 2+5 + 3+5); }); }); it("should allow returning promises", function() { var a = [promised(1), promised(2), promised(3)]; return Promise.reduce(a, function(total, a) { return promised(5).then(function(b) { return total + a + b; }); }, 0).then(function(total){ assert.equal(total, 1+5 + 2+5 + 3+5); }); }); it("should allow returning thenables", function() { var b = [1,2,3]; var a = []; return Promise.reduce(b, function(total, cur) { a.push(cur); return thenabled(3); }, 0).then(function(total) { assert.equal(total, 3); assert.deepEqual(a, b); }); }); it("propagates error", function() { var a = [promised(1), promised(2), promised(3)]; var e = new Error("asd"); return Promise.reduce(a, function(total, a) { if (a > 2) { throw e; } return total + a + 5; }, 0).then(assert.fail, function(err) { assert.equal(err, e); }); }); describe("with no initial accumulator or values", function() { it("works when the iterator returns a value", function() { return Promise.reduce([], function(total, value) { return total + value + 5; }).then(function(total){ assert.strictEqual(total, undefined); }); }); it("works when the iterator returns a Promise", function() { return Promise.reduce([], function(total, value) { return promised(5).then(function(bonus) { return total + value + bonus; }); }).then(function(total){ assert.strictEqual(total, undefined); }); }); it("works when the iterator returns a thenable", function() { return Promise.reduce([], function(total, value) { return thenabled(total + value + 5); }).then(function(total){ assert.strictEqual(total, undefined); }); }); }); describe("with an initial accumulator value", function() { ACCUM_CRITERIA.forEach(function(criteria) { var initial = criteria.value; describe(criteria.desc, function() { VALUES_CRITERIA.forEach(function(criteria) { var values = criteria.value; var valueTotal = criteria.total; describe(criteria.desc, function() { it("works when the iterator returns a value", function() { return Promise.reduce(evaluate(values), function(total, value) { return total + value + 5; }, evaluate(initial)).then(function(total){ assert.strictEqual(total, valueTotal + (values.length * 5)); }); }); it("works when the iterator returns a Promise", function() { return Promise.reduce(evaluate(values), function(total, value) { return promised(5).then(function(bonus) { return total + value + bonus; }); }, evaluate(initial)).then(function(total){ assert.strictEqual(total, valueTotal + (values.length * 5)); }); }); it("works when the iterator returns a thenable", function() { return Promise.reduce(evaluate(values), function(total, value) { return thenabled(total + value + 5); }, evaluate(initial)).then(function(total){ assert.strictEqual(total, valueTotal + (values.length * 5)); }); }); }); }); }); }); it("propagates an initial Error", function() { var initial = Promise.reject(ERROR); var values = [ thenabling(1), promisingThen(2)(), promised(3), 4 ]; return Promise.reduce(values, function(total, value) { return value; }, initial).then(assert.fail, function(err) { assert.equal(err, ERROR); }); }); it("propagates a value's Error", function() { var initial = 0; var values = [ thenabling(1), promisingThen(2)(), Promise.reject(ERROR), promised(3), 4 ]; return Promise.reduce(values, function(total, value) { return value; }, initial).then(assert.fail, function(err) { assert.equal(err, ERROR); }); }); it("propagates an Error from the iterator", function() { var initial = 0; var values = [ thenabling(1), promisingThen(2)(), promised(3), 4 ]; return Promise.reduce(values, function(total, value) { if (value === 2) { throw ERROR; } return value; }, initial).then(assert.fail, function(err) { assert.equal(err, ERROR); }); }); }); describe("with a 0th value acting as an accumulator", function() { it("acts this way when an accumulator value is provided yet `undefined`", function() { return Promise.reduce([ 1, 2, 3 ], function(total, value) { return ((total === void 0) ? 0 : total) + value + 5; }, undefined).then(function(total){ assert.strictEqual(total, (1 + 2+5 + 3+5)); }); }); it("survives an `undefined` 0th value", function() { return Promise.reduce([ undefined, 1, 2, 3 ], function(total, value) { return ((total === void 0) ? 0 : total) + value + 5; }).then(function(total){ assert.strictEqual(total, (1+5 + 2+5 + 3+5)); }); }); ACCUM_CRITERIA.forEach(function(criteria) { var zeroth = criteria.value; describe(criteria.desc, function() { VALUES_CRITERIA.forEach(function(criteria) { var values = criteria.value; var zerothAndValues = [ zeroth ].concat(values); var valueTotal = criteria.total; describe(criteria.desc, function() { it("works when the iterator returns a value", function() { return Promise.reduce(evaluate(zerothAndValues), function(total, value) { return total + value + 5; }).then(function(total){ assert.strictEqual(total, valueTotal + (values.length * 5)); }); }); it("works when the iterator returns a Promise", function() { return Promise.reduce(evaluate(zerothAndValues), function(total, value) { return promised(5).then(function(bonus) { return total + value + bonus; }); }).then(function(total){ assert.strictEqual(total, valueTotal + (values.length * 5)); }); }); it("works when the iterator returns a thenable", function() { return Promise.reduce(evaluate(zerothAndValues), function(total, value) { return thenabled(total + value + 5); }).then(function(total){ assert.strictEqual(total, valueTotal + (values.length * 5)); }); }); }); }); }); }); it("propagates an initial Error", function() { var values = [ Promise.reject(ERROR), thenabling(1), promisingThen(2)(), promised(3), 4 ]; return Promise.reduce(values, function(total, value) { return value; }).then(assert.fail, function(err) { assert.equal(err, ERROR); }); }); it("propagates a value's Error", function() { var values = [ 0, thenabling(1), promisingThen(2)(), Promise.reject(ERROR), promised(3), 4 ]; return Promise.reduce(values, function(total, value) { return value; }).then(assert.fail, function(err) { assert.equal(err, ERROR); }); }); it("propagates an Error from the iterator", function() { var values = [ 0, thenabling(1), promisingThen(2)(), promised(3), 4 ]; return Promise.reduce(values, function(total, value) { if (value === 2) { throw ERROR; } return value; }).then(assert.fail, function(err) { assert.equal(err, ERROR); }); }); }); }); /* Based on When.js tests Open Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2011 Brian Cavalier 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ var assert = require("assert"); var testUtils = require("./helpers/util.js"); var sentinel = {}; var other = {}; describe("Promise.reduce-test", function () { function plus(sum, val) { return sum + val; } function later(val) { return Promise.delay(1, val); } specify("should reduce values without initial value", function() { return Promise.reduce([1,2,3], plus).then( function(result) { assert.deepEqual(result, 6); }, assert.fail ); }); specify("should reduce values with initial value", function() { return Promise.reduce([1,2,3], plus, 1).then( function(result) { assert.deepEqual(result, 7); }, assert.fail ); }); specify("should reduce values with initial promise", function() { return Promise.reduce([1,2,3], plus, Promise.resolve(1)).then( function(result) { assert.deepEqual(result, 7); }, assert.fail ); }); specify("should reduce promised values without initial value", function() { var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; return Promise.reduce(input, plus).then( function(result) { assert.deepEqual(result, 6); }, assert.fail ); }); specify("should reduce promised values with initial value", function() { var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; return Promise.reduce(input, plus, 1).then( function(result) { assert.deepEqual(result, 7); }, assert.fail ); }); specify("should reduce promised values with initial promise", function() { var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; return Promise.reduce(input, plus, Promise.resolve(1)).then( function(result) { assert.deepEqual(result, 7); }, assert.fail ); }); specify("should reduce empty input with initial value", function() { var input = []; return Promise.reduce(input, plus, 1).then( function(result) { assert.deepEqual(result, 1); }, assert.fail ); }); specify("should reduce empty input with eventual promise", function() { return Promise.reduce([], plus, Promise.delay(1, 1)).then( function(result) { assert.deepEqual(result, 1); }, assert.fail ); }); specify("should reduce empty input with initial promise", function() { return Promise.reduce([], plus, Promise.resolve(1)).then( function(result) { assert.deepEqual(result, 1); }, assert.fail ); }); specify("should reject Promise input contains rejection", function() { var input = [Promise.resolve(1), Promise.reject(2), Promise.resolve(3)]; return Promise.reduce(input, plus, Promise.resolve(1)).then( assert.fail, function(result) { assert.deepEqual(result, 2); } ); }); specify("should reduce to undefined with empty array", function() { return Promise.reduce([], plus).then(function(r){ assert(r === void 0); }); }); specify("should reduce to initial value with empty array", function() { return Promise.reduce([], plus, sentinel).then(function(r){ assert(r === sentinel); }); }); specify("should reduce in input order", function() { return Promise.reduce([later(1), later(2), later(3)], plus, '').then( function(result) { assert.deepEqual(result, '123'); }, assert.fail ); }); specify("should accept a promise for an array", function() { return Promise.reduce(Promise.resolve([1, 2, 3]), plus, '').then( function(result) { assert.deepEqual(result, '123'); }, assert.fail ); }); specify("should resolve to initialValue Promise input promise does not resolve to an array", function() { return Promise.reduce(Promise.resolve(123), plus, 1).caught(TypeError, function(e){ }); }); specify("should provide correct basis value", function() { function insertIntoArray(arr, val, i) { arr[i] = val; return arr; } return Promise.reduce([later(1), later(2), later(3)], insertIntoArray, []).then( function(result) { assert.deepEqual(result, [1,2,3]); }, assert.fail ); }); describe("checks", function() { function later(val, ms) { return Promise.delay(ms, val); } function plus(sum, val) { return sum + val; } function plusDelayed(sum, val) { return Promise.delay(0).then(function() { return sum + val; }); } function check(delay1, delay2, delay3) { return Promise.reduce([ later(1, delay1), later(2, delay2), later(3, delay3) ], plus, '').then(function(result) { assert.deepEqual(result, '123'); }) } function checkDelayed(delay1, delay2, delay3) { return Promise.reduce([ later(1, delay1), later(2, delay2), later(3, delay3) ], plusDelayed, '').then(function(result) { assert.deepEqual(result, '123'); }) } specify("16, 16, 16", function() { return check(16, 16, 16); }); specify("16, 16, 4", function() { return check(16, 16, 4); }); specify("4, 16, 16", function() { return check(4, 16, 16); }); specify("16, 4, 16", function() { return check(16, 4, 16); }); specify("16, 16, 4", function() { return check(16, 16, 4); }); specify("4, 4, 16", function() { return check(4, 4, 16); }); specify("16, 4, 4", function() { return check(16, 4, 4); }); specify("4, 16, 4", function() { return check(4, 16, 4); }); specify("4, 4, 4", function() { return check(4, 4, 4); }); specify("16, 16, 16", function() { return checkDelayed(16, 16, 16); }); specify("16, 16, 4", function() { return checkDelayed(16, 16, 4); }); specify("4, 16, 16", function() { return checkDelayed(4, 16, 16); }); specify("16, 4, 16", function() { return checkDelayed(16, 4, 16); }); specify("16, 16, 4", function() { return checkDelayed(16, 16, 4); }); specify("4, 4, 16", function() { return checkDelayed(4, 4, 16); }); specify("16, 4, 4", function() { return checkDelayed(16, 4, 4); }); specify("4, 16, 4", function() { return checkDelayed(4, 16, 4); }); specify("4, 4, 4", function() { return checkDelayed(4, 4, 4); }); }) }); ================================================ FILE: test/mocha/reflect.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var testFulfilled = require("./helpers/testThreeCases").testFulfilled; var testRejected = require("./helpers/testThreeCases").testRejected; describe(".reflect()", function() { testFulfilled(1, function(promise) { return promise.reflect().then(function(inspection) { assert(inspection instanceof Promise.PromiseInspection); assert(inspection.isFulfilled()); assert(inspection.value() === 1); }); }); testRejected(2, function(promise) { return promise.reflect().then(function(inspection) { assert(inspection instanceof Promise.PromiseInspection); assert(inspection.isRejected()); assert(inspection.reason() === 2); }); }); }); ================================================ FILE: test/mocha/regress.js ================================================ var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("regressions", function() { specify("should be able to call .then more than once inside that promise's handler", function() { var called = 0; var resolve; var promise = new Promise(function() { resolve = arguments[0]; }); return new Promise(function(resolve) { promise.then(function() { called++; promise.then(function(){ called++; }); promise.then(function(){ called++; assert.equal(4, called); resolve(); }); }); promise.then(function() { called++; }); setTimeout(resolve, 1); }); }); specify("should be able to nest arbitrary amount of then handlers on already resolved promises", function() { var called = 0; var resolve; var promise = Promise.resolve(); return new Promise(function(resolve) { promise.then(function() { called++; promise.then(function(){ called++; promise.then(function(){ called++; }); promise.then(function(){ called++; }); }); promise.then(function(){ promise.then(function(){ called++; }); promise.then(function(){ called++; assert.equal(8, called); resolve(); }); called++; }); }); promise.then(function() { called++; }); }); }); specify("github-682", function() { var o = { then: function(f) { setTimeout(function() { delete o.then; f(o); }, 1); } }; return Promise.resolve(o).then(function(value) { assert.equal(o, value); }); }); specify("gh-1006", function() { return Promise.resolve().then(function() { new Promise(function() {}).tap(function() {}).cancel(); }); }); if (testUtils.isNodeJS) { describe("github-689", function() { var originalProperty = Object.getOwnPropertyDescriptor(process, "domain"); var bindCalls = 0; beforeEach(function() { bindCalls = 0; }); before(function() { Object.defineProperty(process, "domain", { writable: true, enumerable: true, configurable: true, value: { emit: function() {}, bind: function(fn) { bindCalls++; // Ensure non-strict mode. return new Function("fn", "return function() {return fn.apply(this, arguments);}")(fn); }, enter: function() {}, exit: function() {} } }); }); after(function() { Object.defineProperty(process, "domain", originalProperty); }); specify(".return", function() { return Promise.resolve().thenReturn(true).then(function(val) { assert.strictEqual(val, true); assert.strictEqual(bindCalls, 4); }); }); specify(".throw", function() { return Promise.resolve().thenThrow(true).then(assert.fail, function(err) { assert.strictEqual(err, true); assert.strictEqual(bindCalls, 5); }); }); specify(".finally", function() { return Promise.resolve(true).lastly(function() { return Promise.delay(1); }).then(function(val) { assert.strictEqual(val, true); assert.strictEqual(bindCalls, 6); }); }); }); describe("long promise chain stack overflow", function() { specify("mapSeries", function() { var array = new Array(5000); for (var i = 0; i < array.length; ++i) { array[i] = null; } var theError = new Error(); var queryAsync = Promise.promisify(function(cb) { process.nextTick(function() { cb(theError); }, 1); }); return Promise.mapSeries(array, function() { return queryAsync(); }).caught(function(e) { assert.strictEqual(e.cause, theError); }); }); }); } }); ================================================ FILE: test/mocha/rejections.js ================================================ var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Using as a rejection reason", function() { var nullObject = (function() { var es5 = (function(){"use strict"; return this; })() === undefined; if (es5) { return function() { return Object.create(null); }; } else { return function() { return {}; }; } })(); describe("Object.create(null)", function() { specify("directly", function() { var o = nullObject(); return Promise.reject(o).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through constructor by throw", function() { var o = nullObject(); return new Promise(function() { throw o; }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through constructor immediately", function() { var o = nullObject(); return new Promise(function() { arguments[1](o); }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through constructor eventually", function() { var o = nullObject(); return new Promise(function(_, r) { setTimeout(function() { r(o); }, 1); }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through defer immediately", function() { var o = nullObject(); var d = Promise.defer(); var ret = d.promise.then(assert.fail, function(e) { assert.strictEqual(e, o); }); d.reject(o); return ret; }); specify("through defer eventually", function() { var o = nullObject(); var d = Promise.defer(); var ret = d.promise.then(assert.fail, function(e) { assert.strictEqual(e, o); }); setTimeout(function() { d.reject(o); }, 1); return ret; }); specify("through thenThrow immediately", function() { var o = nullObject(); return Promise.resolve().thenThrow(o).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through handler throw", function() { var o = nullObject(); return Promise.resolve().then(function() { throw o; }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through handler-returned-promise immediately", function() { var o = nullObject(); return Promise.resolve().then(function() { return Promise.reject(o); }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through handler-returned-promise eventually", function() { var o = nullObject(); return Promise.resolve().then(function() { return new Promise(function(_, r) { setTimeout(function() { r(o); }, 1); }); }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through handler-returned-thenable throw", function() { var o = nullObject(); return Promise.resolve().then(function() { return { then: function(_, r) { throw o; } }; }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through handler-returned-thenable immediately", function() { var o = nullObject(); return Promise.resolve().then(function() { return { then: function(_, r) { r(o); } }; }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through handler-returned-thenable eventually", function() { var o = nullObject(); return Promise.resolve().then(function() { return { then: function(_, r) { setTimeout(function() { r(o); }, 1); } }; }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); var BluebirdThenable = require("../../js/debug/promise.js")(); specify("through handler-returned-bluebird-thenable immediately", function() { var o = nullObject(); return Promise.resolve().then(function() { return BluebirdThenable.reject(o); }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through handler-returned-bluebird-thenable eventually", function() { var o = nullObject(); return Promise.resolve().then(function() { return new BluebirdThenable(function(_, r) { setTimeout(function() { r(o); }, 1); }); }).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through promisified callback immediately", function() { var o = nullObject(); return Promise.promisify(function(cb) { cb(o); })().then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through immediate PromiseArray promise", function() { var o = nullObject(); return Promise.all([Promise.reject(o)]).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through eventual PromiseArray promise", function() { var o = nullObject(); return Promise.all([new Promise(function(_, r) { setTimeout(function() { r(o); }, 1); })]).then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); specify("through promisified callback eventually", function() { var o = nullObject(); return Promise.promisify(function(cb) { setTimeout(function() { cb(o); }, 1); })().then(assert.fail, function(e) { assert.strictEqual(e, o); }); }); }); }); ================================================ FILE: test/mocha/resolution.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var getValues = function() { var d = Promise.defer(); var f = Promise.resolve(3); var r = Promise.reject(3); setTimeout(function(){ d.resolve(3); }, 1); return { value: 3, thenableFulfill: {then: function(fn){setTimeout(function(){fn(3)}, 1);}}, thenableReject: {then: function(_, fn){setTimeout(function(){fn(3)}, 1);}}, promiseFulfilled: f, promiseRejected: r, promiseEventual: d.promise }; }; function expect(count, callback) { var cur = 0; return new Promise(function() { }); } function expect(count, done) { var total = 0; return function() { total++; if (total >= count) { } } } describe("Promise.resolve", function() { specify("follows thenables and promises", function() { var values = getValues(); var async = false; var ret = Promise.all([ Promise.resolve(values.value).then(testUtils.noop), Promise.resolve(values.thenableFulfill).then(testUtils.noop), Promise.resolve(values.thenableReject).then(assert.fail, testUtils.noop), Promise.resolve(values.promiseFulfilled).then(testUtils.noop), Promise.resolve(values.promiseRejected).then(assert.fail, testUtils.noop), Promise.resolve(values.promiseEventual).then(testUtils.noop) ]).then(function(v) { assert.deepEqual(v, [3, 3, 3, 3, 3, 3]); assert(async); }); async = true; return ret; }); }); describe("Cast thenable", function() { var b = { then: function(f, fn){ fn(b); } }; specify("rejects with itself", function() { var promise = Promise.cast(b); return promise.then(assert.fail, function(v){ assert(v === b); }); }); }); describe("Implicitly cast thenable", function() { var b = { then: function(f, fn){ fn(b); } }; specify("rejects with itself", function() { return Promise.resolve().then(function(){ return b; }).then(assert.fail, function(v){ assert(v === b); }); }); }); /*! * Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ describe("propagation", function () { it("propagate through then with no callback", function () { return Promise.resolve(10) .then() .then(function (ten) { assert.equal(ten,10); }); }); it("propagate through then with modifying callback", function () { return Promise.resolve(10) .then(function (ten) { return ten + 10; }) .then(function (twen) { assert.equal(twen,20); }); }); it("errback recovers from exception", function () { var error = new Error("Bah!"); return Promise.reject(error) .then(null, function (_error) { assert.equal(_error,error); return 10; }) .then(function (value) { assert.equal(value,10); }); }); it("rejection propagates through then with no errback", function () { var error = new Error("Foolish mortals!"); return Promise.reject(error) .then() .then(null, function (_error) { assert.equal(_error,error); }); }); it("rejection intercepted and rethrown", function () { var error = new Error("Foolish mortals!"); var nextError = new Error("Silly humans!"); return Promise.reject(error) .caught(function () { throw nextError; }) .then(null, function (_error) { assert.equal(_error,nextError); }); }); it("resolution is forwarded through deferred promise", function () { var a = Promise.defer(); var b = Promise.defer(); a.resolve(b.promise); b.resolve(10); return a.promise.then(function (eh) { assert.equal(eh, 10); }); }); }); /* Based on When.js tests Open Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2011 Brian Cavalier 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ var assert = require("assert"); var testUtils = require("./helpers/util.js"); var sentinel = {}; var other = {}; describe("Promise.defer-test", function () { specify("should fulfill with an immediate value", function() { var d = Promise.defer(); d.resolve(sentinel); return d.promise.then( function(val) { assert.equal(val, sentinel); }, assert.fail ); }); specify("should return a promise for the resolution value", function() { var d = Promise.defer(); d.resolve(sentinel); return d.promise.then( function(returnedPromiseVal) { assert.deepEqual(returnedPromiseVal, sentinel); }, assert.fail ); }); specify("should return a promise for a promised resolution value", function() { var d = Promise.defer(); d.resolve(Promise.resolve(sentinel)) return d.promise.then( function(returnedPromiseVal) { assert.deepEqual(returnedPromiseVal, sentinel); }, assert.fail ); }); specify("should return a promise for a promised rejection value", function() { var d = Promise.defer(); // Both the returned promise, and the deferred's own promise should // be rejected with the same value d.resolve(Promise.reject(sentinel)) return d.promise.then( assert.fail, function(returnedPromiseVal) { assert.deepEqual(returnedPromiseVal, sentinel); } ); }); specify("should invoke newly added callback when already resolved", function() { var d = Promise.defer(); d.resolve(sentinel); return d.promise.then( function(val) { assert.equal(val, sentinel); }, assert.fail ); }); specify("should reject with an immediate value", function() { var d = Promise.defer(); d.reject(sentinel); return d.promise.then( assert.fail, function(val) { assert.equal(val, sentinel); } ); }); specify("should reject with fulfilled promised", function() { var d, expected; d = Promise.defer(); expected = testUtils.fakeResolved(sentinel); var ret = d.promise.then( assert.fail, function(val) { assert.equal(val, expected); } ); d.reject(expected); return ret; }); specify("should reject with rejected promise", function() { var d, expected; d = Promise.defer(); expected = testUtils.fakeRejected(sentinel); var ret = d.promise.then( assert.fail, function(val) { assert.equal(val, expected); } ); d.reject(expected); return ret; }); specify("should return a promise for the rejection value", function() { var d = Promise.defer(); // Both the returned promise, and the deferred's own promise should // be rejected with the same value d.reject(sentinel); return d.promise.then( assert.fail, function(returnedPromiseVal) { assert.deepEqual(returnedPromiseVal, sentinel); } ); }); specify("should invoke newly added errback when already rejected", function() { var d = Promise.defer(); d.reject(sentinel); return d.promise.then( assert.fail, function (val) { assert.deepEqual(val, sentinel); } ); }); }); describe("Promise.fromNode", function() { specify("rejects thrown errors from resolver", function() { var err = new Error(); return Promise.fromNode(function(callback) { throw err; }).then(assert.fail, function(e) { assert.strictEqual(err, e); }); }); specify("rejects rejections as operational errors", function() { var err = new Error(); return Promise.fromNode(function(callback) { callback(err); }).caught(Promise.OperationalError, function(e) { assert.strictEqual(err, e.cause); }); }); specify("resolves normally", function() { var result = {}; return Promise.fromNode(function(callback) { callback(null, result); }).then(function(res) { assert.strictEqual(result, res); }); }); specify("resolves with bound thunk", function() { var nodeFn = function(param, cb) { setTimeout(function() { cb(null, param); }, 1); }; return Promise.fromNode(nodeFn.bind(null, 1)).then(function(res) { assert.strictEqual(1, res); }); }); specify("multiArgs option enabled single value", function() { var nodeFn = function(cb) { cb(null, 1); }; return Promise.fromNode(function(callback) { nodeFn(callback); }, {multiArgs: true}).then(function(value) { assert.deepEqual([1], value); }); }); specify("multiArgs option enabled multi value", function() { var nodeFn = function(cb) { cb(null, 1, 2, 3); }; return Promise.fromNode(function(callback) { nodeFn(callback); }, {multiArgs: true}).then(function(value) { assert.deepEqual([1,2,3], value); }); }); specify("multiArgs option disabled single value", function() { var nodeFn = function(cb) { cb(null, 1); }; return Promise.fromNode(function(callback) { nodeFn(callback); }).then(function(value) { assert.strictEqual(1, value); }); }); specify("multiArgs option disabled multi value", function() { var nodeFn = function(cb) { cb(null, 1, 2, 3); }; return Promise.fromNode(function(callback) { nodeFn(callback); }).then(function(value) { assert.strictEqual(1, value); }); }); }); ================================================ FILE: test/mocha/schedule.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var schedule = require("../../js/debug/schedule"); var isNodeJS = testUtils.isNodeJS; describe("schedule", function () { if (isNodeJS) { describe("for Node.js", function () { it("should preserve the active domain", function() { var domain = require("domain"); var activeDomain = domain.create(); return new Promise(function(resolve) { activeDomain.run(function () { schedule(function () { assert(domain.active); assert.equal(domain.active, activeDomain); resolve(); }); }); }); }); }); describe("Promise.setScheduler", function() { it("should work with synchronous scheduler", function() { var prev = Promise.setScheduler(function(task) { task(); }); var success = false; Promise.resolve().then(function() { success = true; }); assert(success); Promise.setScheduler(prev); }); it("should throw for non function", function() { try { Promise.setScheduler({}); } catch (e) { return Promise.resolve(); } assert.fail(); }); }); } }); ================================================ FILE: test/mocha/settle.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); /*! * Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ describe("allSettled", function () { it("works on an empty array", function () { return Promise.settle([]) .then(function (snapshots) { assert.deepEqual(snapshots, []); }); }); it("deals with a mix of non-promises and promises", function () { return Promise.settle([1, Promise.resolve(2), Promise.reject(3)]) .then(function (snapshots) { assert.equal(snapshots[0].value(), 1); assert.equal(snapshots[1].value(), 2); assert.equal(snapshots[2].error(), 3); }); }); it("is settled after every constituent promise is settled", function () { var toFulfill = Promise.defer(); var toReject = Promise.defer(); var promises = [toFulfill.promise, toReject.promise]; var fulfilled; var rejected; Promise.attempt(function () { toReject.reject(); rejected = true; }) .delay(1) .then(function () { toFulfill.resolve(); fulfilled = true; }); return Promise.settle(promises) .then(function () { assert.equal(fulfilled, true); assert.equal(rejected, true); }); }); it("does not modify the input array", function () { var input = [1, Promise.resolve(2), Promise.reject(3)]; return Promise.settle(input) .then(function (snapshots) { assert.notEqual(snapshots, input); assert.equal(snapshots[0].value(), 1); assert.equal(snapshots[1].value(), 2); assert.equal(snapshots[2].error(), 3); }); }); }); /* Based on When.js tests Open Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2011 Brian Cavalier 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ var assert = require("assert"); var testUtils = require("./helpers/util.js"); var sentinel = {}; var other = {}; describe("Promise.settle-test", function () { Promise.promise = function(rs){ var a = Promise.defer(); rs(a); return a.promise; }; specify("should settle empty array", function() { return Promise.settle([]).then(function(settled) { assert.deepEqual(settled.length, 0); }); }); specify("should reject if promise for input array rejects", function() { return Promise.settle(Promise.reject(sentinel)).then( assert.fail, function(reason) { assert.equal(reason, sentinel); } ); }); specify("should settle values", function() { var array = [0, 1, sentinel]; return Promise.settle(array).then(function(settled) { testUtils.assertFulfilled(settled[0], 0); testUtils.assertFulfilled(settled[1], 1); testUtils.assertFulfilled(settled[2], sentinel); }); }); specify("should settle promises", function() { var array = [0, Promise.resolve(sentinel), Promise.reject(sentinel)]; return Promise.settle(array).then(function(settled) { testUtils.assertFulfilled(settled[0], 0); testUtils.assertFulfilled(settled[1], sentinel); testUtils.assertRejected(settled[2], sentinel); }); }); specify("returned promise should fulfill once all inputs settle", function() { var array, p1, p2, resolve, reject; p1 = Promise.promise(function(r) { resolve = function(a){r.fulfill(a);}; }); p2 = Promise.promise(function(r) { reject = function(a){r.reject(a);}; }); array = [0, p1, p2]; setTimeout(function() { resolve(sentinel); }, 0); setTimeout(function() { reject(sentinel); }, 0); return Promise.settle(array).then(function(settled) { testUtils.assertFulfilled(settled[0], 0); testUtils.assertFulfilled(settled[1], sentinel); testUtils.assertRejected(settled[2], sentinel); }); }); }); ================================================ FILE: test/mocha/some.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.some", function(){ it("should reject on negative number", function(){ return Promise.some([1,2,3], -1) .then(assert.fail) .caught(Promise.TypeError, function(){ }); }); it("should reject on NaN", function(){ return Promise.some([1,2,3], -0/0) .then(assert.fail) .caught(Promise.TypeError, function(){ }); }); it("should reject on non-array", function(){ return Promise.some({}, 2) .then(assert.fail) .caught(Promise.TypeError, function(){ }); }); it("should reject with rangeerror when impossible to fulfill", function(){ return Promise.some([1,2,3], 4) .then(assert.fail) .caught(Promise.RangeError, function(e){ }); }); it("should fulfill with empty array with 0", function(){ return Promise.some([1,2,3], 0).then(function(result){ assert.deepEqual(result, []); }); }); }); /* Based on When.js tests Open Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2011 Brian Cavalier 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ var RangeError = Promise.RangeError; describe("Promise.some-test", function () { specify("should reject empty input", function() { return Promise.some([], 1).caught(RangeError, function() { }); }); specify("should resolve values array", function() { var input = [1, 2, 3]; return Promise.some(input, 2).then( function(results) { assert(testUtils.isSubset(results, input)); }, assert.fail ) }); specify("should resolve promises array", function() { var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]; return Promise.some(input, 2).then( function(results) { assert(testUtils.isSubset(results, [1, 2, 3])); }, assert.fail ) }); specify("should not resolve sparse array input", function() { var input = [, 1, , 2, 3 ]; return Promise.some(input, 2).then( function(results) { assert.deepEqual(results, [void 0, 1]); }, function() { console.error(arguments); assert.fail(); } ) }); specify("should reject with all rejected input values if resolving howMany becomes impossible", function() { var input = [Promise.resolve(1), Promise.reject(2), Promise.reject(3)]; return Promise.some(input, 2).then( assert.fail, function(err) { //Cannot use deep equality in IE8 because non-enumerable properties are not //supported assert(err[0] === 2); assert(err[1] === 3); } ) }); specify("should reject with aggregateError", function() { var input = [Promise.resolve(1), Promise.reject(2), Promise.reject(3)]; var AggregateError = Promise.AggregateError; return Promise.some(input, 2) .then(assert.fail) .caught(AggregateError, function(e) { assert(e[0] === 2); assert(e[1] === 3); assert(e.length === 2); }); }); specify("aggregate error should be caught in .error", function() { var input = [Promise.resolve(1), Promise.reject(2), Promise.reject(3)]; var AggregateError = Promise.AggregateError; return Promise.some(input, 2) .then(assert.fail) .error(function(e) { assert(e[0] === 2); assert(e[1] === 3); assert(e.length === 2); }); }); specify("should accept a promise for an array", function() { var expected, input; expected = [1, 2, 3]; input = Promise.resolve(expected); return Promise.some(input, 2).then( function(results) { assert.deepEqual(results.length, 2); }, assert.fail ) }); specify("should reject when input promise does not resolve to array", function() { return Promise.some(Promise.resolve(1), 1).caught(TypeError, function(e){ }); }); specify("should reject when given immediately rejected promise", function() { var err = new Error(); return Promise.some(Promise.reject(err), 1).then(assert.fail, function(e) { assert.strictEqual(err, e); }); }); }); ================================================ FILE: test/mocha/spread.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); /*! * Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ describe("spread", function () { it("spreads values across arguments", function () { return Promise.resolve([1, 2, 3]).spread(function (a, b) { assert.equal(b,2); }); }); it("spreads promises for arrays across arguments", function () { return Promise.resolve([Promise.resolve(10)]) .all() .spread(function (value) { assert.equal(value,10); }); }); it("spreads arrays of promises across arguments", function () { var deferredA = Promise.defer(); var deferredB = Promise.defer(); var promise = Promise.resolve([deferredA.promise, deferredB.promise]).all().spread( function (a, b) { assert.equal(a,10); assert.equal(b,20); }); Promise.delay(1).then(function () { deferredA.resolve(10); }); Promise.delay(1).then(function () { deferredB.resolve(20); }); return promise; }); it("spreads arrays of thenables across arguments", function () { var p1 = { then: function(v) { v(10); } }; var p2 = { then: function(v) { v(20); } }; var promise = Promise.resolve([p1, p2]).all().spread(function (a, b) { assert.equal(a,10); assert.equal(b,20); }); return promise; }); it("should wait for promises in the returned array even when not calling .all", function() { var d1 = Promise.defer(); var d2 = Promise.defer(); var d3 = Promise.defer(); setTimeout(function(){ d1.resolve(1); d2.resolve(2); d3.resolve(3); }, 1); return Promise.resolve().then(function(){ return [d1.promise, d2.promise, d3.promise]; }).all().spread(function(a, b, c){ assert(a === 1); assert(b === 2); assert(c === 3); }); }); it("should wait for thenables in the returned array even when not calling .all", function() { var t1 = { then: function(fn) { setTimeout(function(){ fn(1); }, 1); } }; var t2 = { then: function(fn) { setTimeout(function(){ fn(2); }, 1); } }; var t3 = { then: function(fn) { setTimeout(function(){ fn(3); }, 1); } }; return Promise.resolve().then(function(){ return [t1, t2, t3]; }).all().spread(function(a, b, c){ assert(a === 1); assert(b === 2); assert(c === 3); }); }); it("should wait for promises in an array that a returned promise resolves to even when not calling .all", function() { var d1 = Promise.defer(); var d2 = Promise.defer(); var d3 = Promise.defer(); var defer = Promise.defer(); setTimeout(function(){ defer.resolve([d1.promise, d2.promise, d3.promise]); setTimeout(function(){ d1.resolve(1); d2.resolve(2); d3.resolve(3); }, 1); }, 1); return Promise.resolve().then(function(){ return defer.promise; }).all().spread(function(a, b, c){ assert(a === 1); assert(b === 2); assert(c === 3); }); }); it("should wait for thenables in an array that a returned thenable resolves to even when not calling .all", function() { var t1 = { then: function(fn) { setTimeout(function(){ fn(1); }, 1); } }; var t2 = { then: function(fn) { setTimeout(function(){ fn(2); }, 1); } }; var t3 = { then: function(fn) { setTimeout(function(){ fn(3); }, 1); } }; var thenable = { then: function(fn) { setTimeout(function(){ fn([t1, t2, t3]) }, 1); } }; return Promise.resolve().then(function(){ return thenable; }).all().spread(function(a, b, c){ assert(a === 1); assert(b === 2); assert(c === 3); }); }); it("should reject with error when non array is the ultimate value to be spread", function(){ return Promise.resolve().then(function(){ return 3 }).spread(function(a, b, c){ assert.fail(); }).then(assert.fail, function(e){ }) }); specify("gh-235", function() { var P = Promise; return P.resolve(1).then(function(x) { return [x, P.resolve(2)] }).spread(function(x, y) { return P.all([P.resolve(3), P.resolve(4)]); }).then(function(a) { assert.deepEqual([3, 4], a); }); }) specify("error when passed non-function", function() { return Promise.resolve(3) .spread() .then(assert.fail) .caught(Promise.TypeError, function() {}); }); specify("error when resolution is non-spredable", function() { return Promise.resolve(3) .spread(function(){}) .then(assert.fail) .caught(Promise.TypeError, function() {}); }); }); /* Based on When.js tests Open Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2011 Brian Cavalier 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ describe("Promise.spread-test", function () { var slice = [].slice; specify("should return a promise", function() { assert(typeof (Promise.defer().promise.spread(function(){}).then) === "function"); }); specify("should apply onFulfilled with array as argument list", function() { var expected = [1, 2, 3]; return Promise.resolve(expected).spread(function() { assert.deepEqual(slice.call(arguments), expected); }); }); specify("should resolve array contents", function() { var expected = [Promise.resolve(1), 2, Promise.resolve(3)]; return Promise.resolve(expected).all().spread(function() { assert.deepEqual(slice.call(arguments), [1, 2, 3]); }); }); specify("should reject if any item in array rejects", function() { var expected = [Promise.resolve(1), 2, Promise.reject(3)]; return Promise.resolve(expected).all() .spread(assert.fail) .then(assert.fail, function() {}); }); specify("should apply onFulfilled with array as argument list", function() { var expected = [1, 2, 3]; return Promise.resolve(Promise.resolve(expected)).spread(function() { assert.deepEqual(slice.call(arguments), expected); }); }); specify("should resolve array contents", function() { var expected = [Promise.resolve(1), 2, Promise.resolve(3)]; return Promise.resolve(Promise.resolve(expected)).all().spread(function() { assert.deepEqual(slice.call(arguments), [1, 2, 3]); }); }); specify("should reject if input is a rejected promise", function() { var expected = Promise.reject([1, 2, 3]); return Promise.resolve(expected) .spread(assert.fail) .then(assert.fail, function() {}); }); }); ================================================ FILE: test/mocha/synchronous_inspection.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("Promise.prototype.toJSON", function() { it("should match pending state", function() { var a = new Promise(function(){}).toJSON(); assert.strictEqual(a.isFulfilled, false); assert.strictEqual(a.isRejected, false); assert.strictEqual(a.rejectionReason, undefined); assert.strictEqual(a.fulfillmentValue, undefined); }); it("should match rejected state", function() { var a = Promise.reject(3).toJSON(); assert.strictEqual(a.isFulfilled, false); assert.strictEqual(a.isRejected, true); assert.strictEqual(a.rejectionReason, 3); assert.strictEqual(a.fulfillmentValue, undefined); }); it("should match fulfilled state", function() { var a = Promise.resolve(3).toJSON(); assert.strictEqual(a.isFulfilled, true); assert.strictEqual(a.isRejected, false); assert.strictEqual(a.rejectionReason, undefined); assert.strictEqual(a.fulfillmentValue, 3); }); }); /*! * Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // In browsers that support strict mode, it'll be `undefined`; otherwise, the global. var calledAsFunctionThis = (function () { return this; }()); describe("inspect", function () { it("for a fulfilled promise", function () { var ret = Promise.resolve(10); assert.equal(ret.value(), 10); assert.equal(ret.isFulfilled(), true); }); it("for a rejected promise", function () { var e = new Error("In your face."); var ret = Promise.reject(e); assert.equal(ret.reason(), e); assert.equal(ret.isRejected(), true); return ret.then(assert.fail, function(){}); }); it("for a pending, unresolved promise", function () { var pending = Promise.defer().promise; assert.equal(pending.isPending(), true); }); it("for a promise resolved to a rejected promise", function () { var deferred = Promise.defer(); var error = new Error("Rejected!"); var reject = Promise.reject(error); deferred.resolve(reject); assert.equal(deferred.promise.isRejected(), true); assert.equal(deferred.promise.reason(), error); return deferred.promise.then(assert.fail, function(){}); }); it("for a promise resolved to a fulfilled promise", function () { var deferred = Promise.defer(); var fulfilled = Promise.resolve(10); deferred.resolve(fulfilled); assert.equal(deferred.promise.isFulfilled(), true); assert.equal(deferred.promise.value(), 10); }); it("for a promise resolved to a pending promise", function () { var a = Promise.defer(); var b = Promise.defer(); a.resolve(b.promise); assert.equal(a.promise.isPending(), true); }); describe(".value()", function() { specify("of unfulfilled inspection should throw", function() { Promise.reject(1).reflect().then(function(inspection) { try { inspection.value(); } catch (e) { return Promise.resolve(); } assert.fail(); }); }); specify("of unfulfilled promise should throw", function() { var r = Promise.reject(1); r.reason(); try { r.value(); } catch (e) { return Promise.resolve(); } assert.fail(); }); }); describe(".reason()", function() { specify("of unrejected inspection should throw", function() { Promise.resolve(1).reflect().then(function(inspection) { try { inspection.reason(); } catch (e) { return Promise.resolve(); } assert.fail(); }); }); specify("of unrejected promise should throw", function() { try { Promise.resolve(1).reason(); } catch (e) { return Promise.resolve(); } assert.fail(); }); }); }); ================================================ FILE: test/mocha/tap.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); describe("tap", function () { specify("passes through value", function() { return Promise.resolve("test").tap(function() { return 3; }).then(function(value){ assert.equal(value, "test"); }); }); specify("passes through value after returned promise is fulfilled", function() { var async = false; return Promise.resolve("test").tap(function() { return new Promise(function(r) { setTimeout(function(){ async = true; r(3); }, 1); }); }).then(function(value){ assert(async); assert.equal(value, "test"); }); }); specify("is not called on rejected promise", function() { var called = false; return Promise.reject("test").tap(function() { called = true; }).then(assert.fail, function(value){ assert(!called); }); }); specify("passes immediate rejection", function() { var err = new Error(); return Promise.resolve("test").tap(function() { throw err; }).tap(assert.fail).then(assert.fail, function(e){ assert(err === e); }); }); specify("passes eventual rejection", function() { var err = new Error(); return Promise.resolve("test").tap(function() { return new Promise(function(_, rej) { setTimeout(function(){ rej(err); }, 1) }); }).tap(assert.fail).then(assert.fail, function(e) { assert(err === e); }); }); specify("passes value", function() { return Promise.resolve(123).tap(function(a) { assert(a === 123); }); }); }); ================================================ FILE: test/mocha/tapCatch.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); function rejection() { var error = new Error("test"); var rejection = Promise.reject(error); rejection.err = error; return rejection; } describe("tapCatch", function () { specify("passes through rejection reason", function() { return rejection().tapCatch(function() { return 3; }).caught(function(value) { assert.equal(value.message, "test"); }); }); specify("passes through reason after returned promise is fulfilled", function() { var async = false; return rejection().tapCatch(function() { return new Promise(function(r) { setTimeout(function(){ async = true; r(3); }, 1); }); }).caught(function(value) { assert(async); assert.equal(value.message, "test"); }); }); specify("is not called on fulfilled promise", function() { var called = false; return Promise.resolve("test").tapCatch(function() { called = true; }).then(function(value){ assert(!called); }, assert.fail); }); specify("passes immediate rejection", function() { var err = new Error(); return rejection().tapCatch(function() { throw err; }).tap(assert.fail).then(assert.fail, function(e) { assert(err === e); }); }); specify("passes eventual rejection", function() { var err = new Error(); return rejection().tapCatch(function() { return new Promise(function(_, rej) { setTimeout(function(){ rej(err); }, 1) }); }).tap(assert.fail).then(assert.fail, function(e) { assert(err === e); }); }); specify("passes reason", function() { return rejection().tapCatch(function(a) { assert(a === rejection); }).then(assert.fail, function() {}); }); specify("Works with predicates", function() { var called = false; return Promise.reject(new TypeError).tapCatch(TypeError, function(a) { called = true; assert(err instanceof TypeError) }).then(assert.fail, function(err) { assert(called === true); assert(err instanceof TypeError); }); }); specify("Does not get called on predicates that don't match", function() { var called = false; return Promise.reject(new TypeError).tapCatch(ReferenceError, function(a) { called = true; }).then(assert.fail, function(err) { assert(called === false); assert(err instanceof TypeError); }); }); specify("Supports multiple predicates", function() { var calledA = false; var calledB = false; var calledC = false; var promiseA = Promise.reject(new ReferenceError).tapCatch( ReferenceError, TypeError, function (e) { assert(e instanceof ReferenceError); calledA = true; } ).catch(function () {}); var promiseB = Promise.reject(new TypeError).tapCatch( ReferenceError, TypeError, function (e) { assert(e instanceof TypeError); calledB = true; } ).catch(function () {}); var promiseC = Promise.reject(new SyntaxError).tapCatch( ReferenceError, TypeError, function (e) { calledC = true; } ).catch(function () {}); return Promise.join(promiseA, promiseB, promiseC, function () { assert(calledA === true); assert(calledB === true); assert(calledC === false); }); }) }); ================================================ FILE: test/mocha/timers.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var Q = Promise; Promise.config({cancellation: true}) var globalObject = typeof window !== "undefined" ? window : new Function("return this;")(); /* Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ describe("timeout", function () { it("should do nothing if the promise fulfills quickly", function() { Promise.delay(1).timeout(200).then(function(){ }); }); it("should do nothing if the promise rejects quickly", function() { var goodError = new Error("haha!"); return Promise.delay(1) .then(function () { throw goodError; }) .timeout(200) .then(undefined, function (error) { assert(error === goodError); }); }); it("should reject with a timeout error if the promise is too slow", function() { return Promise.delay(1) .timeout(10) .caught(Promise.TimeoutError, function(){ }) }); it("should reject with a custom timeout error if the promise is too slow and msg was provided", function() { return Promise.delay(1) .timeout(10, "custom") .caught(Promise.TimeoutError, function(e){ assert(/custom/i.test(e.message)); }); }); it("should cancel the parent promise once the timeout expires", function() { var didNotExecute = true; var wasRejectedWithTimeout = false; var p = Promise.delay(22).then(function() { didNotExecute = false; }) p.timeout(11).thenReturn(10).caught(Promise.TimeoutError, function(e) { wasRejectedWithTimeout = true; }) return Promise.delay(33).then(function() { assert(didNotExecute, "parent promise was not cancelled"); assert(wasRejectedWithTimeout, "promise was not rejected with timeout"); }) }); it("should not cancel the parent promise if there are multiple consumers", function() { var derivedNotCancelled = false; var p = Promise.delay(22); var derived = p.then(function() { derivedNotCancelled = true; }) p.timeout(11).thenReturn(10) return Promise.delay(33).then(function() { assert(derivedNotCancelled, "derived promise was cancelled") }) }) var globalsAreReflectedInGlobalObject = (function(window) { var fn = function(id){return clearTimeout(id);}; var old = window.clearTimeout; window.clearTimeout = fn; var ret = clearTimeout === fn; window.clearTimeout = old; return ret; })(globalObject); if (globalsAreReflectedInGlobalObject) { describe("timer handle clearouts", function() { var fakeSetTimeout, fakeClearTimeout; var expectedHandleType; before(function() { fakeSetTimeout = globalObject.setTimeout; fakeClearTimeout = globalObject.clearTimeout; globalObject.setTimeout = globalObject.oldSetTimeout; globalObject.clearTimeout = globalObject.oldClearTimeout; expectedHandleType = typeof (globalObject.setTimeout(function(){}, 1)); }); after(function() { globalObject.setTimeout = fakeSetTimeout; globalObject.clearTimeout = fakeClearTimeout; }); it("should clear timeouts with proper handle type when fulfilled", function() { var old = globalObject.clearTimeout; var handleType = "empty"; globalObject.clearTimeout = function(handle) { handleType = typeof handle; globalObject.clearTimeout = old; }; return Promise.delay(1).timeout(10000).then(function() { assert.strictEqual(expectedHandleType, handleType); }); }); it("should clear timeouts with proper handle type when rejected", function() { var old = globalObject.clearTimeout; var handleType = "empty"; globalObject.clearTimeout = function(handle) { handleType = typeof handle; globalObject.clearTimeout = old; }; return new Promise(function(_, reject) { setTimeout(reject, 10); }).timeout(10000).then(null, function() { assert.strictEqual(expectedHandleType, handleType); }); }); }) } }); describe("delay", function () { it("should not delay rejection", function() { var promise = Promise.reject(5).delay(1); promise.then(assert.fail, function(){}); return Promise.delay(1).then(function () { assert(!promise.isPending()); }); }); it("should delay after resolution", function () { var promise1 = Promise.delay(1, "what"); var promise2 = promise1.delay(1); return promise2.then(function (value) { assert(value === "what"); }); }); it("should resolve follower promise's value", function() { var resolveF; var f = new Promise(function() { resolveF = arguments[0]; }); var v = new Promise(function(f) { setTimeout(function() { f(3); }, 1); }); resolveF(v); return Promise.delay(1, f).then(function(value) { assert.equal(value, 3); }); }); it("should reject with a custom error if an error was provided as a parameter", function() { var err = Error("Testing Errors") return Promise.delay(1) .timeout(10, err) .caught(function(e){ assert(e === err); }); }); }); ================================================ FILE: test/mocha/try.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var obj = {}; var error = new Error(); var thrower = function() { throw error; }; var identity = function(val) { return val; }; var array = function() { return [].slice.call(arguments); }; var receiver = function() { return this; }; var tryy = Promise["try"]; describe("Promise.attempt", function(){ specify("should reject when the function throws", function() { var async = false; var ret = tryy(thrower).then(assert.fail, function(e) { assert(async); assert(e === error); }); async = true; return ret; }); specify("should reject when the function is not a function", function() { var async = false; var ret = tryy(null).then(assert.fail, function(e) { assert(async); assert(e instanceof Promise.TypeError); }); async = true; return ret; }); specify("should unwrap returned promise", function(){ var d = Promise.defer(); var ret = tryy(function(){ return d.promise; }).then(function(v){ assert(v === 3); }) setTimeout(function(){ d.fulfill(3); }, 1); return ret; }); specify("should unwrap returned thenable", function(){ return tryy(function(){ return { then: function(f, v) { f(3); } } }).then(function(v){ assert(v === 3); }); }); }); ================================================ FILE: test/mocha/unhandled_rejections.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var noop = testUtils.noop; var isStrictModeSupported = testUtils.isStrictModeSupported; var onUnhandledFail = testUtils.onUnhandledFail; var onUnhandledSucceed = testUtils.onUnhandledSucceed; function yesE() { return new Error(); } function notE() { var rets = [{}, []]; return rets[Math.random()*rets.length|0]; } function cleanUp() { Promise.onPossiblyUnhandledRejection(null); Promise.onUnhandledRejectionHandled(null); } function setupCleanUps() { beforeEach(cleanUp); afterEach(cleanUp); } describe("Will report rejections that are not handled in time", function() { setupCleanUps(); specify("Immediately rejected not handled at all", function testFunction() { var promise = Promise.defer(); promise.reject(yesE()); return onUnhandledSucceed(); }); specify("Eventually rejected not handled at all", function testFunction() { var promise = Promise.defer(); setTimeout(function(){ promise.reject(yesE()); }, 1); return onUnhandledSucceed(); }); specify("Immediately rejected handled too late", function testFunction() { var promise = Promise.defer(); promise.reject(yesE()); setTimeout(function() { promise.promise.then(assert.fail, function(){}); }, 150); return onUnhandledSucceed(); }); specify("Eventually rejected handled too late", function testFunction() { var promise = Promise.defer(); setTimeout(function(){ promise.reject(yesE()); setTimeout(function() { promise.promise.then(assert.fail, function(){}); }, 150); }, 1); return onUnhandledSucceed(); }); }); describe("Will report rejections that are code errors", function() { setupCleanUps(); specify("Immediately fulfilled handled with erroneous code", function testFunction() { var deferred = Promise.defer(); var promise = deferred.promise; deferred.fulfill(null); promise.then(function(itsNull){ itsNull.will.fail.four.sure(); }); return onUnhandledSucceed(); }); specify("Eventually fulfilled handled with erroneous code", function testFunction() { var deferred = Promise.defer(); var promise = deferred.promise; setTimeout(function(){ deferred.fulfill(null); }, 1); promise.then(function(itsNull){ itsNull.will.fail.four.sure(); }); return onUnhandledSucceed(); }); specify("Already fulfilled handled with erroneous code but then recovered and failDeferred again", function testFunction() { var err = yesE(); var promise = Promise.resolve(null); promise.then(function(itsNull){ itsNull.will.fail.four.sure(); }).then(assert.fail, function(e){ assert.ok(e instanceof Promise.TypeError); }).then(function(){ //then assert.failing again //this error should be reported throw err; }); return onUnhandledSucceed(err); }); specify("Immediately fulfilled handled with erroneous code but then recovered and failDeferred again", function testFunction() { var err = yesE(); var deferred = Promise.defer(); var promise = deferred.promise; deferred.fulfill(null); promise.then(function(itsNull){ itsNull.will.fail.four.sure(); }).then(assert.fail, function(e){ assert.ok(e instanceof Promise.TypeError) //Handling the type error here }).then(function(){ //then assert.failing again //this error should be reported throw err; }); return onUnhandledSucceed(err); }); specify("Eventually fulfilled handled with erroneous code but then recovered and failDeferred again", function testFunction() { var err = yesE(); var deferred = Promise.defer(); var promise = deferred.promise; promise.then(function(itsNull){ itsNull.will.fail.four.sure(); }).then(assert.fail, function(e){ assert.ok(e instanceof Promise.TypeError) //Handling the type error here }).then(function(){ //then assert.failing again //this error should be reported throw err; }); setTimeout(function(){ deferred.fulfill(null); }, 1); return onUnhandledSucceed(err); }); specify("Already fulfilled handled with erroneous code but then recovered in a parallel handler and failDeferred again", function testFunction() { var err = yesE(); var promise = Promise.resolve(null); promise.then(function(itsNull){ itsNull.will.fail.four.sure(); }).then(assert.fail, function(e){ assert.ok(e instanceof Promise.TypeError) }); promise.then(function(){ //then assert.failing again //this error should be reported throw err; }); return onUnhandledSucceed(err); }); }); describe("Will report rejections that are not instanceof Error", function() { setupCleanUps(); specify("Immediately rejected with non instanceof Error", function testFunction() { var failDeferred = Promise.defer(); failDeferred.reject(notE()); return onUnhandledSucceed(); }); specify("Eventually rejected with non instanceof Error", function testFunction() { var failDeferred = Promise.defer(); setTimeout(function(){ failDeferred.reject(notE()); }, 1); return onUnhandledSucceed(); }); }); describe("Will handle hostile rejection reasons like frozen objects", function() { setupCleanUps(); specify("Immediately rejected with non instanceof Error", function testFunction() { var failDeferred = Promise.defer(); failDeferred.reject(Object.freeze({})); return onUnhandledSucceed(function(e) { return true; }); }); specify("Eventually rejected with non instanceof Error", function testFunction() { var failDeferred = Promise.defer(); var obj = {}; setTimeout(function(){ failDeferred.reject(Object.freeze(obj)); }, 1); return onUnhandledSucceed(function(e) { return e === obj; }); }); }); describe("Will not report rejections that are handled in time", function() { setupCleanUps(); specify("Already rejected handled", function testFunction() { var failDeferred = Promise.reject(yesE()).caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Immediately rejected handled", function testFunction() { var failDeferred = Promise.defer(); failDeferred.promise.caught(noop); failDeferred.reject(yesE()); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Eventually rejected handled", function testFunction() { var failDeferred = Promise.defer(); setTimeout(function() { failDeferred.reject(yesE()); }, 1); failDeferred.promise.caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Already rejected handled in a deep sequence", function testFunction() { var failDeferred = Promise.reject(yesE()); failDeferred .then(function(){}) .then(function(){}, null, function(){}) .then() .then(function(){}) .caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Immediately rejected handled in a deep sequence", function testFunction() { var failDeferred = Promise.defer(); failDeferred.promise.then(function(){}) .then(function(){}, null, function(){}) .then() .then(function(){}) .caught(noop); failDeferred.reject(yesE()); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Eventually handled in a deep sequence", function testFunction() { var failDeferred = Promise.defer(); setTimeout(function() { failDeferred.reject(yesE()); }, 1); failDeferred.promise.then(function(){}) .then(function(){}, null, function(){}) .then() .then(function(){}) .caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Already rejected handled in a middle parallel deep sequence", function testFunction() { var failDeferred = Promise.reject(yesE()); failDeferred .then(function(){}) .then(function(){}, null, function(){}) .then() .then(function(){}); failDeferred .then(function(){}) .then(function(){}, null, function(){}) .then(assert.fail, function(){ }); failDeferred .then(function(){}) .then(function(){}, null, function(){}) .then() .then(function(){}); return onUnhandledSucceed(undefined, 2); }); specify("Immediately rejected handled in a middle parallel deep sequence", function testFunction() { var failDeferred = Promise.defer(); failDeferred.promise .then(function(){}) .then(function(){}, null, function(){}) .then() .then(function(){}); failDeferred.promise .then(function(){}) .then(function(){}, null, function(){}) .then(assert.fail, function(){ }); failDeferred.promise .then(function(){}) .then(function(){}, null, function(){}) .then() .then(function(){}); failDeferred.reject(yesE()); return onUnhandledSucceed(undefined, 2); }); specify("Eventually handled in a middle parallel deep sequence", function testFunction() { var failDeferred = Promise.defer(); failDeferred.promise .then(function(){}) .then(function(){}, null, function(){}) .then() .then(function(){}); failDeferred.promise .then(function(){}) .then(function(){}, null, function(){}) .then(assert.fail, function(){ }); failDeferred.promise .then(function(){}) .then(function(){}, null, function(){}) .then() .then(function(){}); setTimeout(function(){ failDeferred.reject(yesE()); }, 1); return onUnhandledSucceed(undefined, 2); }); }); describe("immediate assert.failures without .then", function testFunction() { setupCleanUps(); var err = new Error(''); specify("Promise.reject", function testFunction() { Promise.reject(err); return onUnhandledSucceed(function(e) { return e === err; }); }); specify("new Promise throw", function testFunction() { new Promise(function() { throw err; }); return onUnhandledSucceed(function(e) { return e === err; }); }); specify("new Promise reject", function testFunction() { new Promise(function(_, r) { r(err); }); return onUnhandledSucceed(function(e) { return e === err; }); }); specify("Promise.method", function testFunction() { Promise.method(function() { throw err; })(); return onUnhandledSucceed(function(e) { return e === err; }); }); specify("Promise.all", function testFunction() { Promise.all([Promise.reject(err)]); return onUnhandledSucceed(function(e) { return e === err; }); }); }); describe("immediate assert.failures with .then", function testFunction() { setupCleanUps(); var err = new Error(''); specify("Promise.reject", function testFunction() { Promise.reject(err).caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("new Promise throw", function testFunction() { new Promise(function() { throw err; }).caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("new Promise reject", function testFunction() { new Promise(function(_, r) { r(err); }).caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Promise.method", function testFunction() { Promise.method(function() { throw err; })().caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Promise.all", function testFunction() { Promise.all([Promise.reject("err")]) .caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Promise.all many", function testFunction() { Promise.all([Promise.reject("err"), Promise.reject("err2")]) .caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Promise.all many, latter async", function testFunction() { Promise.all([Promise.reject("err"), Promise.delay(1).thenThrow(new Error())]) .caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Promise.all many pending", function testFunction() { var a = new Promise(function(v, w){ setTimeout(function(){w("err");}, 1); }); var b = new Promise(function(v, w){ setTimeout(function(){w("err2");}, 1); }); Promise.all([a, b]) .caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("Already rejected promise for a collection", function testFunction(){ Promise.settle(Promise.reject(err)) .caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); }); describe("gh-118", function() { setupCleanUps(); specify("eventually rejected promise", function testFunction() { Promise.resolve().then(function() { return new Promise(function(_, reject) { setTimeout(function() { reject(13); }, 1); }); }).caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("already rejected promise", function testFunction() { Promise.resolve().then(function() { return Promise.reject(13); }).caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); specify("immediately rejected promise", function testFunction() { Promise.resolve().then(function() { return new Promise(function(_, reject) { reject(13); }); }).caught(noop); return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee); }); }); describe("Promise.onUnhandledRejectionHandled", function() { specify("should be called when unhandled promise is later handled", function() { var unhandledPromises = []; var spy1 = testUtils.getSpy(); var spy2 = testUtils.getSpy(); Promise.onPossiblyUnhandledRejection(spy1(function(reason, promise) { unhandledPromises.push({ reason: reason, promise: promise }); })); Promise.onUnhandledRejectionHandled(spy2(function(promise) { assert.equal(unhandledPromises.length, 1); assert(unhandledPromises[0].promise === promise); assert(promise === a); assert(unhandledPromises[0].reason === reason); })); var reason = new Error("error"); var a = new Promise(function(){ throw reason; }); setTimeout(function() { Promise._unhandledRejectionCheck(); a.then(assert.fail, function(){}); }, 1); return Promise.all([spy1.promise, spy2.promise]); }); }); describe("global events", function() { var attachGlobalHandler, detachGlobalHandlers; if (typeof process !== "undefined" && typeof process.version === "string" && typeof window === "undefined") { attachGlobalHandler = function(name, fn) { process.on(name, fn); }; detachGlobalHandlers = function() { process.removeAllListeners("unhandledRejection"); process.removeAllListeners("rejectionHandled"); }; } else { attachGlobalHandler = function(name, fn) { window[("on" + name).toLowerCase()] = fn; }; detachGlobalHandlers = function() { window.onunhandledrejection = null; window.onrejectionhandled = null; }; } setupCleanUps(); beforeEach(detachGlobalHandlers); afterEach(detachGlobalHandlers); specify("are fired", function() { return new Promise(function(resolve, reject) { var err = new Error(); var receivedPromise; attachGlobalHandler("unhandledRejection", function(reason, promise) { assert.strictEqual(reason, err); receivedPromise = promise; }); attachGlobalHandler("rejectionHandled", function(promise) { assert.strictEqual(receivedPromise, promise); resolve(); }); var promise = new Promise(function() {throw err;}); setTimeout(function() { Promise._unhandledRejectionCheck(); promise.then(assert.fail, function(){}); }, 1); }); }); specify("are fired with local events", function() { return new Promise(function(resolve, reject) { var expectedOrder = [1, 2, 3, 4]; var order = []; var err = new Error(); var receivedPromises = []; Promise.onPossiblyUnhandledRejection(function(reason, promise) { assert.strictEqual(reason, err); receivedPromises.push(promise); order.push(1); }); Promise.onUnhandledRejectionHandled(function(promise) { assert.strictEqual(receivedPromises[0], promise); order.push(3); }); attachGlobalHandler("unhandledRejection", function(reason, promise) { assert.strictEqual(reason, err); receivedPromises.push(promise); order.push(2); }); attachGlobalHandler("rejectionHandled", function(promise) { assert.strictEqual(receivedPromises[1], promise); order.push(4); assert.deepEqual(expectedOrder, order); assert.strictEqual(receivedPromises.length, 2); resolve(); }); var promise = new Promise(function() {throw err;}); setTimeout(function() { Promise._unhandledRejectionCheck(); promise.then(assert.fail, function(){}); }, 1); }); }); }); var windowDomEventSupported = true; try { var event = document.createEvent("CustomEvent"); event.initCustomEvent("testingtheevent", false, true, {}); self.dispatchEvent(event); } catch (e) { windowDomEventSupported = false; } if (windowDomEventSupported) { describe("dom events", function() { var events = []; beforeEach(detachEvents); afterEach(detachEvents); function detachEvents() { events.forEach(function(e) { self.removeEventListener(e.type, e.fn, false); }); events = []; } function attachEvent(type, fn) { events.push({type: type, fn: fn}); self.addEventListener(type, fn, false); } specify("are fired", function() { return new Promise(function(resolve, reject) { var order = []; var err = new Error(); var promise = Promise.reject(err); attachEvent("unhandledrejection", function(e) { e.preventDefault(); assert.strictEqual(e.detail.promise, promise); assert.strictEqual(e.detail.reason, err); assert.strictEqual(e.promise, promise); assert.strictEqual(e.reason, err); order.push(1); }); attachEvent("unhandledrejection", function(e) { assert.strictEqual(e.detail.promise, promise); assert.strictEqual(e.detail.reason, err); assert.strictEqual(e.promise, promise); assert.strictEqual(e.reason, err); assert.strictEqual(e.defaultPrevented, true); order.push(2); }); attachEvent("rejectionhandled", function(e) { e.preventDefault(); assert.strictEqual(e.detail.promise, promise); assert.strictEqual(e.detail.reason, undefined); assert.strictEqual(e.promise, promise); assert.strictEqual(e.reason, undefined); order.push(3); }); attachEvent("rejectionhandled", function(e) { assert.strictEqual(e.detail.promise, promise); assert.strictEqual(e.detail.reason, undefined); assert.strictEqual(e.promise, promise); assert.strictEqual(e.reason, undefined); assert.strictEqual(e.defaultPrevented, true); order.push(4); resolve(); }); setTimeout(function() { Promise._unhandledRejectionCheck(); promise.then(assert.fail, function(r) { order.push(5); assert.strictEqual(r, err); assert.deepEqual(order, [1,2,3,4,5]); }); }, 1); }); }) }); if (typeof Worker !== "undefined") { describe("dom events in a worker", function() { var worker; beforeEach(function () { worker = new Worker("./worker.js"); }); afterEach(function () { worker.terminate(); }); specify("are fired", function() { var order = []; return new Promise(function(resolve, reject) { worker.onmessage = function (message) { try { switch(message.data) { case "unhandledrejection": order.push(1); break; case "rejectionhandled": order.push(2); resolve(); break; default: throw new Error("unexpected message: " + message); } } catch (e) { reject(e); } }; worker.postMessage("reject"); }).then(function () { assert.deepEqual(order, [1, 2]); }); }); }); } } describe("Unhandled rejection when joining chains with common rejected parent", function testFunction() { specify("GH 645", function() { var aError = new Error('Something went wrong'); var a = Promise.try(function(){ throw aError; }); var b = Promise.try(function(){ throw new Error('Something went wrong here as well'); }); var c = Promise .join(a, b) .spread(function( a, b ){ return a+b; }); var test1 = Promise .join(a, c) .spread(function( a, product ){ // ... }) .caught(Error, function(e) { assert.strictEqual(aError, e); }); var test2 = onUnhandledFail(testFunction); return Promise.all([test1, test2]); }); }); var asyncAwaitSupported = (function() { try { new Function("async function abc() {}"); return true; } catch (e) { return false; } })(); if (asyncAwaitSupported) { describe("No unhandled rejection from async await", function () { setupCleanUps(); specify("gh-1404", function testFunction() { var ret = onUnhandledFail(testFunction); Promise.using(Promise.resolve(), (new Function("Bluebird", "return async function() { await Bluebird.reject(new Error('foo')); }"))(Promise)) .caught(function() {}); return ret; }); }); } describe("issues", function () { setupCleanUps(); specify("GH-1501-1", function testFunction() { var ret = onUnhandledFail(testFunction); Promise.reduce([Promise.resolve("foo"), Promise.reject(new Error("reason"), Promise.resolve("bar"))], function() {}, {}).caught(function() {}); return ret; }); specify("GH-1501-2", function testFunction() { var ret = onUnhandledFail(testFunction); Promise.reduce([Promise.delay(1), Promise.reject(new Error("reason"))], function() {}, {}).caught(function() {}); return ret; }); specify("GH-1501-3", function testFunction() { var ret = onUnhandledFail(testFunction); Promise.reduce([Promise.reject(new Error("reason"))], function() {}, Promise.reject(new Error("reason2"))).caught(function() {}); return ret; }); specify("GH-1487-1", function testFunction() { var ret = onUnhandledFail(testFunction); var p = Promise.reject( new Error('foo') ); Promise.map( p, function() {} ).caught( function() {} ); return ret; }); specify("GH-1487-2", function testFunction() { var ret = onUnhandledFail(testFunction); var arr = [ Promise.reject( new Error('foo') ) ]; Promise.map( arr, function() {} ).caught( function() {} ); return ret; }); specify("GH-1487-3", function testFunction() { var ret = onUnhandledFail(testFunction); var p = Promise.reject( new Error('foo') ); p.map( function() {} ).caught( function() {} ); return ret; }); specify("GH-1487-4", function testFunction() { var ret = onUnhandledFail(testFunction); var arr = [ Promise.reject( new Error('foo') ) ]; var p = Promise.resolve( arr ); p.map( function() {} ).caught( function() {} ); return ret; }); specify("GH-1487-5", function testFunction() { var ret = onUnhandledFail(testFunction); var p = Promise.reject( new Error('foo') ); Promise.filter( p, function() {} ).caught( function() {} ); return ret; }); specify("GH-1487-6", function testFunction() { var ret = onUnhandledFail(testFunction); var arr = [ Promise.reject( new Error('foo') ) ]; Promise.filter( arr, function() {} ).caught( function() {} ); return ret; }); specify("GH-1487-7", function testFunction() { var ret = onUnhandledFail(testFunction); var p = Promise.reject( new Error('foo') ); p.filter( function() {} ).caught( function() {} ); return ret; }); specify("GH-1487-8", function testFunction() { var ret = onUnhandledFail(testFunction); var arr = [ Promise.reject( new Error('foo') ) ]; var p = Promise.resolve( arr ); p.filter( function() {} ).caught( function() {} ); return ret; }); }) ================================================ FILE: test/mocha/using.js ================================================ "use strict"; var assert = require("assert"); var testUtils = require("./helpers/util.js"); var Promise2 = require("../../js/debug/promise.js")(); var using = Promise.using; var error = new Error(""); var id = 0; function Resource() { this.isClosed = false; this.closesCalled = 0; this.commited = false; this.rollbacked = false; this.id = id++; } Resource.prototype.commit = function () { if (this.commited || this.rollbacked) { throw new Error("was already commited or rolled back") } this.commited = true; }; Resource.prototype.commitAsync = function () { return Promise.delay(1).bind(this).then(this.commit) } Resource.prototype.rollback = function () { if (this.commited || this.rollbacked) { throw new Error("was already commited or rolled back") } this.rollbacked = true; }; Resource.prototype.rollbackAsync = function () { return Promise.delay(1).bind(this).then(this.rollback) } Resource.prototype.closeAsync = function() { return Promise.delay(1).bind(this).then(this.close); }; Resource.prototype.close = function() { this.closesCalled++; if (this.isClosed) { return; } this.isClosed = true; }; Resource.prototype.closeError = function() { throw error; }; Resource.prototype.query = function(value) { return Promise.delay(1, value); }; function _connect() { return new Promise(function(resolve) { setTimeout(function(){ resolve(new Resource()) }, 1); }); } function _connect2() { return new Promise2(function(resolve) { setTimeout(function(){ resolve(new Resource()) }, 1); }); } function connectCloseAsync(arr, value) { return _connect().disposer(function(resource){ return resource.closeAsync().then(function() { arr.push(value); }); }); } function promiseForConnectCloseAsync(arr, value) { return Promise.delay(1).then(function() { return connectCloseAsync(arr, value); }); } function connect() { return _connect().disposer(Resource.prototype.close); } function connect2() { return _connect2().disposer(Resource.prototype.close); } function connectError() { return new Promise(function(resolve, reject) { setTimeout(function(){ reject(error); }, 1); }); } function transactionDisposer(tx, outcome) { outcome.isFulfilled() ? tx.commit() : tx.rollback(); } function transactionDisposerAsync(tx, outcome) { return outcome.isFulfilled() ? tx.commitAsync() : tx.rollbackAsync(); } function transaction() { return _connect().disposer(transactionDisposer); } function transactionWithImmediatePromiseAfterConnect() { return _connect().then(function (connection) { return Promise.resolve(connection).then(function(c) { return c; }) }).disposer(transactionDisposer); } function transactionWithEventualPromiseAfterConnect() { return _connect().then(function (connection) { return Promise.delay(1).thenReturn(connection); }).disposer(transactionDisposer); } function transactionAsync() { return _connect().disposer(transactionDisposerAsync); } describe("Promise.using", function() { specify("simple happy case", function() { var res; return using(connect(), function(connection){ res = connection; }).then(function() { assert(res.isClosed); assert.equal(res.closesCalled, 1); }); }); specify("simple async happy case", function() { var res; var async = false; return using(connect(), function(connection) { res = connection; return Promise.delay(1).then(function() { async = true; }); }).then(function() { assert(async); assert(res.isClosed); assert.equal(res.closesCalled, 1); }); }); specify("simple unhappy case", function() { var a = connect(); var promise = a._promise; var b = connectError(); return using(b, a, function(a, b) { assert(false); }).then(assert.fail, function() { assert(promise.value().isClosed); assert.equal(promise.value().closesCalled, 1); }) }); specify("calls async disposers sequentially", function() { var a = []; var _res3 = connectCloseAsync(a, 3); var _res2 = connectCloseAsync(a, 2); var _res1 = connectCloseAsync(a, 1); return using(_res1, _res2, _res3, function(res1, res2, res3) { _res1 = res1; _res2 = res2; _res3 = res3; }).then(function() { assert.deepEqual(a, [1,2,3]); assert(_res1.isClosed); assert(_res2.isClosed); assert(_res3.isClosed); assert(_res1.closesCalled == 1); assert(_res2.closesCalled == 1); assert(_res3.closesCalled == 1); }); }); specify("calls async disposers sequentially when assert.failing", function() { var a = []; var _res3 = connectCloseAsync(a, 3); var _res2 = connectCloseAsync(a, 2); var _res1 = connectCloseAsync(a, 1); var p1 = _res1.promise(); var p2 = _res2.promise(); var p3 = _res3.promise(); var e = new Error(); var promise = Promise.delay(1).thenThrow(e); return using(_res1, _res2, _res3, promise, function() { }).then(assert.fail, function(err) { assert.deepEqual(a, [1,2,3]); assert(p1.value().isClosed); assert(p2.value().isClosed); assert(p3.value().isClosed); assert(p1.value().closesCalled == 1); assert(p2.value().closesCalled == 1); assert(p3.value().closesCalled == 1); assert(e === err) }); }); specify("calls promised async disposers sequentially", function() { var a = []; var _res3 = promiseForConnectCloseAsync(a, 3); var _res2 = promiseForConnectCloseAsync(a, 2); var _res1 = promiseForConnectCloseAsync(a, 1); return using(_res1, _res2, _res3, function(res1, res2, res3) { assert(res1 instanceof Resource) assert(res2 instanceof Resource) assert(res3 instanceof Resource) _res1 = res1; _res2 = res2; _res3 = res3; }).then(function() { assert.deepEqual(a, [1,2,3]); assert(_res1.isClosed); assert(_res2.isClosed); assert(_res3.isClosed); assert(_res1.closesCalled == 1); assert(_res2.closesCalled == 1); assert(_res3.closesCalled == 1); }); }); specify("calls promised async disposers sequentially when assert.failing", function() { var a = []; var _res3 = promiseForConnectCloseAsync(a, 3); var _res2 = promiseForConnectCloseAsync(a, 2); var _res1 = promiseForConnectCloseAsync(a, 1); var e = new Error(); var promise = Promise.delay(1).thenThrow(e); return using(_res1, _res2, _res3, promise, function() { }).then(assert.fail, function(err) { assert.deepEqual(a, [1,2,3]); assert(_res1.value().promise().value().isClosed); assert(_res2.value().promise().value().isClosed); assert(_res3.value().promise().value().isClosed); assert(_res1.value().promise().value().closesCalled == 1); assert(_res2.value().promise().value().closesCalled == 1); assert(_res3.value().promise().value().closesCalled == 1); assert(e === err) }); }); specify("mixed promise, promise-for-disposer and disposer", function() { var a = []; var _res3 = promiseForConnectCloseAsync(a, 3); var _res2 = connectCloseAsync(a, 2); var _res1 = Promise.delay(1, 10); return using(_res1, _res2, _res3, function(res1, res2, res3) { assert(res1 === 10); assert(res2 instanceof Resource); assert(res3 instanceof Resource); _res1 = res1; _res2 = res2; _res3 = res3; }).then(function() { assert.deepEqual(a, [2,3]); assert(_res2.isClosed); assert(_res3.isClosed); assert(_res2.closesCalled == 1); assert(_res3.closesCalled == 1); }); }); specify("successful transaction", function() { var _tx; return using(transaction(), function(tx) { _tx = tx; return tx.query(1).then(function() { return tx.query(2); }) }).then(function(){ assert(_tx.commited); assert(!_tx.rollbacked); }); }); specify("successful async transaction", function() { var _tx; return using(transactionAsync(), function(tx) { _tx = tx; return tx.query(1).then(function() { return tx.query(3); }) }).then(function(){ assert(_tx.commited); assert(!_tx.rollbacked); }) }) specify("successful transaction with an immediate promise before disposer creation", function() { var _tx; return using(transactionWithImmediatePromiseAfterConnect(), function(tx) { _tx = tx; return tx.query(1); }).then(function(){ assert(_tx.commited); assert(!_tx.rollbacked); }); }); specify("successful transaction with an eventual promise before disposer creation", function() { var _tx; return using(transactionWithEventualPromiseAfterConnect(), function(tx) { _tx = tx; return tx.query(1); }).then(function(){ assert(_tx.commited); assert(!_tx.rollbacked); }); }); specify("assert.fail transaction", function() { var _tx; return using(transaction(), function(tx) { _tx = tx; return tx.query(1).then(function() { throw new Error(); }) }).then(assert.fail, function(){ assert(!_tx.commited); assert(_tx.rollbacked); }); }); specify("assert.fail async transaction", function() { var _tx; return using(transactionAsync(), function(tx) { _tx = tx; return tx.query(1).then(function() { throw new Error(); }) }).then(assert.fail, function(){ assert(!_tx.commited); assert(_tx.rollbacked); }); }); specify("with using coming from another Promise instance", function() { var res; return Promise2.using(connect(), function(connection){ res = connection; }).then(function() { assert(res.isClosed); assert.equal(res.closesCalled, 1); }); }); specify("with using coming from another Promise instance other way around", function() { var res; return Promise.using(connect2(), function(connection){ res = connection; }).then(function() { assert(res.isClosed); assert.equal(res.closesCalled, 1); }); }); if (testUtils.isNodeJS) { specify("disposer throwing should throw in node process", function() { var err = new Error(); var disposer = Promise.resolve().disposer(function() { throw err; }); using(disposer, function(){}); return testUtils.awaitGlobalException(function(e) { assert.strictEqual(e, err); }); }); } specify("Return rejected promise with less than 2 arguments", function() { return Promise.using(1).caught(Promise.TypeError, function(e) {}); }); specify("Throw if disposer is not passed a function", function() { try { Promise.resolve().disposer({}); } catch (e) { return Promise.resolve(); } assert.fail(); }); specify("Mixed rejected disposers are not called", function() { var err = new Error("rejected should not be called"); var a = Promise.delay(1).thenThrow(err).disposer(function() { done(err); }); var b = Promise.delay(1).thenReturn(connect()); return Promise.using(a, b, function() { done(new Error("should not be here")); }).then(assert.fail, function(e) { assert.strictEqual(b.value()._promise.value().isClosed, true); assert.strictEqual(b.value()._promise.value().closesCalled, 1); }); }); specify("Return rejected promise when last argument is not function", function() { return Promise.using({}, {}, {}, {}).caught(Promise.TypeError, function(e) {}); }); specify("Supports an array of 2 items", function() { return Promise.using([Promise.resolve(1), Promise.resolve(2)], function(results) { assert.deepEqual(results, [1,2]); }); }); specify("Supports an empty array", function() { return Promise.using([], function(results) { assert.deepEqual(results, []); }); }) specify("null disposer is called", function() { var calls = 0; var getNull = function() { return Promise.resolve(null).disposer(function() { calls++; }); }; return Promise.using(getNull(), function() { calls++; }).then(function() { assert.equal(2, calls); }) }) }) ================================================ FILE: tests ================================================ #!/usr/bin/env bash node tools/test.js "$@" ================================================ FILE: tools/README.md ================================================ ## Tools Tools that can be run from command line: ### test.js For running tests. See [Testing](../#testing). ### build.js For building the library. See [Custom builds](../#custom-builds). ### jshintrc_generator.js Generates a .jshintrc file in the project root. Example: node tools/jshintrc_generator ================================================ FILE: tools/ast_passes.js ================================================ //All kinds of conversion passes over the source code var jsp = require("acorn"); var walk = require("acorn-walk"); var rnonIdentMember = /[.\-_$a-zA-Z0-9]/g; var global = new Function("return this")(); function equals( a, b ) { if( a.type === b.type ) { if( a.type === "MemberExpression" ) { return equals( a.object, b.object ) && equals( a.property, b.property ); } else if( a.type === "Identifier" ) { return a.name === b.name; } else if( a.type === "ThisExpression" ) { return true; } else { console.log("equals", a, b); unhandled(); } } return false; } function getReceiver( expr ) { if( expr.type === "MemberExpression" ) { return expr.object; } return null; } function nodeToString( expr ) { if( expr == null || typeof expr !== "object" ) { if( expr === void 0 ) { return "void 0"; } else if( typeof expr === "string" ) { return '"' + safeToEmbedString(expr) + '"'; } return ("" + expr); } if( expr.type === "Identifier" ) { return expr.name; } else if( expr.type === "MemberExpression" ) { if( expr.computed ) return nodeToString( expr.object ) + "[" + nodeToString( expr.property ) + "]"; else return nodeToString( expr.object ) + "." + nodeToString( expr.property ); } else if( expr.type === "UnaryExpression" ) { if( expr.operator === "~" || expr.operator === "-" || expr.operator === "+" ) { return expr.operator + nodeToString( expr.argument ); } return "(" + expr.operator + " " + nodeToString( expr.argument ) + ")"; } else if( expr.type === "Literal" ) { return expr.raw; } else if( expr.type === "BinaryExpression" || expr.type === "LogicalExpression" ) { return "("+nodeToString(expr.left) + " " + expr.operator + " " + nodeToString(expr.right) + ")"; } else if( expr.type === "ThisExpression" ) { return "this"; } else if( expr.type === "ObjectExpression") { var props = expr.properties; var ret = []; for( var i = 0, len = props.length; i < len; ++i ) { var prop = props[i]; ret.push( nodeToString(prop.key) + ": " + nodeToString(prop.value)); } return "({"+ret.join(",\n")+"})"; } else if( expr.type === "NewExpression" ) { return "new " + nodeToString(expr.callee) + "(" + nodeToString(expr.arguments) +")"; } //assuming it is arguments else if( Array.isArray( expr ) ) { var tmp = []; for( var i = 0, len = expr.length; i < len; ++i ) { tmp.push( nodeToString(expr[i]) ); } return tmp.join(", "); } else if( expr.type === "FunctionExpression" ) { var params = []; for( var i = 0, len = expr.params.length; i < len; ++i ) { params.push( nodeToString(expr.params[i]) ); } } else if( expr.type === "BlockStatement" ) { var tmp = []; for( var i = 0, len = expr.body.length; i < len; ++i ) { tmp.push( nodeToString(expr.body[i]) ); } return tmp.join(";\n"); } else if( expr.type === "CallExpression" ) { var args = []; for( var i = 0, len = expr.arguments.length; i < len; ++i ) { args.push( nodeToString(expr.arguments[i]) ); } return nodeToString( expr.callee ) + "("+args.join(",")+")"; } else { console.log( "nodeToString", expr ); unhandled() } } function DynamicCall( receiver, fnDereference, arg, start, end ) { this.receiver = receiver; this.fnDereference = fnDereference; this.arg = arg; this.start = start; this.end = end; } DynamicCall.prototype.toString = function() { return nodeToString(this.fnDereference) + ".call(" + nodeToString(this.receiver) + ", " + nodeToString(this.arg) + ")"; }; function DirectCall( receiver, fnName, arg, start, end ) { this.receiver = receiver; this.fnName = fnName; this.arg = arg; this.start = start; this.end = end; } DirectCall.prototype.toString = function() { return nodeToString(this.receiver) + "." + nodeToString(this.fnName) + "(" + nodeToString(this.arg) + ")" }; function ConstantReplacement( value, start, end ) { this.value = value; this.start = start; this.end = end; } ConstantReplacement.prototype.toString = function() { return nodeToString(this.value); }; function Empty(start, end) { this.start = start; this.end = end; } Empty.prototype.toString = function() { return ""; }; function Assertion( expr, exprStr, start, end ) { this.expr = expr; this.exprStr = exprStr; this.start = start; this.end = end; } Assertion.prototype.toString = function() { return 'ASSERT('+nodeToString(this.expr)+',\n '+this.exprStr+')'; }; function BitFieldRead(mask, start, end, fieldExpr) { if (mask === 0) throw new Error("mask cannot be zero"); this.mask = mask; this.start = start; this.end = end; this.fieldExpr = fieldExpr; } BitFieldRead.prototype.getShiftCount = function() { var b = 1; var shiftCount = 0; while ((this.mask & b) === 0) { b <<= 1; shiftCount++; } return shiftCount; }; BitFieldRead.prototype.toString = function() { var fieldExpr = this.fieldExpr ? nodeToString(this.fieldExpr) : "bitField"; var mask = this.mask; var shiftCount = this.getShiftCount(); return shiftCount === 0 ? "(" + fieldExpr + " & " + mask + ")" : "((" + fieldExpr + " & " + mask + ") >>> " + shiftCount + ")"; }; function BitFieldCheck(value, inverted, start, end, fieldExpr) { this.value = value; this.inverted = inverted; this.start = start; this.end = end; this.fieldExpr = fieldExpr; } BitFieldCheck.prototype.toString = function() { var fieldExpr = this.fieldExpr ? nodeToString(this.fieldExpr) : "bitField"; var equality = this.inverted ? "===" : "!=="; return "((" + fieldExpr + " & " + this.value + ") " + equality + " 0)"; }; function InlineSlice(varExpr, collectionExpression, startExpression, endExpression, start, end, isBrowser, pad) { this.varExpr = varExpr; this.collectionExpression = collectionExpression; this.startExpression = startExpression; this.endExpression = endExpression; this.start = start; this.end = end; this.isBrowser = isBrowser; this.pad = typeof pad === "number" ? pad : 0; } InlineSlice.prototype.hasSimpleStartExpression = function InlineSlice$hasSimpleStartExpression() { return this.startExpression.type === "Identifier" || this.startExpression.type === "Literal"; }; InlineSlice.prototype.hasSimpleEndExpression = function InlineSlice$hasSimpleEndExpression() { return this.endExpression.type === "Identifier" || this.endExpression.type === "Literal"; }; InlineSlice.prototype.hasSimpleCollection = function InlineSlice$hasSimpleCollection() { return this.collectionExpression.type === "Identifier"; }; InlineSlice.prototype.toString = function InlineSlice$toString() { var pad = this.pad; var init = this.hasSimpleCollection() ? "" : "var $_collection = " + nodeToString(this.collectionExpression) + ";"; var collectionExpression = this.hasSimpleCollection() ? nodeToString(this.collectionExpression) : "$_collection"; init += "var $_len = " + collectionExpression + ".length"; if (pad !== 0) { init += " + " + Math.abs(pad) + ";"; } else { init += ";"; } var varExpr = nodeToString(this.varExpr); //No offset arguments at all if( this.startExpression === firstElement ) { if (this.isBrowser) { if (pad > 0) { return "var " + varExpr + " = (new Array("+pad+")).concat([].slice.call("+collectionExpression+"));"; } else if (pad < 0) { return "var " + varExpr + " = ([].slice.call("+collectionExpression+")).concat(new Array("+Math.abs(pad)+"));"; } else { return "var " + varExpr + " = [].slice.call("+collectionExpression+");"; } } else { var startVal = pad > 0 ? String(pad) : "0"; var collectionExpressionVal = pad > 0 ? " - " + pad : ""; var lenVal = pad < 0 ? "- " + Math.abs(pad) : ""; return init + "var " + varExpr + " = new Array($_len); " + "for(var $_i = " + startVal + "; $_i < $_len " + lenVal + "; ++$_i) {" + varExpr + "[$_i] = " + collectionExpression + "[$_i " + collectionExpressionVal + "];" + "}"; } } else { if( !this.hasSimpleStartExpression() ) { init += "var $_start = " + nodeToString(this.startExpression) + ";"; } var startExpression = this.hasSimpleStartExpression() ? nodeToString(this.startExpression) : "$_start"; //Start offset argument given if( this.endExpression === lastElement ) { if (this.isBrowser) { return "var " + varExpr + " = [].slice.call("+collectionExpression+", "+startExpression+");"; } else { return init + "var " + varExpr + " = new Array(Math.max($_len - " + startExpression + ", 0)); " + "for(var $_i = " + startExpression + "; $_i < $_len; ++$_i) {" + varExpr + "[$_i - "+startExpression+"] = " + collectionExpression + "[$_i];" + "}"; } } //Start and end offset argument given else { if( !this.hasSimpleEndExpression() ) { init += "var $_end = " + nodeToString(this.endExpression) + ";"; } var endExpression = this.hasSimpleEndExpression() ? nodeToString(this.endExpression) : "$_end"; if (this.isBrowser) { return "var " + varExpr + " = [].slice.call("+collectionExpression+", "+startExpression+", "+endExpression+");"; } else { return init + "var " + varExpr + " = new Array(Math.max(" + endExpression + " - " + startExpression + ", 0)); " + "for(var $_i = " + startExpression + "; $_i < " + endExpression + "; ++$_i) {" + varExpr + "[$_i - "+startExpression+"] = " + collectionExpression + "[$_i];" + "}"; } } } }; var opts = { ecmaVersion: 5, strictSemicolons: false, allowTrailingCommas: true, forbidReserved: false, locations: false, onComment: null, ranges: false, program: null, sourceFile: null }; var rlineterm = /[\r\n\u2028\u2029]/; var rhorizontalws = /[ \t]/; var convertSrc = function( src, results ) { if( results.length ) { results.sort(function(a, b){ var ret = a.start - b.start; if( ret === 0 ) { ret = a.end - b.end; } return ret; }); for( var i = 1; i < results.length; ++i ) { var item = results[i]; if( item.start === results[i-1].start && item.end === results[i-1].end ) { results.splice(i++, 1); } } var ret = ""; var start = 0; for( var i = 0, len = results.length; i < len; ++i ) { var item = results[i]; ret += src.substring( start, item.start ); ret += item.toString(); start = item.end; } ret += src.substring( start ); return ret; } return src; }; var rescape = /[\r\n\u2028\u2029"]/g; var replacer = function( ch ) { return "\\u" + (("0000") + (ch.charCodeAt(0).toString(16))).slice(-4); }; function safeToEmbedString( str ) { return str.replace( rescape, replacer ); } function parse( src, opts, fileName) { if( !fileName ) { fileName = opts; opts = void 0; } try { return jsp.parse(src, opts); } catch(e) { e.message = e.message + " " + fileName; e.scriptSrc = src; throw e; } } var inlinedFunctions = Object.create(null); var lastElement = jsp.parse("___input.length").body[0].expression; var firstElement = jsp.parse("0").body[0].expression; inlinedFunctions.INLINE_SLICE = function( node, isBrowser ) { var statement = node; node = node.expression; var args = node.arguments; if( !(2 <= args.length && args.length <= 4 ) ) { throw new Error("INLINE_SLICE must have exactly 2, 3 or 4 arguments"); } var varExpression = args[0]; var collectionExpression = args[1]; var startExpression = args.length < 3 ? firstElement : args[2]; var endExpression = args.length < 4 ? lastElement : args[3]; return new InlineSlice(varExpression, collectionExpression, startExpression, endExpression, statement.start, statement.end, isBrowser); }; inlinedFunctions.INLINE_SLICE_LEFT_PADDED = function( node, isBrowser ) { var statement = node; node = node.expression; var args = node.arguments; if(args.length !== 3) { throw new Error("INLINE_SLICE_LEFT_PADDED must have exactly 3 arguments"); } var padCount = Number(nodeToString(args[0])); var varExpression = args[1]; var collectionExpression = args[2]; var startExpression = firstElement; var endExpression = lastElement; return new InlineSlice(varExpression, collectionExpression, startExpression, endExpression, statement.start, statement.end, isBrowser, padCount); }; inlinedFunctions.BIT_FIELD_READ = function(node) { var statement = node; var args = node.expression.arguments; if (args.length !== 1 && args.length !== 2) { throw new Error("BIT_FIELD must have 1 or 2 arguments"); } var arg = args[0]; if (arg.type !== "Identifier") { throw new Error("BIT_FIELD argument must be an identifier"); } var name = arg.name; var constant = constants[name]; if (constant === undefined) { throw new Error(name + " is not a constant"); } var value = constant.value; return new BitFieldRead(value, statement.start, statement.end, args[1]); }; inlinedFunctions.BIT_FIELD_CHECK = function(node) { var statement = node; var args = node.expression.arguments; if (args.length !== 1 && args.length !== 2) { throw new Error("BIT_FIELD must have 1 or 2 arguments"); } var arg = args[0]; if (arg.type !== "Identifier") { throw new Error("BIT_FIELD argument must be an identifier"); } var name = arg.name; var constant = constants[name]; if (constant === undefined) { throw new Error(name + " is not a constant"); } var value = constant.value; var inverted = false; if (name.slice(-4) === "_NEG") { inverted = true; } return new BitFieldCheck(value, inverted, statement.start, statement.end, args[1]); }; inlinedFunctions.USE = function(node) { return new Empty(node.start, node.end); }; var constants = {}; var ignore = []; Error.stackTraceLimit = 10000; var astPasses = module.exports = { inlineExpansion: function( src, fileName, isBrowser ) { var ast = parse(src, fileName); var results = []; var expr = []; function doInline(node) { if( node.expression.type !== 'CallExpression' ) { return; } var name = node.expression.callee.name; if(typeof inlinedFunctions[ name ] === "function" && expr.indexOf(node.expression) === -1) { expr.push(node.expression); try { results.push( inlinedFunctions[ name ]( node, isBrowser ) ); } catch(e) { e.fileName = fileName; throw e; } } } walk.simple(ast, { ExpressionStatement: doInline, CallExpression: function(node) { node.expression = node; doInline(node); } }); var ret = convertSrc( src, results ); return ret; }, //Parse constants in from constants.js readConstants: function( src, fileName ) { var ast = parse(src, fileName); walk.simple(ast, { ExpressionStatement: function( node ) { if( node.expression.type !== 'CallExpression' ) { return; } var start = node.start; var end = node.end; node = node.expression; var callee = node.callee; if( callee.name === "CONSTANT" && callee.type === "Identifier" ) { if( node.arguments.length !== 2 ) { throw new Error( "Exactly 2 arguments must be passed to CONSTANT\n" + src.substring(start, end) ); } if( node.arguments[0].type !== "Identifier" ) { throw new Error( "Can only define identifier as a constant\n" + src.substring(start, end) ); } var args = node.arguments; var name = args[0]; var nameStr = name.name; var expr = args[1]; var e = eval; constants[nameStr] = { identifier: name, value: e(nodeToString(expr)) }; walk.simple( expr, { Identifier: function( node ) { ignore.push(node); } }); global[nameStr] = constants[nameStr].value; } } }); }, //Expand constants in normal source files expandConstants: function( src, fileName ) { var results = []; var identifiers = []; var ast = parse(src, fileName); walk.simple(ast, { Identifier: function( node ) { identifiers.push( node ); } }); for( var i = 0, len = identifiers.length; i < len; ++i ) { var id = identifiers[i]; if( ignore.indexOf(id) > -1 ) { continue; } var constant = constants[id.name]; if( constant === void 0 ) { continue; } if( constant.identifier === id ) { continue; } results.push( new ConstantReplacement( constant.value, id.start, id.end ) ); } return convertSrc( src, results ); }, removeComments: function( src, fileName ) { var results = []; var rnoremove = /^[*\s\/]*(?:@preserve|jshint|global)/; opts.onComment = function( block, text, start, end ) { if( rnoremove.test(text) ) { return; } var e = end + 1; var s = start - 1; while(rhorizontalws.test(src.charAt(s--))); while(rlineterm.test(src.charAt(e++))); results.push( new Empty( s + 2, e - 1 ) ); }; var ast = parse(src, opts, fileName); return convertSrc( src, results ); }, expandAsserts: function( src, fileName ) { var ast = parse( src, fileName ); var results = []; walk.simple(ast, { CallExpression: function( node ) { var start = node.start; var end = node.end; var callee = node.callee; if( callee.type === "Identifier" && callee.name === "ASSERT" ) { if( node.arguments.length !== 1 ) { results.push({ start: start, end: end, toString: function() { return src.substring(start, end); } }); return; } var expr = node.arguments[0]; var str = src.substring(expr.start, expr.end); str = '"' + safeToEmbedString(str) + '"' var assertion = new Assertion( expr, str, start, end ); results.push( assertion ); } } }); return convertSrc( src, results ); }, removeAsserts: function( src, fileName ) { var ast = parse( src, fileName ); var results = []; walk.simple(ast, { ExpressionStatement: function( node ) { if( node.expression.type !== 'CallExpression' ) { return; } var start = node.start; var end = node.end; node = node.expression; var callee = node.callee; if( callee.type === "Identifier" && callee.name === "ASSERT" ) { var e = end + 1; var s = start - 1; while(rhorizontalws.test(src.charAt(s--))); while(rlineterm.test(src.charAt(e++))); results.push( new Empty( s + 2, e - 1) ); } }, VariableDeclaration: function(node) { var start = node.start; var end = node.end; if (node.kind === 'var' && node.declarations.length === 1) { var decl = node.declarations[0]; if (decl.id.type === "Identifier" && decl.id.name === "ASSERT") { var e = end + 1; var s = start - 1; while(rhorizontalws.test(src.charAt(s--))); while(rlineterm.test(src.charAt(e++))); results.push( new Empty( s + 2, e - 1) ); } } } }); return convertSrc( src, results ); }, asyncConvert: function( src, objName, fnProp, fileName ) { var ast = parse( src, fileName ); var results = []; walk.simple(ast, { CallExpression: function( node ) { var start = node.start; var end = node.end; if( node.callee.type === "MemberExpression" && node.callee.object.name === objName && node.callee.property.name === fnProp && node.arguments.length === 3 ) { var args = node.arguments; var fnDereference = args[0]; var dynamicReceiver = args[1]; var arg = args[2]; var receiver = getReceiver(fnDereference); if( receiver == null || !equals(receiver, dynamicReceiver) ) { //Have to use fnDereference.call(dynamicReceiver, arg); results.push( new DynamicCall( dynamicReceiver, fnDereference, arg, start, end ) ); } else { var fnName = fnDereference.property; results.push( new DirectCall( receiver, fnName, arg, start, end ) ); //Can use receiver.fnName( arg ); } } } }); return convertSrc( src, results ); } }; ================================================ FILE: tools/browser_test_generator.js ================================================ var Promise = require("bluebird"); var path = require("path"); var readFile = Promise.promisify(require("fs").readFile); var writeFile = Promise.promisify(require("fs").writeFile); var stringToStream = require("./utils.js").stringToStream; var baseDir = path.join(__dirname, "..", "test", "browser"); // Sinon pulls the entire NPM as dependencies (including crypto code) for // some reason so // the browserify bundle ends up taking megabyte and having high risk of // IE-incompatible code function dependsOnSinon(test) { return /3\.2\.5\.|done|nodeify|2\.2\.6/.test(test.name); }; module.exports = function(tests, options) { var testRequires = tests.filter(function(test) { return !dependsOnSinon(test) && test.name.indexOf("generator") === -1; }).map(function(test) { var code = "require('../mocha/" + test.name + "');"; if (test.name.indexOf("2.3.3") >= 0) { code = "if (haveGetters) " + code; } return code; }).join("\n"); var promiseExport = options.cover ? readFile(path.join(baseDir, "promise_instrumented.js"), "utf8") : readFile(path.join(baseDir, "promise_debug.js"), "utf8"); var main = readFile(path.join(baseDir, "main.js"), "utf8") return Promise.join(promiseExport, main, function(promiseExport, main) { var browserify = require("browserify"); var contents = promiseExport + "\n" + main + "\n" + testRequires; var complete = browserify({ basedir: baseDir, entries: stringToStream(contents) }); var worker = browserify({ basedir: baseDir, entries: stringToStream(promiseExport), }); return Promise.join( Promise.promisify(complete.bundle, complete)().then(function(src) { return writeFile(path.join(baseDir, "bundle.js"), src); }), Promise.promisify(worker.bundle, worker)().then(function (src) { return writeFile(path.join(baseDir, "worker_bundle.js"), src); })); }).then(function() { if (options.executeBrowserTests) { return require("./browser_test_runner.js")(options); } }).catch(function(e) { console.error(e.stack || e.message); process.exit(2); }); }; ================================================ FILE: tools/browser_test_runner.js ================================================ var path = require("path"); var build = require("./build.js"); var Promise = require("bluebird"); var cp = Promise.promisifyAll(require("child_process")); var fs = Promise.promisifyAll(require("fs")); var baseDir = path.join(__dirname, "..", "test", "browser"); var browsers = [ ["Windows XP", "internet explorer", "7"], ["Windows XP", "internet explorer", "8"], ["Windows 7", "internet explorer", "9"], ["Windows 7", "internet explorer", "10"], ["Windows 8.1", "internet explorer", "11"], ["Windows 7", "firefox", "3.5"], ["Windows 7", "firefox", "4"], ["Windows 7", "firefox", "25"], ["Windows 7", "firefox", "33"], ["Windows 7", "chrome", "beta"], ["Windows 7", "safari", "5"], ["OS X 10.9", "iphone", "8.1"], ["OS X 10.8", "safari", "6"], ["OS X 10.9", "safari", "7"] ]; var saucelabsOptions = { urls: ["http://127.0.0.1:9999/index.html"], tunnelTimeout: 30, build: process.env.TRAVIS_JOB_ID, maxPollRetries: 3, throttled: 3, browsers: browsers, testname: "mocha tests", tags: ["master"] }; module.exports = function(options) { var Promise = require("bluebird"); var ret = Promise.resolve(); function createServer() { var http = require("http"); var serve = require("serve-static")(baseDir, {'index': ['index.html']}); var bodyParser = require("body-parser").urlencoded({ limit: "100mb", extended: false }); var server = http.createServer(function(req, res) { serve(req, res, function() { if (options.cover && req.url.indexOf("coverdata") >= 0 && req.method.toLowerCase() === "post") { bodyParser(req, res, function() { try { var json = JSON.parse(req.body.json); } catch (e) { res.writeHead(404, {'Content-Type': 'text/plain'}); res.end('404\n'); return; } var browser = (req.body.browser + "").replace(/[^a-zA-Z0-9]/g, ""); var fileName = path.join(build.dirs.coverage, "coverage-" + browser + ".json"); fs.writeFileAsync(fileName, JSON.stringify(json), "utf8").then(function() { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Success\n'); }); }); } else { res.writeHead(404, {'Content-Type': 'text/plain'}); res.end('404\n'); } }); }); return Promise.promisify(server.listen, server)(options.port) } if (options.saucelabs) { var saucelabsRunner = require("./saucelabs_runner.js"); ret = createServer().then(function() { return saucelabsRunner(saucelabsOptions); }).then(function() { process.exit(0); }); } else if (options.nw) { ret = cp.execAsync((options.nwPath || "nw") + " .", { maxBuffer: 2 * 1024 * 1024, cwd: path.join(process.cwd(), "test/browser") }); } else { var open = require("open"); ret = createServer().then(function() { var url = "http://localhost:" + options.port; console.log("Test can be run at " + url); if (options.openBrowser && !options.cover) { return Promise.promisify(open)(url); } }); } return ret; }; ================================================ FILE: tools/build.js ================================================ var Promise = require("bluebird"); var path = require("path"); var jobRunner = require("./job-runner/job-runner.js"); Promise.longStackTraces(); var utils = require("./utils.js"); var glob = Promise.promisify(require("glob")); var fs = Promise.promisifyAll(require("fs")); var mkdirp = Promise.promisify(require("mkdirp")); var rimraf = Promise.promisify(require("rimraf")); jobRunner.init(path.join(__dirname, ".."), function() { var fs = Promise.promisifyAll(require("fs")); var utils = require("./tools/utils.js"); var path = require("path"); var astPasses = require("./tools/ast_passes.js"); var Mocha = require("mocha"); astPasses.readConstants( fs.readFileSync("./src/constants.js", "utf8"), "constants.js" ); function applyOptionalRequires(code, depsRequireCode) { return code.replace( /};([^}]*)$/, depsRequireCode + "\n};$1"); } }); var optionalModuleRequireMap = { "race.js": true, "call_get.js": true, "generators.js": true, "map.js": true, "nodeify.js": true, "promisify.js": true, "props.js": true, "reduce.js": true, "settle.js": true, "some.js": true, "using.js": true, "timers.js": true, "filter.js": ["map.js"], "any.js": ["some.js"], "each.js": ["reduce.js"] }; var lastLineCode = " \n\ util.toFastProperties(Promise); \n\ util.toFastProperties(Promise.prototype); \n\ function fillTypes(value) { \n\ var p = new Promise(INTERNAL); \n\ p._fulfillmentHandler0 = value; \n\ p._rejectionHandler0 = value; \n\ p._promise0 = value; \n\ p._receiver0 = value; \n\ } \n\ // Complete slack tracking, opt out of field-type tracking and \n\ // stabilize map \n\ fillTypes({a: 1}); \n\ fillTypes({b: 2}); \n\ fillTypes({c: 3}); \n\ fillTypes(1); \n\ fillTypes(function(){}); \n\ fillTypes(undefined); \n\ fillTypes(false); \n\ fillTypes(new Promise(INTERNAL)); \n\ debug.setBounds(Async.firstLineError, util.lastLineError); \n\ return Promise; \n\ "; function getOptionalRequireCode(srcs) { return srcs.sort(function(a, b) { var aHasDeps = Array.isArray(optionalModuleRequireMap[a.sourceFileName]); var bHasDeps = Array.isArray(optionalModuleRequireMap[b.sourceFileName]); return aHasDeps - bHasDeps; }).reduce(function(ret, cur, i) { if(optionalModuleRequireMap[cur.sourceFileName]) { ret += "require('./"+cur.sourceFileName+"')("+ cur.deps.join(", ") +");\n"; } return ret; }, "") + lastLineCode; } function getBrowserBuildHeader(sources, npmPackage) { var header = "/**\n * bluebird build version " + npmPackage.version + "\n"; var enabledFeatures = ["core"]; var disabledFeatures = []; featureLoop: for (var key in optionalModuleRequireMap) { for (var i = 0, len = sources.length; i < len; ++i) { var source = sources[i]; if(source.sourceFileName === key) { enabledFeatures.push(key.replace( ".js", "")); continue featureLoop; } } disabledFeatures.push(key.replace(".js", "")); } header += (" * Features enabled: " + enabledFeatures.join(", ") + "\n"); if (disabledFeatures.length) { header += " * Features disabled: " + disabledFeatures.join(", ") + "\n"; } header += "*/\n"; return header; } function getSourcePaths(features) { return glob("./src/*.js").map(function(v) { return path.basename(v); }).then(function(results) { if (features) features = features.toLowerCase().split(/\s+/g); return results.filter(function(fileName) { if (features && optionalModuleRequireMap[fileName] !== undefined) { for (var i = 0; i < features.length; ++i) { if (fileName.indexOf(features[i]) >= 0) { return true; } } return false; } return fileName !== "constants.js"; }); }); } function ensureDirectory(dir, isUsed, clean) { return (clean ? rimraf(dir) : Promise.resolve()).then(function() { if (!isUsed) return dir; return mkdirp(dir).thenReturn(dir); }); } function buildRelease(sources, depsRequireCode, dir) { return dir.then(function(dir) { return Promise.map(sources, function(source) { return jobRunner.run(function() { var code = source.source; var sourceFileName = source.sourceFileName; code = astPasses.removeAsserts(code, sourceFileName); code = astPasses.inlineExpansion(code, sourceFileName); code = astPasses.expandConstants(code, sourceFileName); code = code.replace( /__DEBUG__/g, "false" ); code = code.replace( /__BROWSER__/g, "false" ); if (sourceFileName === "promise.js") { code = applyOptionalRequires(code, depsRequireCode); } return fs.writeFileAsync(path.join(root, sourceFileName), code); }, { context: { depsRequireCode: depsRequireCode, source: source, root: dir } }); }); }); } function buildDebug(sources, depsRequireCode, dir) { return dir.then(function(dir) { return Promise.map(sources, function(source) { return jobRunner.run(function() { var code = source.source; var sourceFileName = source.sourceFileName; code = astPasses.expandAsserts(code, sourceFileName); code = astPasses.inlineExpansion(code, sourceFileName); code = astPasses.expandConstants(code, sourceFileName); code = code.replace( /__DEBUG__/g, "true" ); code = code.replace( /__BROWSER__/g, "false" ); if (sourceFileName === "promise.js") { code = applyOptionalRequires(code, depsRequireCode); } return fs.writeFileAsync(path.join(root, sourceFileName), code); }, { context: { depsRequireCode: depsRequireCode, source: source, root: dir } }); }); }); } function buildBrowser(sources, dir, tmpDir, depsRequireCode, minify, npmPackage, license) { return Promise.join(dir, tmpDir, npmPackage, license, function(dir, tmpDir, npmPackage, license) { return Promise.map(sources, function(source) { return jobRunner.run(function() { var code = source.source; var sourceFileName = source.sourceFileName; code = astPasses.removeAsserts(code, sourceFileName); code = astPasses.inlineExpansion(code, sourceFileName, true); code = astPasses.expandConstants(code, sourceFileName); code = code.replace( /__BROWSER__/g, "true" ); if (sourceFileName === "promise.js") { code = applyOptionalRequires(code, depsRequireCode); } return fs.writeFileAsync(path.join(root, sourceFileName), code); }, { context: { depsRequireCode: depsRequireCode, source: source, root: tmpDir } }); }).then(function() { var header = getBrowserBuildHeader(sources, npmPackage); return jobRunner.run(function() { var UglifyJS = require("uglify-js"); var browserify = require("browserify"); var dest = path.join(root, "bluebird.js"); var minDest = path.join(root, "bluebird.min.js"); var b = browserify({ entries: entries, detectGlobals: false, standalone: "Promise" }).exclude('async_hooks').exclude("timers"); return Promise.promisify(b.bundle, b)().then(function(src) { var alias = "\ ;if (typeof window !== 'undefined' && window !== null) { \ window.P = window.Promise; \ } else if (typeof self !== 'undefined' && self !== null) { \ self.P = self.Promise; \ }"; src = src + alias; src = src.replace(/\brequire\b/g, "_dereq_"); var minWrite, write; if (minify) { var minSrc = src.replace( /__DEBUG__/g, "false" ); minSrc = UglifyJS.minify(minSrc, { comments: false, compress: true, fromString: true }).code; minSrc = license + header + minSrc; minWrite = fs.writeFileAsync(minDest, minSrc); } src = src.replace( /__DEBUG__/g, "true" ); src = license + header + src; write = fs.writeFileAsync(dest, src); return Promise.all([write, minWrite]); }) }, { context: { header: header, root: dir, entries: path.join(tmpDir, "bluebird.js"), license: license, minify: minify } }) }); }); } var root = process.cwd(); // Since rm -rf is called, better be sure... if (path.basename(root).toLowerCase().indexOf("bluebird") !== 0) { throw new Error("cwd must be set to bluebird project root. cwd is currently\n\n" + " " + process.cwd() + "\n"); } var dirs = { release: path.join(root, "js", "release"), debug: path.join(root, "js", "debug"), browser: path.join(root, "js", "browser"), browserTmp: path.join(root, "js", "tmp"), instrumented: path.join(root, "js", "instrumented"), coverage: path.join(root, "coverage") }; function build(options) { options = Object(options); var clean = (typeof options.clean !== "boolean" ? true : options.clean); var npmPackage = fs.readFileAsync("./package.json").then(JSON.parse); var version = npmPackage.get("version"); var sourceFileNames = getSourcePaths(options.features); var license = utils.getLicense(); var releaseDir = ensureDirectory(dirs.release, options.release, clean); var debugDir = ensureDirectory(dirs.debug, options.debug, clean); var browserDir = ensureDirectory(dirs.browser, options.browser, clean); var browserTmpDir = ensureDirectory(dirs.browserTmp, options.browser, clean); return Promise.join(license, version, function(license, version) { return sourceFileNames.map(function(sourceFileName) { return jobRunner.run(function() { var sourcePath = path.join("./src", sourceFileName); var source = fs.readFileAsync(sourcePath, "utf8"); return source.then(function(source) { utils.checkAscii(sourceFileName, source); var deps = null; if (optionalModuleRequireMap[sourceFileName] !== undefined) { deps = utils.parseDeps(source); } source = astPasses.removeComments(source, sourceFileName); source = source.replace(/__VERSION__/g, version); return { sourceFileName: sourceFileName, source: source, deps: deps }; }); }, { context: { sourceFileName: sourceFileName, optionalModuleRequireMap: optionalModuleRequireMap, license: license, version: version } }); }); }).then(function(results) { var depsRequireCode = getOptionalRequireCode(results); var release, debug, browser; if (options.release) release = buildRelease(results, depsRequireCode, releaseDir); if (options.debug) debug = buildDebug(results, depsRequireCode, debugDir); if (options.browser) browser = buildBrowser(results, browserDir, browserTmpDir, depsRequireCode, options.minify, npmPackage, license); return Promise.all([release, debug, browser]); }); } module.exports = build; module.exports.ensureDirectory = ensureDirectory; module.exports.dirs = dirs; if (require.main === module) { var argv = require("optimist").argv; var browser = (typeof argv.browser !== "boolean" ? false : argv.browser) || !!argv.features; var clean = (typeof argv.clean !== "boolean" ? true : argv.clean); module.exports({ minify: browser && (typeof argv.minify !== "boolean" ? true : argv.minify), browser: browser, debug: !!argv.debug, release: !!argv.release, features: argv.features || null, clean: clean }); } ================================================ FILE: tools/job-runner/job-runner.js ================================================ var Promise = require("bluebird"); var spawn = require("child_process").spawn; var assert = require("assert"); var argv = require("optimist").argv; var ARGS = [].concat( process.execArgv, __filename, process.argv.slice(2) ); function sanitizeCpCount(input) { return Math.min(Math.max(1, (+input | 0)), 16); } if (typeof argv["child-processes"] === "number") { var CHILD_PROCESSES = sanitizeCpCount(argv["child-processes"]); } else if (process.env.CHILD_PROCESSES) { var CHILD_PROCESSES = sanitizeCpCount(process.env.CHILD_PROCESSES); } else { var CHILD_PROCESSES = 4; } var debugging = false; function debug() { if (debugging) { var msg = [].slice.call(arguments).join(" "); console.log(msg); } } function ResultWithOutput(result, stdout, stderr) { this.result = result; this.stdout = stdout; this.stderr = stderr; } var jobRunner = (function() { var taskId = 0; var workerCount; var workers = []; var tasks = []; var killed = true; function leastTotalRunningTime(a, b) { return a.totalRunningTime() - b.totalRunningTime(); } function each(fn) { if (typeof fn === "string") { var args = [].slice.call(arguments, 1); return workers.forEach(function(worker) { worker[fn].apply(worker, args); }); } return workers.forEach(fn); } function retLogger(result) { if (result instanceof ResultWithOutput) { process.stdout.write(result.stdout); process.stderr.write(result.stderr); } return result; } function throwLogger(result) { if (result && (result.stderr || result.stdout)) { process.stdout.write(result.stdout); process.stderr.write(result.stderr); } throw result; } function reinit() { each("init"); } function checkShutDown(secondCheck) { if (tasks.length > 0) return; var anyWorkerHasTasks = workers.some(function(w) { return w.hasTasks(); }); if (anyWorkerHasTasks) return; if (secondCheck) return ret.exit(); setTimeout(function() { checkShutDown(true); }, 10); } function schedule(task, queued) { var worker = workers.filter(function(worker) { return task.isolated ? !worker.hasTasks() : !worker._runningIsolatedTask; }).sort(leastTotalRunningTime)[0]; if (!worker) { if (!queued) tasks.push(task); return false; } else { assert(task.isolated ? !worker.hasTasks() : true); debug("found free worker", worker._id, "for task", task.id); worker.performTask(task); return true; } } var ret = { init: function(requirePath, initTaskFn) { if (workers.length) return; if (typeof requirePath !== "string") throw new TypeError(); var count = CHILD_PROCESSES; workerCount = count; var id = 0; for (var i = 0; i < count; ++i) { workers.push(new Worker(id++, requirePath, initTaskFn)); } process.on("exit", ret.exit); }, exit: function() { if (killed) return; killed = true; each("kill"); }, run: function(task, opts) { if (!workerCount) throw new Error("task runner has not been initialized"); if (typeof task !== "function") throw new TypeError("fn not function"); if (killed) { killed = false; reinit(); } opts = opts || {}; var context = opts.context || {}; var log = opts.log === false ? false : true; var estimate = typeof opts.estimate === "number" ? opts.estimate : null; var progress = typeof opts.progress === "function" ? opts.progress : null; var isolated = !!opts.isolated; var resolve, reject; var promise = new Promise(function() { resolve = arguments[0]; reject = arguments[1]; }); task = { isolated: isolated, task: { code: task + "", context: context }, resolve: resolve, reject: reject, estimate: estimate, id: taskId++, log: log, progress: progress }; schedule(task, false); if (log) promise = promise.then(retLogger, throwLogger); return promise; }, setVerbose: function(v) { debugging = !!v; }, _workerIdleNotification: function() { var _t = tasks; if (_t.length === 0) { return checkShutDown(); } while(_t.length > 0) { var task = _t.shift(); if (!schedule(task, true)) { _t.unshift(task); return; } } } }; return ret; })(); function Worker(id, requirePath, initTaskFn) { this._initTaskFn = initTaskFn; this._requirePath = requirePath; this._id = id; this._runningTaskCount = 0; this._runningIsolatedTask = false; this._performingIsolatedTask = false; this._queuedIsolatedTask = null; this._bufferingStdio = false; this._runningTime = 0; this._c = null; this._stdout = ""; this._stderr = ""; this._tasks = {}; this._onStdOut = bind(this._onStdOut, this); this._onStdErr = bind(this._onStdErr, this); this._onError = bind(this._onError, this); this._onMessage = bind(this._onMessage, this); } Worker.prototype.totalRunningTime = function() { var ret = this._runningTime; var ids = Object.keys(this._tasks); var now = Date.now(); for (var i = 0; i < ids.length; ++i) { var task = this._tasks[ids[i]]; ret += task.estimate === null ? (now - task.started + 1): task.estimate; } return ret; }; Worker.prototype._onStdOut = function(data) { data = data.toString(); if (this._bufferingStdio) { this._stdout += data; } else { process.stdout.write(data); } }; Worker.prototype._onStdErr = function(data) { data = data.toString(); if (this._bufferingStdio) { this._stderr += data; } else { process.stderr.write(data); } }; Worker.prototype._onError = function(e) { process.stderr.write(e && e.stack && e.stack + "" || (e + "")); }; Worker.prototype._onMessage = function(payload) { var self = this; setImmediate(function() { self[payload.type].call(self, payload); }); }; Worker.prototype.removeListeners = function() { var c = this._c; c.stdout.removeListener("data", this._onStdOut); c.stderr.removeListener("data", this._onStdErr); c.removeListener("message", this._onMessage); c.removeListener("error", this._onError); }; Worker.prototype.debug = function(msg, task) { debug("worker", this._id, msg, (task.isolated ? "isolated" : ""), "task", task.id); }; Worker.prototype.hasTasks = function() { return this.runningTaskCount() > 0 || this._queuedIsolatedTask || this._runningIsolatedTask; }; Worker.prototype.runningTaskCount = function() { return this._runningTaskCount; }; Worker.prototype.performTask = function(task) { if (task !== this._queuedIsolatedTask) { assert(!this._runningIsolatedTask); if (task.isolated) { this._runningIsolatedTask = true; if (this.runningTaskCount() > 0) { this.debug("queued", task); this._queuedIsolatedTask = task; return; } else { assert(this._queuedIsolatedTask === null); this._performingIsolatedTask = true; } } } else { assert(this.runningTaskCount() === 0); assert(this._runningIsolatedTask); this._queuedIsolatedTask = null; this._performingIsolatedTask = true; } this._runningTaskCount++; assert(this._performingIsolatedTask ? this._runningTaskCount === 1 : true); this._tasks[task.id] = task; task.started = Date.now(); this.debug("starts to perform", task); this._c.send({ type: "newTask", id: task.id, task: task.task, isolated: task.isolated, log: task.log, progress: !!task.progress }); }; function getFunctionSource(fn) { return (fn + "") .replace(/^\s*function\s*\(\s*\)\s*{/, "") .replace(/}\s*$/, ""); } Worker.prototype.init = function() { assert(Array.isArray(ARGS)); assert(!this._c); var env = {}; Object.getOwnPropertyNames(process.env).forEach(function(key) { env[key] = process.env[key]; }); env.requirePath = this._requirePath; if (typeof this._initTaskFn === "function") { env.initialCode = getFunctionSource(this._initTaskFn); } var c = spawn(process.execPath, ARGS, { env: env, stdio: ["pipe", "pipe", "pipe", "ipc"], cwd: this._requirePath }); assert(typeof c.send === "function"); this._c = c; c.stdout.on("data", this._onStdOut); c.stderr.on("data", this._onStdErr); c.on("error", this._onError); c.on("message", this._onMessage); }; Worker.prototype.taskComplete = function(payload) { var task = this._tasks[payload.id]; this.debug("completed", task); delete this._tasks[payload.id]; this._runningTaskCount--; var resolve, result; if (payload.isError) { resolve = task.reject; var err = payload.error; if (err.__isErrorInstance__) { result = new Error(); Object.keys(err).forEach(function(key) { if (key === "__isErrorInstance__") return;; result[key] = err[key]; }); result.name = err.name; result.stack = err.stack; result.message = err.message; } else { result = err; } } else { resolve = task.resolve; result = payload.result; } if (this._runningIsolatedTask) { if (this._queuedIsolatedTask) { if (this.runningTaskCount() === 0) { this.performTask(this._queuedIsolatedTask); } } else { if (payload.error) { result.stdout = this._stdout; result.stderr = this._stderr; } else { result = new ResultWithOutput(result, this._stdout, this._stderr); } this._stderr = this._stdout = ""; this._performingIsolatedTask = false; this._runningIsolatedTask = false; this._bufferingStdio = false; this.kill(); this.init(); } } resolve(result); if (!this._runningIsolatedTask) { jobRunner._workerIdleNotification(); } }; Worker.prototype.kill = function() { if (this._c) { this.removeListeners(); this._c.kill("SIGKILL"); this._c = null; } }; Worker.prototype.progress = function(payload) { var id = payload.id; var task = this._tasks[id]; if (task && typeof task.progress === "function") { task.progress.call(undefined, payload.value); } }; Worker.prototype.outputFlushed = function() { this._bufferingStdio = true; this._c.send({type: "outputFlushedAck"}); }; function bind(fn, ctx) {return function() {return fn.apply(ctx, arguments); };} function getTaskFunction(context, code) { with (context) { return eval( "(" + code + ")"); } } if (require.main === module) { var __requirePath = process.env.requirePath; var __oldreq = require; var __path = require("path"); require = function(p) { if (p.charAt(0) === ".") { p = __path.join(__requirePath, p); } return __oldreq(p); }; if (process.env.initialCode) { eval(process.env.initialCode); } (function() { function waitForOutput() { return new Promise(function(resolve, reject) { var flushCount = 0; function onFlushed() { flushCount++; if (flushCount === 2) { resolve(); } } function checkStream(stream) { if (stream.bufferSize === 0) { onFlushed(); } else { stream.write("", "utf-8", onFlushed); } } checkStream(process.stdout); checkStream(process.stderr); }); } function waitForPreviousOutput(id) { return waitForOutput().then(function() { var ret = waitForFlushAck(id); process.send({type: "outputFlushed", id: id}); return ret; }); } var ackWaitResolve = null; function waitForFlushAck(id) { assert(ackWaitResolve === null); return new Promise(function(resolve) { ackWaitResolve = resolve; }); } var noop = function() {}; var noopWrite = function(_, a, b) { if (typeof a === "function") return a(); if (typeof b === "function") return b(); }; function toSerializableError(err) { if (err instanceof Error) { var ret = Object.create(null); Object.keys(err).forEach(function(key){ ret[key] = err[key]; }); ret.name = err.name; ret.stack = err.stack; ret.message = err.message; ret.__isErrorInstance__ = true; return ret; } else { return err; } } var actions = { newTask: function(payload) { var task = payload.task; var code = task.code; var context = task.context; var id = payload.id; var promise = payload.isolated ? waitForPreviousOutput(id) : Promise.resolve(); return promise .then(function() { if (payload.log === false && payload.isolated) { process.stdout.write = noopWrite; } var fn = getTaskFunction(context, code); if (typeof fn !== "function") throw new Error("fn must be function"); return fn(payload.progress ? function(value) { process.send({type: "progress", id: id, value: value}); } : noop); }) .finally(function() { if (payload.isolated) { return waitForOutput(); } }) .then(function(result) { process.send({ type: "taskComplete", id: payload.id, result: result }); }) .catch(function(error) { process.send({ type: "taskComplete", id: payload.id, error: toSerializableError(error), isError: true }); }); }, outputFlushedAck: function() { var resolve = ackWaitResolve; ackWaitResolve = null; resolve(); }, addGlobals: function(payload) { new Function(payload.code)(); } }; process.on("message", function(payload) { setImmediate(function() { actions[payload.type].call(actions, payload); }); }); })(); } else { module.exports = jobRunner; Object.defineProperty(module.exports, "CHILD_PROCESSES", { value: CHILD_PROCESSES, writable: false }); } ================================================ FILE: tools/jshint.js ================================================ var utils = require("./utils.js"); var path = require("path"); module.exports = function() { var wd = path.join(__dirname, ".."); return utils.run("node_modules/jshint/bin/jshint", [ "--verbose", "--reporter", "node_modules/jshint-stylish/stylish.js", "src/" ], wd); }; function log(value) { process.stdout.write(value.stdout); process.stderr.write(value.stderr); } if (require.main === module) { module.exports().then(log, log); } ================================================ FILE: tools/jshintrc_generator.js ================================================ var assert = require("assert"); assert.equal(require.main, module); // Since many globals are dynamic, this file is needed to generate jshintrc dynamically var Promise = require("bluebird"); var path = require("path"); Promise.longStackTraces(); var fs = Promise.promisifyAll(require("fs")); var constantsFile = path.join(__dirname, "..", "src", "constants.js"); var globals = fs.readFileAsync(constantsFile, "utf8").then(function(contents) { var rconstantname = /CONSTANT\(\s*([^,]+)/g; var m; var globals = { Symbol: false, Map: false, JSON: false, Error: true, args: true, chrome: true, INLINE_SLICE: false, INLINE_SLICE_LEFT_PADDED: false, BIT_FIELD_CHECK: false, BIT_FIELD_READ: false, USE: false, global: true, setImmediate: true, Promise: true, WebKitMutationObserver: true, TypeError: true, RangeError: true, __DEBUG__: false, __BROWSER__: false, process: true, self: true, "console": false, "require": false, "module": false, "define": false }; while((m = rconstantname.exec(contents))) { globals[m[1]] = false; } return globals; }); var jshintrcFile = path.join(__dirname, "..", ".jshintrc"); var jshintrc = fs.readFileAsync(jshintrcFile, "utf8").then(JSON.parse); Promise.join(jshintrc, globals, function(jshintrc, globals) { jshintrc.globals = globals; var json = JSON.stringify(jshintrc, null, " "); return fs.writeFileAsync(jshintrcFile, json); }); ================================================ FILE: tools/mocha_runner.js ================================================ module.exports = function mochaRun(progress) { var currentTime = 0; var timers = {}; var currentId = 0; function checkTimers() { var keys = Object.keys(timers); for (var i = 0; i < keys.length; ++i) { var key = keys[i]; var timer = timers[key]; if (!timer) continue; if (currentTime >= (timer.started + timer.time)) { if (timer.interval) { timer.started = currentTime; } else { delete timers[key]; } var fn = timer.fn; if (timer.domain) timer.domain.enter(); fn(); if (timer.domain) timer.domain.exit(); } } } function setInterval(fn, time) { var id = currentId++; time = (+time || 0) | 0; if (time < 0) time = 0; timers[id] = { fn: fn, time: time, started: currentTime, interval: true, domain: process.domain }; return id; } function setTimeout(fn, time) { var id = currentId++; time = (+time || 0) | 0; if (time < 11) time = 11; timers[id] = { fn: fn, time: time, started: currentTime, interval: false, domain: process.domain }; return id; } function clearTimeout(id) { delete timers[id]; } var clearInterval = clearTimeout; if (fakeTimers) { (function timerLoop() { currentTime += 10; try { checkTimers(); } finally { setImmediate(timerLoop); } })(); global.oldSetTimeout = global.setTimeout; global.oldClearTimeout = global.clearTimeout; global.setTimeout = setTimeout; global.clearTimeout = clearTimeout; global.setInterval = setInterval; global.clearInterval = clearInterval; } var failures = []; delete Error.__BluebirdErrorTypes__; global.adapter = cover ? require("./js/instrumented/bluebird.js") : require("./js/debug/bluebird.js"); global.Promise = adapter; Promise = adapter; adapter.defer = adapter.pending = function() { var ret = {}; ret.promise = new Promise(function(resolve, reject) { ret.resolve = ret.fulfill = resolve; ret.reject = reject; }); return ret; }; Promise.config({cancellation: true}); Promise.config({longStackTraces: false}); Promise.config({longStackTraces: true}); return Promise.each(testGroup, function(test, index, length) { var mocha = new Mocha({ reporter: "spec", timeout: "300s", enableTimeouts: true, slow: Infinity, bail: true }); mocha.addFile(test.path); return new Promise(function(resolve, reject) { mocha.run(function(failures) { if (failures === 0) { test.failed = false; progress(test); } resolve(); }).on("fail", function(_, err) { test.failed = true; progress(test); failures.push({ name: test.name, error: err }); }); }); }).then(function() { function failAdvice(failedTestFileName) { return "For additional details you can run it individually " + " with the command `node tools/test --run=" + failedTestFileName + "`"; } if (failures.length > 0) { var error; if (singleTest) { error = failures[0].error; } else { var message = "\u001b[31mSome tests failed: \u001b[m\n" failures.forEach(function(failResult) { message += " " + failResult.name + " " + failAdvice(failResult.name) + "\n"; }); error = new Error(message); error.noStackPrint = true; } throw error; } if (cover) { return __coverage__; } }); }; ================================================ FILE: tools/saucelabs_runner.js ================================================ /* Ripped from grunt-saucelabs node module and made independent on grunt MIT license: Copyright (c) 2012 Parashuram 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ module.exports = function(options) { var Promise = require("bluebird"); var SauceTunnel = require('grunt-saucelabs/node_modules/sauce-tunnel/index.js'); var TestRunner = require('grunt-saucelabs/src/TestRunner.js'); function reportProgress(notification) { switch (notification.type) { case 'tunnelOpen': console.log('=> Starting Tunnel to Sauce Labs'); break; case 'tunnelOpened': console.log('Connected to Saucelabs'); break; case 'tunnelClose': console.log('=> Stopping Tunnel to Sauce Labs'); break; case 'tunnelEvent': console.log(notification.text); break; case 'jobStarted': console.log('\n', notification.startedJobs, '/', notification.numberOfJobs, 'tests started'); break; case 'jobCompleted': console.log('\nTested %s', notification.url); console.log('Platform: %s', notification.platform); if (notification.tunnelId && unsupportedPort(notification.url)) { console.log('Warning: This url might use a port that is not proxied by Sauce Connect.'); } console.log('Passed: %s', notification.passed); console.log('Url %s', notification.jobUrl); break; case 'testCompleted': console.log('All tests completed with status %s', notification.passed); break; case 'retrying': console.log('Timed out, retrying'); break; default: console.error('Unexpected notification type'); } } function createTunnel(arg) { var tunnel; reportProgress({ type: 'tunnelOpen' }); tunnel = new SauceTunnel(arg.username, arg.key, arg.identifier, true, ['-P', '0'].concat(arg.tunnelArgs)); ['write', 'writeln', 'error', 'ok', 'debug'].forEach(function (method) { tunnel.on('log:' + method, function (text) { reportProgress({ type: 'tunnelEvent', verbose: false, method: method, text: text }); }); tunnel.on('verbose:' + method, function (text) { reportProgress({ type: 'tunnelEvent', verbose: true, method: method, text: text }); }); }); return tunnel; } function runTask(arg, framework) { var tunnel; return Promise .try(function () { var deferred; if (arg.tunneled) { deferred = Promise.defer(); tunnel = createTunnel(arg); tunnel.start(function (succeeded) { if (!succeeded) { deferred.reject('Could not create tunnel to Sauce Labs'); } else { reportProgress({ type: 'tunnelOpened' }); deferred.resolve(); } }); return deferred.promise; } }) .then(function () { var testRunner = new TestRunner(arg, framework, reportProgress); return testRunner.runTests(); }) .finally(function () { var deferred; if (tunnel) { deferred = Promise.defer(); reportProgress({ type: 'tunnelClose' }); tunnel.stop(function () { deferred.resolve(); }); return deferred.promise; } }); } function unsupportedPort(url) { // Not all ports are proxied by Sauce Connect. List of supported ports is // available at https://saucelabs.com/docs/connect#localhost var portRegExp = /:(\d+)\//; var matches = portRegExp.exec(url); var port = matches ? parseInt(matches[1], 10) : null; var supportedPorts = [ 80, 443, 888, 2000, 2001, 2020, 2109, 2222, 2310, 3000, 3001, 3030, 3210, 3333, 4000, 4001, 4040, 4321, 4502, 4503, 4567, 5000, 5001, 5050, 5555, 5432, 6000, 6001, 6060, 6666, 6543, 7000, 7070, 7774, 7777, 8000, 8001, 8003, 8031, 8080, 8081, 8765, 8888, 9000, 9001, 9080, 9090, 9876, 9877, 9999, 49221, 55001 ]; if (port) { return supportedPorts.indexOf(port) === -1; } return false; } var defaults = { username: process.env.SAUCE_USERNAME, key: process.env.SAUCE_ACCESS_KEY, tunneled: true, identifier: Math.floor((new Date()).getTime() / 1000 - 1230768000).toString(), pollInterval: 1000 * 2, maxPollRetries: 0, testname: '', browsers: [{}], tunnelArgs: [], sauceConfig: {}, maxRetries: 0 }; var opts = {}; options = options || {}; Object.keys(defaults).forEach(function(key) { opts[key] = defaults[key]; }); Object.keys(options).forEach(function(key) { opts[key] = options[key]; }); return runTask(opts, "mocha"); }; ================================================ FILE: tools/test.js ================================================ process.env.BLUEBIRD_WARNINGS = 0; var assert = require("assert"); assert.equal(require.main, module); var Promise = require("bluebird"); var build = require("./build.js"); var utils = require("./utils.js"); var tableLogger = utils.tableLogger; var argv = require("optimist").argv; var glob = Promise.promisify(require("glob")); var path = require("path"); var mkdirp = Promise.promisify(require("mkdirp")); var rimraf = Promise.promisify(require("rimraf")); var jobRunner = require("./job-runner/job-runner.js"); var mochaRunner = require("./mocha_runner.js"); var fs = Promise.promisifyAll(require("fs")); var testUtils = require("../test/mocha/helpers/util"); jobRunner.setVerbose(0); // Random slowness after tests complete function getTests(options) { var g; if (options.testName === "all" || options.testName.indexOf("no") === 0) { g = "./test/mocha/*.js"; } else if (options.testName === "aplus") { g = "./test/mocha/[0-9].[0-9].[0-9].js"; } else if (options.testName.indexOf("*") >= 0) { g = "./test/mocha/" + options.testName; } else { var testName = options.testName.replace(/^(\d)(\d)(\d)$/, "$1.$2.$3"); g = "./test/mocha/" + testName + ".js"; } var excludes = []; if (options.testName.indexOf("no") === 0) { excludes = options.testName.slice(2).split(","); } return glob(g).then(function(matches) { return matches.filter(function(match) { if (match.indexOf("generator") >= 0) { return options.generators; } for (var i = 0; i < excludes.length; ++i) { if (match.indexOf(excludes[i]) >= 0) { return false; } } return true; }) }).tap(function(m) { if (m.length === 0) { throw new Error("No test file matches: '" + options.testName + "'"); } }).map(function(filePath, i) { var name = path.basename(filePath); return { name: name, path: filePath, index: i, nameMatcher: "\\b" + name.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + "\\b" }; }); } function getColorForCoverage(coveragePct) { var colorThresholds = { 95: "brightgreen", 85: "green", 80: "yellowgreen", 70: "yellow", 60: "red" }; var values = Object.keys(colorThresholds).map(Number).sort(function(a, b) { return b - a; }); for (var i = 0; i < values.length; ++i) { if (coveragePct >= values[i]) return colorThresholds[values[i].toString()]; } return colorThresholds[values[values.length - 1].toString()]; } function getCoverage() { return utils.run("npm", ["run", "istanbul", "--", "report", "text-summary"]).then(function(result) { var stdout = result.stdout; var pctPattern = /(\d+\.\d+)%/g; var matches = stdout.match(pctPattern); var sum = matches.map(function(pct) { return parseFloat(pct.replace(/[^0-9.]/g, "")) }).reduce(function(a, b) { return a + b; }, 0); var average = Math.round(sum / matches.length); return average; }); } function generateCoverageBadge(coverage) { var text = "coverage-" + coverage + "%"; var color = getColorForCoverage(coverage); var imgSrc = "http://img.shields.io/badge/" + text + "-" + color + ".svg?style=flat"; var link = "http://petkaantonov.github.io/bluebird/coverage/debug/index.html"; var markdown = "[!["+text+"]("+imgSrc+")]("+link+")"; return markdown; } function writeCoverageFile(coverage, groupNumber) { var dir = build.dirs.coverage; var fileName = path.join(dir, "coverage-group" + groupNumber + ".json"); var json = JSON.stringify(coverage); return fs.writeFileAsync(fileName, json, "utf8").thenReturn(fileName); } function needsFreshProcess(testName) { return /regress|using|domain|multiple-copies|unhandled_rejections|nodeify|getNewLibraryCopy|long_stack_traces/.test(testName) || testUtils.isOldNode && /api_exceptions|promisify/.test(testName); } function runTestGroup(testGroup, options, progress) { return jobRunner.run(mochaRunner, { isolated: !options.singleTest, log: options.singleTest, progress: progress, context: { testGroup: testGroup, singleTest: options.singleTest, fakeTimers: options.fakeTimers, cover: options.cover } }); } function combineTests(tests) { var arrays = new Array(jobRunner.CHILD_PROCESSES); for (var i = 0; i < arrays.length; ++i) { arrays[i] = []; } var initialLength = arrays.length; for (var i = 0; i < tests.length; ++i) { var test = tests[i]; if (needsFreshProcess(test.name)) { arrays.push([test]); } else { arrays[i % initialLength].push(tests[i]); } } return arrays; } var testName = "all"; if ("run" in argv) { testName = (argv.run + ""); if (testName.indexOf("*") === -1) { testName = testName.toLowerCase() .replace( /\.js$/, "" ) .replace( /[^,a-zA-Z0-9_\-.]/g, "" ); } } var options = { generators: (function() { try { new Function("(function*(){})"); return true; } catch (e) { return false; } })() || !!argv.nw, cover: !!argv["cover"], testName: testName, singleTest: false, saucelabs: !!argv.saucelabs, testBrowser: !!argv.saucelabs || !!argv.browser || !!argv.nw, executeBrowserTests: !!argv.saucelabs || !!argv.nw || (typeof argv["execute-browser-tests"] === "boolean" ? argv["execute-browser-tests"] : !!argv.browser), openBrowser: typeof argv["open-browser"] === "boolean" ? argv["open-browser"] : true, port: argv.port || 9999, fakeTimers: typeof argv["fake-timers"] === "boolean" ? argv["fake-timers"] : true, jsHint: typeof argv["js-hint"] === "boolean" ? argv["js-hint"] : true, nw: !!argv.nw, nwPath: argv["nw-path"] }; if (options.cover && typeof argv["cover"] === "string") { options.coverFormat = argv["cover"]; } else { options.coverFormat = "html"; } var jsHint = options.jsHint ? require("./jshint.js")() : Promise.resolve(); var tests = getTests(options); var buildOpts = { debug: true }; if (options.testBrowser) { buildOpts.browser = true; buildOpts.minify = true; } var buildResult = build(buildOpts); if (options.cover) { var exclusions = ["assert.js", "captured_trace.js"]; var coverageInstrumentedRoot = build.ensureDirectory(build.dirs.instrumented,options.cover, true); var coverageReportsRoot = mkdirp(build.dirs.coverage, true).then(function() { return fs.readdirAsync(build.dirs.coverage); }).map(function(fileName) { var filePath = path.join(build.dirs.coverage, fileName); if (path.extname(fileName).indexOf("json") === -1) { return rimraf(filePath); } }); buildResult = Promise.join(coverageInstrumentedRoot, buildResult, coverageReportsRoot, function() { return utils.run("npm", ["-v"]).then(function(result) { var version = result.stdout.split(".").map(Number); if (version[0] < 2) { throw new Error("Npm version 2.x.x required, current version is " + result.stdout); } }); }).tap(function() { var copyExclusions = Promise.map(exclusions, function(exclusion) { var fromPath = path.join(build.dirs.debug, exclusion); var toPath = path.join(build.dirs.instrumented, exclusion); return fs.readFileAsync(fromPath, "utf8").then(function(contents) { return fs.writeFileAsync(toPath, contents, "utf8"); }); }); var args = [ "run", "istanbul", "--", "instrument", "--output", build.dirs.instrumented, "--no-compact", "--preserve-comments", "--embed-source" ]; exclusions.forEach(function(x) { args.push("-x", x); }); args.push(build.dirs.debug); var istanbul = utils.run("npm", args, null, true); return Promise.all([istanbul, copyExclusions]); }); } var testResults = Promise.join(tests, buildResult, function(tests) { var singleTest = tests.length === 1; options.singleTest = singleTest; process.stdout.write("\u001b[m"); if (options.testBrowser) { return require("./browser_test_generator.js")(tests, options); } else if (singleTest) { return runTestGroup(tests, options); } else { utils.cursorTo(0, 0); utils.clearScreenDown(); tableLogger.addTests(tests); return Promise.map(combineTests(tests), function(testGroup, index) { return runTestGroup(testGroup, options, function(test) { if (test.failed) { tableLogger.testFail(test); } else { tableLogger.testSuccess(test); } }).then(function(maybeCoverage) { if (options.cover) { return writeCoverageFile(maybeCoverage.result, index + 1); } }) }).then(function() { var p = Promise.resolve(); if (options.cover) { var coverage = getCoverage(); if (process.execPath.indexOf("iojs") >= 0 && testName === "all") { p = p.return(coverage).then(generateCoverageBadge).then(console.log); } p = p.then(function() { return utils.run("npm", ["run", "istanbul", "--", "report", options.coverFormat], null, true); }).return(coverage).then(function(coverage) { console.log("Total coverage " + coverage + "%"); }); } console.log("All tests passed"); return p; }); } }); Promise.all([testResults, jsHint]).spread(function(_, jsHintResponse) { if (jsHintResponse) { console.log("JSHint:"); console.log(jsHintResponse.stdout); console.log(jsHintResponse.stderr); } }).catch(function(e) { if (e && e.stdout || e.stderr) { console.log(e.stdout); console.error(e.stderr); } if (!e || !e.stack) { console.error(e + ""); } else { console.error(e.noStackPrint ? e.message : e.stack); } process.exit(2); }); ================================================ FILE: tools/utils.js ================================================ var Promise = require("bluebird"); var assert = require("assert"); var path = require("path"); var spawn = require("cross-spawn"); Promise.longStackTraces(); var fs = Promise.promisifyAll(require("fs")); var notAscii = /[^\u000D\u0019-\u007E]/; var Table = require('cli-table'); function noStackError(message) { var e = new Error(message); e.noStackPrint = true; return e; } function checkAscii(fileName, contents) { if (notAscii.test(contents)) { contents.split("\n").forEach(function(line, i) { if (notAscii.test(line)) { var lineNo = i + 1; var col = line.indexOf(RegExp.lastMatch) + 1; var code = "U+" + (("0000" + line.charCodeAt(col-1) .toString(16)).slice(-4)); code = RegExp.lastMatch + " (" + code.toUpperCase() + ")"; var fullPath = path.join(process.cwd(), "src", fileName); throw noStackError("The file " + fullPath + "\ncontains an illegal character: " + code + " on line " + lineNo + " at column " + col); } }); } } var license; function getLicense() { if (license) return license; var licenseFile = path.join(__dirname, "..", "LICENSE"); return license = fs.readFileAsync(licenseFile, "utf8").then(function(text) { return "/* @preserve\n" + text.split("\n").map(function(line) { return " * " + line; }).join("\n") + "\n */\n"; }); } function cursorTo(x, y) { if (process.stdout.cursorTo) process.stdout.cursorTo(x, y); } function clearScreenDown() { if (process.stdout.clearScreenDown) process.stdout.clearScreenDown(); } function run(cmd, args, dir, log) { return new Promise(function(resolve, reject) { function makeResult(errorMessage) { var ret = errorMessage ? new Error(errorMessage) : {}; ret.stdout = out.trim(); ret.stderr = err.trim(); return ret; } var out = ""; var err = ""; var c = spawn(cmd, args, {stdin: ["ignore", "ignore", "ignore"], cwd: dir || process.cwd()}); c.stdout.on("data", function(data) { if (log) process.stdout.write(data.toString()); out += data; }); c.stderr.on("data", function(data) { if (log) process.stderr.write(data.toString()); err += data; }); c.on("error", function(err) { reject(makeResult(err.message)); }); c.on("close", function(code) { if (code == 0) resolve(makeResult()) else reject(makeResult(path.basename(cmd) + " exited with code: " + code + "\n" + err.trim())); }) }); } function parseDeps(src) { var rdeps = /function\s*\(\s*([^)]+)\)/; var match = rdeps.exec(src); assert.equal(match.length, 2); var deps = match[1].split(/\s*,\s*/g).map(function(val) { return val.trim(); }); return deps; } var tableLogger = (function() { var metaKeyCodeReAnywhere = /(?:\x1b)([a-zA-Z0-9])/; var functionKeyCodeReAnywhere = new RegExp('(?:\x1b+)(O|N|\\[|\\[\\[)(?:' + [ '(\\d+)(?:;(\\d+))?([~^$])', '(?:M([@ #!a`])(.)(.))', // mouse '(?:1;)?(\\d+)?([a-zA-Z])' ].join('|') + ')'); function stripVTControlCharacters(str) { str = str.replace(new RegExp(functionKeyCodeReAnywhere.source, 'g'), ''); return str.replace(new RegExp(metaKeyCodeReAnywhere.source, 'g'), ''); } var ROWS = 35; var log = new Array(ROWS); for (var i = 0; i < ROWS; ++i) log[i] = []; var tableOpts = { chars: { 'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' }, style: { 'padding-left': 0, 'padding-right': 0, compact: true } }; var table; var split; function showTable() { assert(!table); table = new Table(tableOpts); table.push.apply(table, log); table = table.toString(); split = table.split("\n").map(function(line) { return stripVTControlCharacters(line); }); cursorTo(0, 0); process.stdout.write(table); cursorTo(0, split.length + 1); } function addTests(tests) { var cols = 0; tests.forEach(function(test) { var index = test.index; var row = index % ROWS; var column = (index / ROWS) | 0; cols = Math.max(column, cols); log[row][column] = "\u001b[m" + test.name + " \u001b[31m\u00D7 "; }); cols = cols + 1; for (var i = 0; i < log.length; ++i) { var row = log[i]; for (var j = 0; j < cols; ++j) { if (!row[j]) { row[j] = " "; } } } showTable(); } function getPosition(test) { for (var y = 0; y < split.length; ++y) { var s = split[y]; var x = s.search(new RegExp(test.nameMatcher)); if (x >= 0) { return { x: x + test.name.length, y: y }; } } assert(false); } function update(test, message) { var pos = getPosition(test); cursorTo(pos.x + 1, pos.y); process.stdout.write(message); cursorTo(0, split.length + 2); } function testFail(test) { update(test, "\u001b[31m\u00D7 FAILURE\u001b[39m"); } function testSuccess(test) { update(test, "\u001b[32m\u221A\u001b[39m") } return { addTests: addTests, testFail: testFail, testSuccess: testSuccess } })(); function stringToStream(str) { var Readable = require('stream').Readable; var readable = new Readable() readable.push(str + ""); readable.push(null); return readable; } module.exports = { checkAscii: checkAscii, getLicense: getLicense, run: run, parseDeps: parseDeps, tableLogger: tableLogger, stringToStream: stringToStream, cursorTo: cursorTo, clearScreenDown: clearScreenDown };