Import Upstream version 3.7.2+dfsg1
This commit is contained in:
commit
be76ba655c
|
@ -0,0 +1,14 @@
|
||||||
|
# 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
|
|
@ -0,0 +1,11 @@
|
||||||
|
docs/Gemfile.lock
|
||||||
|
gh-pages
|
||||||
|
node_modules
|
||||||
|
gh-pages
|
||||||
|
test/browser/bundle.js
|
||||||
|
test/browser/worker_bundle.js
|
||||||
|
js/*
|
||||||
|
zalgo.js
|
||||||
|
coverage/*
|
||||||
|
.vscode
|
||||||
|
.idea
|
|
@ -0,0 +1,49 @@
|
||||||
|
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: []
|
|
@ -0,0 +1 @@
|
||||||
|
src/constants.js
|
|
@ -0,0 +1,161 @@
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
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"
|
|
@ -0,0 +1 @@
|
||||||
|
[http://bluebirdjs.com/docs/api-reference.html](http://bluebirdjs.com/docs/api-reference.html)
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
# 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
|
|
@ -0,0 +1,21 @@
|
||||||
|
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.
|
|
@ -0,0 +1,57 @@
|
||||||
|
<a href="http://promisesaplus.com/">
|
||||||
|
<img src="http://promisesaplus.com/assets/logo-small.png" alt="Promises/A+ logo"
|
||||||
|
title="Promises/A+ 1.1 compliant" align="right" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
[![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
|
||||||
|
|
||||||
|
Promises in Node.js 10 are significantly faster than before. Bluebird still includes a lot of features like cancellation, iteration methods and warnings that native promises don't. If you are using Bluebird for performance rather than for those - please consider giving native promises a shot and running the benchmarks yourself.
|
||||||
|
|
||||||
|
# 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-2019 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.
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "bluebird",
|
||||||
|
"version": "3.7.2",
|
||||||
|
"homepage": "https://github.com/petkaantonov/bluebird",
|
||||||
|
"authors": [
|
||||||
|
"Petka Antonov <petka_antonov@hotmail.com>"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[http://bluebirdjs.com/docs/changelog.html](http://bluebirdjs.com/docs/changelog.html)
|
|
@ -0,0 +1 @@
|
||||||
|
[http://bluebirdjs.com/docs/deprecated-apis.html](http://bluebirdjs.com/docs/deprecated-apis.html)
|
|
@ -0,0 +1,12 @@
|
||||||
|
(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 diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,78 @@
|
||||||
|
{
|
||||||
|
"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.4.1",
|
||||||
|
"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": "~2.1",
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
"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);
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,55 @@
|
||||||
|
"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;
|
||||||
|
};
|
||||||
|
})();
|
|
@ -0,0 +1,129 @@
|
||||||
|
"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;
|
|
@ -0,0 +1,67 @@
|
||||||
|
"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);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
"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;
|
|
@ -0,0 +1,123 @@
|
||||||
|
"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);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,136 @@
|
||||||
|
"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();
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
"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;
|
||||||
|
};
|
|
@ -0,0 +1,154 @@
|
||||||
|
//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");
|
|
@ -0,0 +1,69 @@
|
||||||
|
"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 diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,46 @@
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
"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;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
"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
|
||||||
|
};
|
|
@ -0,0 +1,80 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
"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);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,146 @@
|
||||||
|
"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;
|
||||||
|
};
|
|
@ -0,0 +1,230 @@
|
||||||
|
"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;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,165 @@
|
||||||
|
"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;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,192 @@
|
||||||
|
"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);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,57 @@
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,51 @@
|
||||||
|
"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;
|
|
@ -0,0 +1,62 @@
|
||||||
|
"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;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,827 @@
|
||||||
|
"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__";
|
||||||
|
};
|
|
@ -0,0 +1,203 @@
|
||||||
|
"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;
|
||||||
|
};
|
|
@ -0,0 +1,326 @@
|
||||||
|
"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);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
"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);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,79 @@
|
||||||
|
"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;
|
|
@ -0,0 +1,50 @@
|
||||||
|
"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);
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,191 @@
|
||||||
|
"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;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,79 @@
|
||||||
|
"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;
|
|
@ -0,0 +1,55 @@
|
||||||
|
"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);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,159 @@
|
||||||
|
"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;
|
||||||
|
};
|
|
@ -0,0 +1,103 @@
|
||||||
|
"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;
|
||||||
|
};
|
|
@ -0,0 +1,89 @@
|
||||||
|
"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;
|
||||||
|
};
|
|
@ -0,0 +1,93 @@
|
||||||
|
"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;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,226 @@
|
||||||
|
"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();
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,432 @@
|
||||||
|
"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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toFastProperties(obj) {
|
||||||
|
/*jshint -W027,-W055,-W031*/
|
||||||
|
function FakeConstructor() {}
|
||||||
|
FakeConstructor.prototype = obj;
|
||||||
|
var receiver = new FakeConstructor();
|
||||||
|
function ic() {
|
||||||
|
return typeof receiver.foo;
|
||||||
|
}
|
||||||
|
ic();
|
||||||
|
ic();
|
||||||
|
ASSERT("%HasFastProperties", true, obj);
|
||||||
|
return obj;
|
||||||
|
// Prevent the function from being optimized through dead code elimination
|
||||||
|
// or further optimizations. This code is never reached but even using eval
|
||||||
|
// in unreachable code causes v8 to not optimize functions.
|
||||||
|
eval(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;
|
|
@ -0,0 +1,76 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,76 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,41 @@
|
||||||
|
"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");
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,151 @@
|
||||||
|
"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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,151 @@
|
||||||
|
"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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,182 @@
|
||||||
|
"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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,257 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,109 @@
|
||||||
|
"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");
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,34 @@
|
||||||
|
"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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,126 @@
|
||||||
|
"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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,993 @@
|
||||||
|
"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`");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,69 @@
|
||||||
|
"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;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,41 @@
|
||||||
|
"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");
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,187 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,187 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
"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;
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,257 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,322 @@
|
||||||
|
"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");
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,112 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,231 @@
|
||||||
|
"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(){});
|
||||||
|
}
|
|
@ -0,0 +1,245 @@
|
||||||
|
"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");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,145 @@
|
||||||
|
"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 diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,107 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
"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 diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,431 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,355 @@
|
||||||
|
"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);
|
||||||
|
});
|
|
@ -0,0 +1,201 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,68 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,257 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,503 @@
|
||||||
|
"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();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,184 @@
|
||||||
|
"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]);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
|
@ -0,0 +1,184 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,120 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,274 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,185 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,754 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,83 @@
|
||||||
|
"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;
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,30 @@
|
||||||
|
"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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,21 @@
|
||||||
|
"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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
"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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue