Import Upstream version 3.7.2+dfsg1

This commit is contained in:
zhouganqing 2023-02-17 11:33:40 +08:00
commit be76ba655c
153 changed files with 35441 additions and 0 deletions

14
.editorconfig Normal file
View File

@ -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

11
.gitignore vendored Normal file
View File

@ -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

49
.istanbul.yml Normal file
View File

@ -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: []

1
.jshintignore Normal file
View File

@ -0,0 +1 @@
src/constants.js

161
.jshintrc Normal file
View File

@ -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
}
}

26
.travis.yml Normal file
View File

@ -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"

1
API.md Normal file
View File

@ -0,0 +1 @@
[http://bluebirdjs.com/docs/api-reference.html](http://bluebirdjs.com/docs/api-reference.html)

9
CONTRIBUTING.md Normal file
View File

@ -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

21
LICENSE Normal file
View File

@ -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.

57
README.md Normal file
View File

@ -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.

39
bench Executable file
View File

@ -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

34
bower.json Normal file
View File

@ -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"
]
}

2
build Executable file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env bash
node tools/build.js "$@"

1
changelog.md Normal file
View File

@ -0,0 +1 @@
[http://bluebirdjs.com/docs/changelog.html](http://bluebirdjs.com/docs/changelog.html)

1
deprecated_apis.md Normal file
View File

@ -0,0 +1 @@
[http://bluebirdjs.com/docs/deprecated-apis.html](http://bluebirdjs.com/docs/deprecated-apis.html)

12
issue_template.md Normal file
View File

@ -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)

3209
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

78
package.json Normal file
View File

@ -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"
]
}

25
src/any.js Normal file
View File

@ -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);
};
};

55
src/assert.js Normal file
View File

@ -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;
};
})();

129
src/async.js Normal file
View File

@ -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;

67
src/bind.js Normal file
View File

@ -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);
};
};

11
src/bluebird.js Normal file
View File

@ -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;

123
src/call_get.js Normal file
View File

@ -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);
};
};

136
src/cancel.js Normal file
View File

@ -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();
};
};

42
src/catch_filter.js Normal file
View File

@ -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;
};

154
src/constants.js Normal file
View File

@ -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");

69
src/context.js Normal file
View File

@ -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;
};

1043
src/debuggability.js Normal file

File diff suppressed because it is too large Load Diff

46
src/direct_resolve.js Normal file
View File

@ -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);
}
};
};

30
src/each.js Normal file
View File

@ -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;
};

117
src/errors.js Normal file
View File

@ -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
};

80
src/es5.js Normal file
View File

@ -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;
}
};
}

12
src/filter.js Normal file
View File

@ -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);
};
};

146
src/finally.js Normal file
View File

@ -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;
};

230
src/generators.js Normal file
View File

@ -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;
};
};

165
src/join.js Normal file
View File

@ -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;
};
};

192
src/map.js Normal file
View File

@ -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);
};
};

57
src/method.js Normal file
View File

@ -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);
}
};
};

51
src/nodeback.js Normal file
View File

@ -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;

62
src/nodeify.js Normal file
View File

@ -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;
};
};

827
src/promise.js Normal file
View File

@ -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__";
};

203
src/promise_array.js Normal file
View File

@ -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;
};

326
src/promisify.js Normal file
View File

@ -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);
};
};

125
src/props.js Normal file
View File

@ -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);
};
};

79
src/queue.js Normal file
View File

@ -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;

50
src/race.js Normal file
View File

@ -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);
};
};

191
src/reduce.js Normal file
View File

@ -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;
}
};

79
src/schedule.js Normal file
View File

@ -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;

55
src/settle.js Normal file
View File

@ -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);
};
};

159
src/some.js Normal file
View File

@ -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;
};

View File

@ -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;
};

89
src/thenables.js Normal file
View File

@ -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;
};

93
src/timers.js Normal file
View File

@ -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;
};
};

226
src/using.js Normal file
View File

@ -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();
};
};

432
src/util.js Normal file
View File

@ -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;

76
test/mocha/2.1.2.js Normal file
View File

@ -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);
});
});

76
test/mocha/2.1.3.js Normal file
View File

@ -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);
});
});

41
test/mocha/2.2.1.js Normal file
View File

@ -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");
});
});

151
test/mocha/2.2.2.js Normal file
View File

@ -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();
});
});
});
});

151
test/mocha/2.2.3.js Normal file
View File

@ -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();
});
});
});
});

182
test/mocha/2.2.4.js Normal file
View File

@ -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();
});
});
});
});

58
test/mocha/2.2.5.js Normal file
View File

@ -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();
});
});
});
});

257
test/mocha/2.2.6.js Normal file
View File

@ -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);
});
});
});
});
});
});

109
test/mocha/2.2.7.js Normal file
View File

@ -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");
});
});

34
test/mocha/2.3.1.js Normal file
View File

@ -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();
});
});
});

126
test/mocha/2.3.2.js Normal file
View File

@ -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();
});
});
});
});
});

993
test/mocha/2.3.3.js Normal file
View File

@ -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`");
});
});

69
test/mocha/2.3.4.js Normal file
View File

@ -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;
}
);
});

41
test/mocha/3.2.1.js Normal file
View File

@ -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");
});
});

187
test/mocha/3.2.2.js Normal file
View File

@ -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);
});
});
});

187
test/mocha/3.2.3.js Normal file
View File

@ -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);
});
});
});

31
test/mocha/3.2.4.js Normal file
View File

@ -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;
});
});

257
test/mocha/3.2.5.js Normal file
View File

@ -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);
});
});
});
});
});
});

322
test/mocha/3.2.6.js Normal file
View File

@ -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");
});
});

112
test/mocha/any.js Normal file
View File

@ -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);
});
});
});

View File

@ -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(){});
}

245
test/mocha/async.js Normal file
View File

@ -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");
});
});
});
}
});

145
test/mocha/async_hooks.js Normal file
View File

@ -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()));
}));
});
});
});
}

1225
test/mocha/bind.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);
});
});
});
}

57
test/mocha/call.js Normal file
View File

@ -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);
}
});
});
});

3184
test/mocha/cancel.js Normal file

File diff suppressed because it is too large Load Diff

431
test/mocha/catch_filter.js Normal file
View File

@ -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);
});
});
});

View File

@ -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);
});

201
test/mocha/constructor.js Normal file
View File

@ -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);
});
});
});
});

68
test/mocha/cycles.js Normal file
View File

@ -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);
});
});
});
});

View File

@ -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);
});
});
});

503
test/mocha/domain.js Normal file
View File

@ -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();
})
});
});
})
});
}

131
test/mocha/done.js Normal file
View File

@ -0,0 +1,131 @@
"use strict";
var assert = require("assert");
var testUtils = require("./helpers/util.js");
var isNodeJS = testUtils.isNodeJS;
/*!
*
Copyright 20092012 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);
});
});
});
}
});
});

184
test/mocha/each.js Normal file
View File

@ -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]);
});
})
});

184
test/mocha/error.js Normal file
View File

@ -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);
});
}
});
});

120
test/mocha/filter.js Normal file
View File

@ -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);
});
});
});

274
test/mocha/finally.js Normal file
View File

@ -0,0 +1,274 @@
"use strict";
var assert = require("assert");
var testUtils = require("./helpers/util.js");
/*!
*
Copyright 20092012 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);
});
});
});
});
});

185
test/mocha/following.js Normal file
View File

@ -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);
});
});
});
});

754
test/mocha/generator.js Normal file
View File

@ -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);
});
});

83
test/mocha/get.js Normal file
View File

@ -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;
});
});

View File

@ -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);
});
});

View File

@ -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');
});
});
});
}

View File

@ -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