├── tests
├── helpers
│ ├── error.js
│ ├── reasons.js
│ ├── testThreeCases.js
│ ├── assert_long_trace.js
│ ├── thenables.js
│ └── util.js
├── aplus.js
├── OperationalError.js
├── getNewLibraryCopy.js
├── finally.js
├── AggregateError.js
├── asCallback.js
├── suppressUnhandledRejections.js
├── any.js
├── try.js
├── method.js
├── defer.js
├── reflect.js
├── setScheduler.js
├── call.js
├── tap.js
├── get.js
├── some.js
├── error.js
├── join.js
├── map.js
├── using.js
├── return.js
├── tapCatch.js
├── filter.js
├── each.js
├── timers.js
├── props.js
├── spread.js
├── catchFilter.js
└── reduce.js
├── promiseFns
├── cast.js
├── throw.js
├── spread.js
├── return.js
├── done.js
├── cancel.js
├── suppressUnhandledRejections.js
├── defer.js
├── noConflict.js
├── try.js
├── utils
│ └── nextTick.js
├── delay.js
├── catchReturn.js
├── catchThrow.js
├── any.js
├── method.js
├── disposer.js
├── error.js
├── join.js
├── finally.js
├── asCallback.js
├── tap.js
├── map.js
├── each.js
├── call.js
├── filter.js
├── props.js
├── get.js
├── mapSeries.js
├── tapCatch.js
├── then.js
├── reduce.js
├── timeout.js
├── reflect.js
├── setScheduler.js
├── some.js
├── onPossiblyUnhandledRejection.js
├── util.js
├── catch.js
├── using.js
├── coroutine.js
└── promisify.js
├── index.js
├── webpack.config.js
├── promiseErrors
├── CancellationError.js
├── TimeoutError.js
├── OperationalError.js
└── AggregateError.js
├── docs
├── index.html
└── index.js
├── .vscode
└── launch.json
├── .gitignore
├── package.json
├── LICENSE
├── promise.js
└── README.md
/tests/helpers/error.js:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/promiseFns/cast.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.cast = Promise.resolve;
3 | };
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const Bluebird = require("./promise.js");
2 | module.exports = Bluebird
3 |
4 | if (typeof window !== 'undefined') {
5 | window.Bluebird = Bluebird;
6 | }
--------------------------------------------------------------------------------
/promiseFns/throw.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.throw = function throwing(value) {
3 | return this.then(() => { throw value });
4 | };
5 | };
--------------------------------------------------------------------------------
/promiseFns/spread.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.spread = function spread(fn) {
3 | return this.all().then(results => fn(...results));
4 | };
5 | }
--------------------------------------------------------------------------------
/promiseFns/return.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 |
3 | Bluebird.prototype.return =
4 | Bluebird.prototype.thenReturn = function thenReturn(any) {
5 | return this.then(x => any);
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/promiseFns/done.js:
--------------------------------------------------------------------------------
1 | const schedule = require("./utils/nextTick.js");
2 | module.exports = (Bluebird) => {
3 | Bluebird.prototype.done = function() {
4 | this.catch(e => schedule(() => { throw e; }) );
5 | };
6 | };
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | entry: './index.js',
5 | output: {
6 | path: path.resolve(__dirname, 'dist'),
7 | filename: 'bluebird-api.js'
8 | },
9 | };
--------------------------------------------------------------------------------
/promiseFns/cancel.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | let once = false;
3 | Bluebird.prototype.cancel = () => {
4 | if(once) continue;
5 | once = true;
6 | console.warn("Cancellation is disabled and not supported");
7 | }
8 | };
--------------------------------------------------------------------------------
/promiseFns/suppressUnhandledRejections.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | // no cancellation means simple implementation here
3 | Bluebird.prototype.suppressUnhandledRejections = function() {
4 | this.catch(() => {});
5 | return this;
6 | };
7 | };
--------------------------------------------------------------------------------
/promiseFns/defer.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.defer = function defer() {
3 | let resolve, reject,
4 | promise = new Bluebird((res, rej) => [resolve, reject] = [res, rej]);
5 | return { promise, resolve, reject}
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/promiseFns/noConflict.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.noConflict = () => {
3 | console.error("Please do not call noConflict with bluebird-api, simply do not import it!");
4 | console.error("See getNewLibraryCopy for copying bluebird");
5 | };
6 | };
--------------------------------------------------------------------------------
/promiseFns/try.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.try = function(fn) {
3 | if(typeof fn !== 'function') {
4 | return Bluebird.reject(new TypeError("fn must be a function"));
5 | }
6 | return Bluebird.resolve().then(fn);
7 | };
8 | };
--------------------------------------------------------------------------------
/promiseFns/utils/nextTick.js:
--------------------------------------------------------------------------------
1 | if(typeof process !== "undefined" && typeof process.nextTick === "function") {
2 | module.exports = process.nextTick;
3 | } else if (typeof setImmediate === "function") {
4 | module.exports = setImmediate;
5 | } else {
6 | module.exports = setTimeout;
7 | }
--------------------------------------------------------------------------------
/promiseFns/delay.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.delay = function delay(ms) {
3 | return this.then(obj => new Bluebird((onFulfilled) => setTimeout(() => onFulfilled(obj), ms)));
4 | }
5 | Bluebird.delay = (ms, o) => Bluebird.resolve(o).delay(ms);
6 | }
7 |
--------------------------------------------------------------------------------
/promiseFns/catchReturn.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.catchReturn = function catchReturn (value) {
3 | const filters = Array.prototype.slice.call(arguments, 0, -1);
4 |
5 | return this.catch.apply(this, [ ...filters, () => value ]);
6 | }
7 | };
--------------------------------------------------------------------------------
/promiseFns/catchThrow.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.catchThrow = function catchThrow(value) {
3 | const filters = Array.prototype.slice.call(arguments, 0, -1);
4 |
5 | return this.catch.apply(this, [ ...filters, () => { throw value; } ]);
6 | }
7 | };
--------------------------------------------------------------------------------
/promiseFns/any.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.any = (prom, n) => Bluebird.resolve(prom).any();
3 | Bluebird.prototype.any = async function() {
4 | // const items = await this;
5 | // if(items.length === 0) { throw new TypeError("0 promises passed to any")}
6 | return this.some(1).get(0);
7 | }
8 | };
--------------------------------------------------------------------------------
/promiseErrors/CancellationError.js:
--------------------------------------------------------------------------------
1 | module.exports = function (Bluebird) {
2 | // cancellation is not supported, but let's export the error type for compat
3 | class CancellationError extends Error {
4 | constructor(message) {
5 | super(message);
6 | }
7 | };
8 | Bluebird.CancellationError = CancellationError;
9 | };
--------------------------------------------------------------------------------
/promiseFns/method.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 |
3 | Bluebird.method = function(fn) {
4 | if(typeof fn !== "function") {
5 | throw new TypeError("Non function passed to .method");
6 | }
7 | return async function() {
8 | return fn.apply(this, arguments);
9 | };
10 | };
11 | };
--------------------------------------------------------------------------------
/promiseErrors/TimeoutError.js:
--------------------------------------------------------------------------------
1 | module.exports = function (Bluebird) {
2 | require("./OperationalError.js")(Bluebird);
3 | class TimeoutError extends Bluebird.OperationalError {
4 | constructor(message) {
5 | super(message || "timeout error");
6 | }
7 |
8 | };
9 | Bluebird.TimeoutError = TimeoutError;
10 | };
11 |
--------------------------------------------------------------------------------
/promiseFns/disposer.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.disposer = function disposer(fn) {
3 | return {
4 | _use: this,
5 | _cleanup: fn,
6 | then() {
7 | throw new Error("A disposer is not a promise. Use it with `Promise.using`.")
8 | }
9 | };
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/promiseFns/error.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.error = function error(handler) {
3 | return this.catch(isOperationalError, handler);
4 | };
5 | function isOperationalError(e) {
6 | if (e == null) return false;
7 | return (e instanceof Bluebird.OperationalError) || (e.isOperational === true);
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/promiseFns/join.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.join = async function join(...args) {
3 | const last = args.slice(-1)[0];
4 |
5 | if (typeof last === "function") {
6 | const otherArgs = args.slice(0, -1);
7 | let values = await Bluebird.all(otherArgs);
8 | return await last(...values);
9 | }
10 | else {
11 | return await Bluebird.all(args);
12 | }
13 | };
14 | };
--------------------------------------------------------------------------------
/promiseFns/finally.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.finally = Bluebird.prototype.lastly = function(onResolved) {
3 | return Bluebird.resolve((async () => {
4 | try {
5 | var res = await this;
6 | } catch (e) {
7 | await onResolved();
8 | throw e;
9 | }
10 | await onResolved();
11 | return res;
12 | })());
13 | };
14 | };
--------------------------------------------------------------------------------
/promiseErrors/OperationalError.js:
--------------------------------------------------------------------------------
1 | module.exports = function (Bluebird) {
2 | class OperationalError extends Error {
3 | constructor(message) {
4 | super(message);
5 | }
6 | get isOperational() {
7 | return true;
8 | }
9 |
10 | static fromError(err) {
11 | return Object.assign(new OperationalError(err.message), {
12 | stack: err.stack
13 | });
14 | }
15 | };
16 | Bluebird.OperationalError = OperationalError;
17 | };
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Bluebird API example
4 |
5 |
6 |
7 |
8 |
9 |
10 | Hello World!
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/promiseFns/asCallback.js:
--------------------------------------------------------------------------------
1 | const escapePromiseCatch = require("./utils/nextTick.js");
2 | module.exports = (Bluebird) => {
3 | Bluebird.prototype.nodeify = // back compat alias
4 | Bluebird.prototype.asCallback = function asCallback(cb, opts) {
5 | const spread = opts && opts.spread;
6 | this.catch(() => {}); // opt out of unhandledRejection detection
7 | this.then(v => escapePromiseCatch (() => {
8 | if(spread) cb(null, ...v);
9 | else cb(null, v);
10 | }), e => escapePromiseCatch(() => cb(e)) );
11 | };
12 | };
--------------------------------------------------------------------------------
/promiseFns/tap.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.tap = function tap(onFullfiled) {
3 | return Bluebird.resolve((async () => {
4 | const value = await this;
5 | await onFullfiled(value);
6 | return value;
7 | })());
8 | };
9 |
10 | Bluebird.tap = function tap(promise, onFullfiled) {
11 | return Bluebird.resolve((async () => {
12 | const value = await promise;
13 | await onFullfiled(value);
14 | return value;
15 | })());
16 | };
17 | };
--------------------------------------------------------------------------------
/promiseFns/map.js:
--------------------------------------------------------------------------------
1 | const util = require("./util");
2 | module.exports = (Bluebird) => {
3 | Bluebird.map = (x, mapper, opts) => Bluebird.resolve(x).map(mapper, opts);
4 |
5 | Bluebird.prototype.map = async function(mapper, {concurrency} = {}) {
6 | const values = await Bluebird.all(await this);
7 | if(!concurrency) {
8 | return await Bluebird.all(values.map(mapper));
9 | }
10 | const throttled = util.throttle(mapper, Number(concurrency), Bluebird);
11 | return await Bluebird.all(values.map(throttled));
12 | };
13 | };
14 |
15 |
--------------------------------------------------------------------------------
/promiseFns/each.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.each = function each(iterator) {
3 | return Bluebird.resolve((async () => {
4 | const promises = await Promise.all(await this);
5 | const length = promises.length;
6 |
7 | for(let index = 0; index <= promises.length-1; index++){
8 | const value = await iterator(await promises[index], index, length);
9 | };
10 | return promises;
11 | })());
12 | };
13 |
14 | Bluebird.each = (promise, iterator) => Bluebird.resolve(promise).each(iterator);
15 | };
--------------------------------------------------------------------------------
/promiseErrors/AggregateError.js:
--------------------------------------------------------------------------------
1 | module.exports = function (Bluebird) {
2 | require("./OperationalError.js")(Bluebird);
3 | class AggregateError extends Bluebird.OperationalError {
4 | constructor(message) {
5 | super(message);
6 | this.length = 0;
7 | }
8 | _add(err) {
9 | this[this.length++] = err;
10 | }
11 | forEach(fn) {
12 | for(var i = 0; i < this.length; i++) {
13 | fn(this[i], i, this);
14 | }
15 | }
16 | };
17 | Bluebird.AggregateError = AggregateError;
18 | };
--------------------------------------------------------------------------------
/promiseFns/call.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.call = function call(methodName) {
3 | const passedArgs = Array.prototype.slice.call(arguments, 1);
4 | return Bluebird.resolve((async () => {
5 | const obj = await this;
6 | if (obj && obj[methodName])
7 | return obj[methodName].call(obj, ...passedArgs)
8 |
9 | throw new Error(`Method ${methodName} doesn't exist on obj ${JSON.stringify(obj)}` );
10 | })());
11 | };
12 |
13 | Bluebird.call = (o, ...args) => Bluebird.resolve(o).call(...args);
14 | };
15 |
--------------------------------------------------------------------------------
/promiseFns/filter.js:
--------------------------------------------------------------------------------
1 | const util = require("./util");
2 | module.exports = (Bluebird) => {
3 | Bluebird.filter = (x, predicate, opts) => Bluebird.resolve(x).filter(predicate, opts);
4 | Bluebird.prototype.filter = async function(predicate, {concurrency} = {}) {
5 | const values = await this.all();
6 | const predicateResults = await this.map(predicate, {concurrency});
7 | const output = [];
8 | for(let i = 0; i < predicateResults.length; i++) {
9 | if(!predicateResults[i]) continue;
10 | output.push(values[i]);
11 | }
12 | return output;
13 | };
14 | };
15 |
16 |
--------------------------------------------------------------------------------
/promiseFns/props.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.props = function props() {
3 | return Bluebird.resolve((async () => {
4 | const ret = {}, value = await this;
5 | if(typeof value !== "object" || value == null) {
6 | throw new TypeError("Expected an object passed to `.props`, got " + typeof value + " instead");
7 | }
8 | for(const key of Object.keys(value)) {
9 | ret[key] = await value[key];
10 | }
11 | return ret;
12 | })());
13 | };
14 | Bluebird.props = o => Bluebird.resolve(o).props();
15 | };
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible Node.js debug attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Debug current test file",
9 | "type": "node",
10 | "request": "launch",
11 | "cwd": "${workspaceRoot}",
12 | "runtimeExecutable": "mocha",
13 | "runtimeArgs": [
14 | "--debug-brk", "${file}"
15 | ],
16 | "port": 5858
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/promiseFns/get.js:
--------------------------------------------------------------------------------
1 | function indexedGetter(obj, index) {
2 | if (index < 0) index = Math.max(0, index + obj.length);
3 | return obj[index];
4 | }
5 |
6 | module.exports = (Bluebird) => {
7 | Bluebird.prototype.get = function get(prop) {
8 | return Bluebird.resolve((async () => {
9 | const value = await this;
10 | const isIndex = (typeof prop === "number");
11 | if (!isIndex) {
12 | return value[prop]
13 | } else {
14 | return indexedGetter(value, prop)
15 | }
16 | })());
17 | };
18 |
19 | Bluebird.get = (promise, prop) => Bluebird.resolve(promise).get(prop);
20 | };
21 |
--------------------------------------------------------------------------------
/promiseFns/mapSeries.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.mapSeries = function each(iterator) {
3 | return Bluebird.resolve((async () => {
4 | const promises = await this;
5 | const length = promises.length;
6 | let ret = Array(length);
7 |
8 | for(let index = 0; index <= promises.length-1; index++){
9 | const value = await iterator(await promises[index], index, length);
10 | ret = ret.concat(value);
11 | };
12 |
13 | return ret;
14 | })());
15 | };
16 |
17 | Bluebird.mapSeries = (promise, iterator) => Bluebird.resolve(promise).mapSeries(iterator);
18 | };
--------------------------------------------------------------------------------
/tests/aplus.js:
--------------------------------------------------------------------------------
1 | // promises/a+ suite.
2 | const promisesAplusTests = require("promises-aplus-tests");
3 | const Promise = require("../promise.js");
4 |
5 |
6 | // needed for aplus
7 | process.on("unhandledRejection", (e, v) => {});
8 |
9 | describe("Promises/A+ Tests", function () {
10 | require("promises-aplus-tests").mocha({
11 | resolved(v) { return Promise.resolve(v); },
12 | rejected(e) { return Promise.reject(e); },
13 | deferred() {
14 | let resolve, reject;
15 | let promise = new Promise((res, rej) => {
16 | resolve = res;
17 | reject = rej;
18 | });
19 | return {
20 | promise,
21 | resolve,
22 | reject
23 | };
24 | }
25 | });
26 | });
--------------------------------------------------------------------------------
/tests/OperationalError.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("OperationalError", () => {
5 |
6 | it("is on the promise object", () => {
7 | assert(Promise.OperationalError);
8 | });
9 |
10 | it("is an error OperationalError", () => {
11 | assert(new Promise.OperationalError() instanceof Error);
12 | });
13 |
14 | it("is operational", () => {
15 | assert((new Promise.OperationalError).isOperational);
16 | });
17 |
18 | it("has a stack property", () => {
19 | let err;
20 | try {
21 | throw new Promise.OperationalError;
22 | } catch (e) {
23 | err = e;
24 | }
25 | assert("stack" in err);
26 | });
27 |
28 | });
--------------------------------------------------------------------------------
/promiseFns/tapCatch.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.tapCatch = function(onResolved) {
3 | return tapCatch.call(this, ...arguments)
4 | };
5 |
6 | Bluebird.tapCatch = function(promise, onResolved) {
7 | return tapCatch.call(promise, ...arguments)
8 | };
9 | };
10 |
11 | function tapCatch(onResolved) {
12 | if (arguments.length > 1) {
13 | const filters = Array.prototype.slice.call(arguments, 0, -1);
14 | onResolved = arguments[arguments.length - 1];
15 | return this.catch(...filters, tapCatchCallback(onResolved));
16 | }
17 | return this.catch(tapCatchCallback(onResolved));
18 | };
19 |
20 | const tapCatchCallback = (onResolved) =>
21 | async err => {
22 | await onResolved(err);
23 | throw err;
24 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | #IntelliJ
40 | .idea
41 |
42 | # Mac OSX
43 | .DS_Store
44 |
45 | yarn.lock
--------------------------------------------------------------------------------
/promiseFns/then.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird, logger) => {
2 | function warningThen(onFulfilled, onRejected) {
3 | if(!logger.active){
4 | return super.then(onFulfilled, onRejected);
5 | }
6 | if(typeof onFulfilled !== "function" && onFulfilled !== null) { // explicit `then(null, handler)` case
7 | try { throw new Error(); } catch (e) { // get stack
8 | console.warn(" Warning: .then's onFulfilled only accepts functions, got ", onFulfilled, e);
9 | }
10 | }
11 | if(typeof onRejected !== "function") {
12 | try { throw new Error(); } catch (e) { // get stack
13 | console.warn(" Warning: .then's onRejected only accepts functions, got ", onRejected, e);
14 | }
15 | }
16 | return super.then(onFulfilled, onRejected);
17 | }
18 | return warningThen;
19 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bluebird-api",
3 | "version": "1.0.4",
4 | "description": "Bluebird compatible API for native promise subclass",
5 | "main": "promise.js",
6 | "directories": {
7 | "test": "tests"
8 | },
9 | "scripts": {
10 | "test": "mocha tests",
11 | "test-debug": "mocha --debug-brk tests",
12 | "build": "webpack"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/benjamingr/bluebird-api.git"
17 | },
18 | "keywords": [
19 | "promise",
20 | "bluebird"
21 | ],
22 | "author": "",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/benjamingr/bluebird-api/issues"
26 | },
27 | "homepage": "https://github.com/benjamingr/bluebird-api#readme",
28 | "devDependencies": {
29 | "assert": "^1.4.1",
30 | "mocha": "^3.2.0",
31 | "promises-aplus-tests": "^2.1.2",
32 | "webpack": "^2.5.1"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/promiseFns/reduce.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.reduce = function reduce(reducer, initialValue) {
3 | return Bluebird.resolve((async () => {
4 | const promises = await this;
5 | const length = promises.length;
6 | if (length === 0) return await initialValue;
7 |
8 | let ret;
9 | if (initialValue == undefined){
10 | let first = promises.shift();
11 | ret = await first;
12 | }else{
13 | ret = await initialValue;
14 | }
15 |
16 | for(let index = 0; index <= promises.length-1; index++){
17 | const value = await promises[index];
18 | ret = await reducer(ret, value, index, length);
19 | };
20 |
21 | return ret;
22 | })());
23 | };
24 |
25 | Bluebird.reduce = (promise, reducer, initialValue) => Bluebird.resolve(promise).reduce(reducer, initialValue);
26 | };
--------------------------------------------------------------------------------
/promiseFns/timeout.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.timeout = function (ms, rejectDesc) {
3 |
4 | return Bluebird.resolve((async () => {
5 | const PLACE_HOLDER = [];
6 |
7 | if (typeof rejectDesc === 'undefined') {
8 | rejectDesc = PLACE_HOLDER;
9 | }
10 |
11 |
12 | var winner = await Bluebird.race([this, Bluebird.delay(ms, rejectDesc)]);
13 |
14 | if (winner === rejectDesc) {
15 | if (rejectDesc instanceof Error)
16 | throw rejectDesc;
17 | else if (winner === PLACE_HOLDER)
18 | throw new Bluebird.TimeoutError();
19 | throw new Bluebird.TimeoutError(rejectDesc);
20 | }
21 |
22 | return winner;
23 | })());
24 | }
25 | Bluebird.timeout = (ms, rejectDesc, o) => Bluebird.resolve(o).delay(ms)
26 | }
27 |
--------------------------------------------------------------------------------
/docs/index.js:
--------------------------------------------------------------------------------
1 | const apiUrls = [
2 | 'https://api.github.com/users/',
3 | 'http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&q=',
4 | ];
5 |
6 | document.addEventListener("DOMContentLoaded", () => {
7 |
8 | const searchInput = document.getElementById('search');
9 | const searchBtn = document.getElementById('goForIt');
10 |
11 | searchBtn.onclick = (event) => {
12 | event.preventDefault();
13 |
14 | const {superagent} = window;
15 | const value = searchInput.value;
16 |
17 | const promiserAgent = Bluebird.promisify(superagent.get);
18 |
19 | Bluebird.map(apiUrls,
20 | (url) => promiserAgent(url + value)
21 | .tap(resp => console.log(url + ' is ready'))
22 | )
23 | // todo change to map when pr fixing bug in map will be merged
24 | .then(responses => responses.map(response => ({
25 | statusCode: response.statusCode,
26 | body: response.body,
27 | })))
28 | .then(responses => console.log('responses', responses))
29 | .catch(err => console.error('err', err))
30 | };
31 | });
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Benjamin Gruenbaum
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/promiseFns/reflect.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.prototype.reflect = async function() {
3 | try {
4 | const val = await this;
5 | return {
6 | isPending() { return false },
7 | reason() { throw new Error(); },
8 | value() { return val; },
9 | isFulfilled() { return true; },
10 | isCancelled() { return false; }, // back compat
11 | isRejected() { return false; }
12 | };
13 | } catch (e) {
14 | return {
15 | isPending() { return false },
16 | value() { throw new Error("Tried to get the value of a rejected promise"); },
17 | reason() { return e; },
18 | isFulfilled() { return false; },
19 | isCancelled() { return false; }, // back compat
20 | isRejected() { return true; }
21 | };
22 | }
23 | };
24 | // backwards compat Bluebird 2 method that works in Bluebird 3
25 | Bluebird.prototype.settle = arr => arr.map(Bluebird.resolve).map(x => x.reflect());
26 | };
--------------------------------------------------------------------------------
/tests/getNewLibraryCopy.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Bluebird = require("../promise.js");
3 | describe("getNewLibraryCopy", () => {
4 | it("is a different library instance", () => {
5 | const b = Bluebird.getNewLibraryCopy();
6 | assert.notEqual(b, Bluebird);
7 | assert((new b(() => {})) instanceof Promise);
8 | assert(!(b instanceof Bluebird));
9 | });
10 | it("called twice, gets you two different library instances", () => {
11 | const b = Bluebird.getNewLibraryCopy();
12 | const c = Bluebird.getNewLibraryCopy();
13 | assert.notEqual(b, c);
14 | assert.notEqual(b, Bluebird);
15 | assert((new b(() => {})) instanceof Promise);
16 | assert((new c(() => {})) instanceof Promise);
17 | assert(!((new c(() => {})) instanceof b));
18 | });
19 | it("does not copy new properties if forked from one with more properties", () => {
20 | const b = Bluebird.getNewLibraryCopy();
21 | b.foo = 15;
22 | const c = b.getNewLibraryCopy();
23 | assert.notEqual(c.foo, 15);
24 | assert(!("foo" in c));
25 | });
26 | });
--------------------------------------------------------------------------------
/tests/finally.js:
--------------------------------------------------------------------------------
1 |
2 | const assert = require("assert");
3 | const Promise = require("../promise.js");
4 |
5 | describe("finally", () => {
6 | it("runs on fulfillment", async () => {
7 | let run = false;
8 | await Promise.resolve().finally(() => run = true);
9 | assert(run);
10 | });
11 |
12 | it("runs on rejection", async () => {
13 | let run = false;
14 | try {
15 | await Promise.reject(Error('test rejection')).finally(() => run = true);
16 | }
17 | catch (e) {
18 | assert(run);
19 | }
20 | });
21 |
22 | it("doesn't change return vlaue", async () => {
23 | const value = await Promise.resolve(2).finally(() => 3);
24 | assert.equal(value, 2);
25 | });
26 | it("runs async ", async () => {
27 | let sync = true;
28 | Promise.resolve().finally(() => sync = false);
29 | assert(sync);
30 | });
31 | it("overried rejections", async () => {
32 | let sync = true;
33 | try {
34 | await Promise.reject(2).finally(() => { throw 3});
35 | assert.fail();
36 | } catch (e) {
37 | assert.equal(e, 3);
38 | }
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/tests/helpers/reasons.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // This module exports some valid rejection reason factories, keyed by human-readable versions of their names.
4 |
5 | var adapter = global.adapter;
6 | var fulfilled = adapter.fulfilled;
7 | var rejected = adapter.rejected;
8 |
9 | var dummy = { dummy: "dummy" };
10 |
11 | exports["`undefined`"] = function () {
12 | return undefined;
13 | };
14 |
15 | exports["`null`"] = function () {
16 | return null;
17 | };
18 |
19 | exports["`false`"] = function () {
20 | return false;
21 | };
22 |
23 | exports["`0`"] = function () {
24 | return 0;
25 | };
26 |
27 | exports["an error"] = function () {
28 | return new Error();
29 | };
30 |
31 | exports["an error without a stack"] = function () {
32 | var error = new Error();
33 | delete error.stack;
34 |
35 | return error;
36 | };
37 |
38 | exports["a date"] = function () {
39 | return new Date();
40 | };
41 |
42 | exports["an object"] = function () {
43 | return {};
44 | };
45 |
46 | exports["an always-pending thenable"] = function () {
47 | return { then: function () { } };
48 | };
49 |
50 | exports["a fulfilled promise"] = function () {
51 | return fulfilled(dummy);
52 | };
53 |
54 | exports["a rejected promise"] = function () {
55 | return rejected(dummy);
56 | };
57 |
--------------------------------------------------------------------------------
/tests/AggregateError.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("AggregateError", () => {
5 | it("is on the promise object", () => {
6 | assert(Promise.AggregateError);
7 | });
8 |
9 | it("is an error subclass", () => {
10 | assert(new Promise.AggregateError() instanceof Error);
11 | });
12 | it("is operational", () => {
13 | assert((new Promise.AggregateError()).isOperational);
14 | });
15 | it("has a forEach property", () => {
16 | assert("forEach" in (new Promise.AggregateError));
17 | });
18 | it("has a length property", () => {
19 | assert("length" in (new Promise.AggregateError));
20 | });
21 | it("has a stack property", () => {
22 | let err;
23 | try {
24 | throw (new Promise.AggregateError);
25 | } catch (e) {
26 | err = e;
27 | }
28 | assert("stack" in err);
29 | });
30 | it("iterates errors with forEach", () => {
31 | const err = new Promise.AggregateError();
32 | err._add(new Error("hello"));
33 | let count = 0;
34 | err.forEach(e => {
35 | count++;
36 | assert.equal(e.message, "hello");
37 | });
38 | assert.equal(count, 1);
39 | });
40 | })
--------------------------------------------------------------------------------
/promiseFns/setScheduler.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 |
3 | // this is tricky because we need to override how `then` works.
4 | // note that Zalgo is by-design impossible.
5 | // setScheduler will defer using the native microtask scheduler and
6 | // only then delay with the provided scheduler. This makes it still work
7 | // for things like notifying change detection - but you can never be quicker
8 | // than the built in scheduler.
9 |
10 | // it is important that we get a reference to then before it might be externally
11 | // modified
12 | const originalThen = Symbol();
13 | Bluebird.prototype[originalThen] = Bluebird.prototype.then;
14 | Bluebird.setScheduler = function(scheduler) {
15 | if(typeof scheduler !== "function") {
16 | throw new TypeError("Passed non function to setScheduler");
17 | }
18 | Bluebird.prototype.then = function then(onFulfill, onReject) {
19 | return this[originalThen](
20 | v => new Promise(scheduler),
21 | e => new Promise((_, r) => scheduler(r))
22 | )[originalThen](onFulfill, onReject);
23 | };
24 | };
25 | // hook for tests
26 | Bluebird.setScheduler.disable = () =>
27 | Bluebird.prototype.then = Bluebird.prototype[originalThen];
28 | };
--------------------------------------------------------------------------------
/promiseFns/some.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.some = (prom, n) => Bluebird.resolve(prom).some(n);
3 | Bluebird.prototype.some = function(n) {
4 | return Bluebird.resolve((async () => {
5 | let count = 0;
6 | const promises = await this;
7 | if(promises.length === 0) {
8 | throw new TypeError("Not enough promises passed to call waiting for some promises")
9 | }
10 | const results = Array(n);
11 | let currentResult = 0;
12 | const err = new Bluebird.AggregateError("Not enough promises resolved in some");
13 | return new Bluebird((resolve, reject) => {
14 | for(const promise of promises) {
15 | promise.then(v => {
16 | //console.log("resolved");
17 | count++;
18 | results[currentResult++] = v;
19 | if(count >= n) {
20 | resolve(results);
21 | }
22 | }, e => {
23 | err._add(e);
24 | //console.log("rejected", e, err.length, promises.length, n);
25 | if(err.length > promises.length - n) {
26 | reject(err);
27 | }
28 | });
29 | }
30 | })
31 |
32 | })());
33 | };
34 | };
--------------------------------------------------------------------------------
/tests/asCallback.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const assert = require("assert");
4 | const testUtils = require("./helpers/util.js");
5 |
6 | const Promise = require("../promise.js");
7 |
8 | describe("asCallback", () => {
9 | it("converts to a callback form", done => {
10 | Promise.resolve(2).asCallback((err, data) => {
11 | assert.equal(data, 2);
12 | done();
13 | });
14 | });
15 |
16 | it("converts to a errback form", done => {
17 | Promise.reject(2).asCallback((err, data) => {
18 | assert.equal(err, 2);
19 | done();
20 | });
21 | });
22 | it("passes null as error when resolved", done => {
23 | Promise.resolve(2).asCallback((err, data) => {
24 | assert.strictEqual(err, null);
25 | done();
26 | });
27 | });
28 | it("spreads when spread option is passed", done => {
29 | Promise.resolve([1,2]).asCallback((err, one, two) => {
30 | assert.strictEqual(err, null);
31 | assert.equal(one, 1);
32 | assert.equal(two, 2);
33 | done();
34 | }, {spread: true});
35 | });
36 | it("doesn't spread when spread option is passed false", done => {
37 | Promise.resolve([1,2]).asCallback((err, [one, two]) => {
38 | assert.strictEqual(err, null);
39 | assert.equal(one, 1);
40 | assert.equal(two, 2);
41 | done();
42 | }, {spread: false});
43 |
44 | });
45 | });
--------------------------------------------------------------------------------
/tests/suppressUnhandledRejections.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | const Promise = require("../promise.js");
4 | const assert = require("assert");
5 | const waitEnough = cb => setTimeout(cb, 5);
6 |
7 | describe("suppressunhandledrejections", () => {
8 | const listener = e => assert.fail(e);
9 |
10 | before(() => {
11 | process.on("unhandledRejection", listener);
12 | });
13 |
14 | after(() => {
15 | process.removeListener("unhandledRejection", listener);
16 | });
17 |
18 | it("suppresses rejections", done => {
19 | Promise.reject().suppressUnhandledRejections();
20 | waitEnough(done);
21 | });
22 |
23 | it("returns a promise", () => {
24 | const p = Promise.reject().suppressUnhandledRejections();
25 | assert(p instanceof Promise);
26 | });
27 |
28 | it("returns the same promise", () => {
29 | const p = Promise.reject();
30 | const p2 = p.suppressUnhandledRejections();
31 | assert.strictEqual(p, p2);
32 | });
33 |
34 | it("suppresses rejections if multiple calls", done => {
35 | const p = Promise.reject();
36 | p.suppressUnhandledRejections();
37 | p.suppressUnhandledRejections();
38 | waitEnough(done);
39 | });
40 |
41 | it("is still able to catch the rejection", done => {
42 | const p = Promise.reject(2);
43 | let v = 1;
44 | p.suppressUnhandledRejections().catch(v2 => v = v2);
45 | waitEnough(() => {
46 | assert.equal(v, 2);
47 | done();
48 | });
49 | });
50 | });
--------------------------------------------------------------------------------
/promiseFns/onPossiblyUnhandledRejection.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | Bluebird.onPossiblyUnhandledRejection = fn => {
3 | if(typeof process !== "undefined" && typeof process.on === "function") {
4 | process.on("unhandledRejection", (e, p) => {
5 | if(p instanceof Bluebird) {
6 | fn(e, p);
7 | } else {
8 | if(process.listenerCount("unhandledRejection") > 1) {
9 | // existing listener
10 | return false;
11 | } else {
12 | throw e; // default to throwing per Node's policy
13 | }
14 | }
15 | });
16 | } else if (typeof window !== "undefined" &&
17 | typeof window.addEventListener === "function") {
18 | window.addEventListener("unhandledrejection", e => {
19 | if(p instanceof Bluebird) {
20 | fn(event.reason, event.promise);
21 | }
22 | // default to throwing, this is the best we can do since we don't
23 | // want to throw on other unrelated promises.
24 | });
25 | } else {
26 | throw new Error("Could not set unhandled rejection handler in this environment \
27 | because native handlers do not exist.");
28 | }
29 | };
30 | Bluebird.onUnhandledRejectionHandled = () => {
31 | throw new Error("No one ever uses this API, if you do - let us know and I'll port it")
32 | };
33 | }
--------------------------------------------------------------------------------
/promiseFns/util.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | throttle,
3 | isPromise,
4 | isObject,
5 | classString
6 | };
7 |
8 | // slow implementation to start with. Should be "fast enough" for small concurrency values < 100;
9 | function throttle(fn, concurrency = 20, Promise) {
10 | const workers = Array(concurrency).fill(Promise.resolve());
11 | const work = [];
12 | return function (...args) {
13 | let worker = workers.pop();
14 | if (worker === undefined) {
15 | let resolve, reject;
16 | const promise = new Promise((res, rej) => {
17 | resolve = res;
18 | reject = rej;
19 | });
20 | work.unshift({ args, resolve, reject, promise });
21 | return promise;
22 | }
23 | worker = worker.then(() => (fn(...args), null));
24 | worker.then(function pump() {
25 | if (work.length) {
26 | const {resolve, reject, args} = work.pop();
27 | worker = worker.then(() => fn(...args)).then(resolve, reject).then(pump);
28 | } else {
29 | workers.push(worker);
30 | }
31 | return null;
32 | });
33 | return worker;
34 | }
35 | }
36 |
37 | function isPromise(obj) {
38 | return obj && obj.then && (typeof(obj.then) === 'function');
39 | }
40 |
41 |
42 | function classString(obj) {
43 | return {}.toString.call(obj);
44 | }
45 |
46 | function isObject(value) {
47 | return typeof value === "function" ||
48 | typeof value === "object" && value !== null;
49 | }
50 |
--------------------------------------------------------------------------------
/tests/any.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("any", () => {
5 | it("rejects no promises passed", async () => {
6 | try {
7 | await Promise.any([]);
8 | assert.fail();
9 | } catch(e) {
10 | assert(e instanceof Promise.TypeError);
11 | }
12 | });
13 | it("rejects one rejected promise passed", async () => {
14 | try {
15 | await Promise.any([Promise.reject(2)]);
16 | assert.fail();
17 | } catch(errs) {
18 | errs.forEach(e => assert.equal(e, 2))
19 | }
20 | });
21 |
22 | it("rejects two rejected promises passed", async () => {
23 | try {
24 | await Promise.any([Promise.reject(0), Promise.reject(1)]);
25 | assert.fail();
26 | } catch(errs) {
27 | errs.forEach((e, i) => assert.equal(e, i))
28 | }
29 | });
30 |
31 |
32 | it("resolves with two rejections and a resolve", async () => {
33 | const v = await Promise.any([
34 | Promise.reject(0),
35 | Promise.reject(1),
36 | Promise.resolve(2)
37 | ]);
38 | assert.equal(v, 2);
39 | });
40 |
41 |
42 | it("resolves with two rejections and a resolve ordered", async () => {
43 | const v = await Promise.any([
44 | Promise.resolve(2),
45 | Promise.reject(0),
46 | Promise.reject(1),
47 | ]);
48 | assert.equal(v, 2);
49 | });
50 |
51 | it("resolves with async resolution", async () => {
52 | const v = await Promise.any([
53 | new Promise(r => setTimeout(() => r(2), 5)),
54 | Promise.reject(0),
55 | Promise.reject(1),
56 | ]);
57 | assert.equal(v, 2);
58 | });
59 | });
--------------------------------------------------------------------------------
/tests/helpers/testThreeCases.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var adapter = global.adapter;
4 | var fulfilled = adapter.fulfilled;
5 | var rejected = adapter.rejected;
6 | var pending = adapter.pending;
7 |
8 | function success(done) {
9 | return function() {
10 | done();
11 | };
12 | }
13 |
14 | function fail(done) {
15 | return function(err) {
16 | done(err);
17 | };
18 | }
19 |
20 | function handlePromise(val, done) {
21 | if (val && typeof val.then === "function") {
22 | val.then(success(done), fail(done));
23 | }
24 | }
25 |
26 | exports.testFulfilled = function (value, test) {
27 | specify("already-fulfilled", function (done) {
28 | handlePromise(test(fulfilled(value), done), done);
29 | });
30 |
31 | specify("immediately-fulfilled", function (done) {
32 | var tuple = pending();
33 | handlePromise(test(tuple.promise, done), done);
34 | tuple.fulfill(value);
35 | });
36 |
37 | specify("eventually-fulfilled", function (done) {
38 | var tuple = pending();
39 | handlePromise(test(tuple.promise, done), done);
40 | setTimeout(function () {
41 | tuple.fulfill(value);
42 | }, 1);
43 | });
44 | };
45 |
46 | exports.testRejected = function (reason, test) {
47 | specify("already-rejected", function (done) {
48 | handlePromise(test(rejected(reason), done), done);
49 | });
50 |
51 | specify("immediately-rejected", function (done) {
52 | var tuple = pending();
53 | handlePromise(test(tuple.promise, done), done);
54 | tuple.reject(reason);
55 | });
56 |
57 | specify("eventually-rejected", function (done) {
58 | var tuple = pending();
59 | handlePromise(test(tuple.promise, done), done);
60 | setTimeout(function () {
61 | tuple.reject(reason);
62 | }, 1);
63 | });
64 | };
65 |
--------------------------------------------------------------------------------
/tests/try.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("try", () => {
5 | it("try works", async () => {
6 | let x = 1;
7 | await Promise.try(() => {
8 | x = 2;
9 | });
10 | assert.equal(x, 2);
11 | });
12 | it("try works with rejections", async () => {
13 | try {
14 | await Promise.try(() => {
15 | throw new Error();
16 | });
17 | assert.fail();
18 | } catch (e) { }
19 | });
20 | });
21 |
22 | const specify = it;
23 |
24 | describe("try builtin tests", () => {
25 | const tryy = Promise.try;
26 | const error = new Error();
27 |
28 | const thrower = function() { throw error; };
29 | var identity = x => x;
30 | var array = Array.from.bind(Array);
31 |
32 | var receiver = function() { return this; };
33 |
34 | specify("should reject when the function throws", function() {
35 | var async = false;
36 | var ret = tryy(thrower).then(assert.fail, function(e) {
37 | assert(async);
38 | assert(e === error);
39 | });
40 | async = true;
41 | return ret;
42 | });
43 |
44 | specify("should reject when the function is not a function", function() {
45 | var async = false;
46 | var ret = tryy(null).then(assert.fail, function(e) {
47 | assert(async);
48 | assert(e instanceof TypeError);
49 | });
50 | async = true;
51 | return ret;
52 | });
53 |
54 | specify("should unwrap returned thenable", function(){
55 | return tryy(function(){
56 | return {
57 | then: function(f, v) {
58 | f(3);
59 | }
60 | }
61 | }).then(function(v){
62 | assert(v === 3);
63 | });
64 |
65 | });
66 | })
--------------------------------------------------------------------------------
/tests/method.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("method", () => {
5 | it("wraps errors in promises", async () => {
6 | var p = Promise.method(() => {throw new Error("HEY");})();
7 | // gets here
8 | try {
9 | await p;
10 | } catch (e) {
11 | assert.equal(e.message, "HEY");
12 | }
13 | })
14 | });
15 |
16 | const specify = it;
17 | describe("method bb tests", () => {
18 | var obj = {};
19 | var error = new Error();
20 | var thrower = Promise.method(function() {
21 | throw error;
22 | });
23 |
24 | var identity = Promise.method(function(val) {
25 | return val;
26 | });
27 |
28 | var array = Promise.method(function() {
29 | return [].slice.call(arguments);
30 | });
31 |
32 | var receiver = Promise.method(function() {
33 | return this;
34 | });
35 |
36 | specify("should reject when the function throws", function() {
37 | var async = false;
38 | var ret = thrower().then(assert.fail, function(e) {
39 | assert(async);
40 | assert(e === error);
41 | });
42 | async = true;
43 | return ret;
44 | });
45 | specify("should throw when the function is not a function", async function() {
46 | try {
47 | Promise.method(null);
48 | assert.fail();
49 | }
50 | catch (e) {
51 | assert(e instanceof TypeError);
52 | return;
53 | }
54 | });
55 | specify("should call the function with the given receiver", function(){
56 | var async = false;
57 | var obj = {};
58 | var ret = receiver.call(obj).then(function(val) {
59 | assert(async);
60 | assert(val === obj);
61 | }, assert.fail);
62 | async = true;
63 | return ret;
64 | });
65 | });
--------------------------------------------------------------------------------
/tests/defer.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const assert = require("assert");
4 | const testUtils = require("./helpers/util.js");
5 | const Promise = require("../promise.js");
6 |
7 | describe("defer back-compat", () => {
8 | it("exists", () => {
9 | const d = Promise.defer();
10 | });
11 | it("has a promise property", () => {
12 | const defer = Promise.defer();
13 | assert(defer.promise);
14 | assert(defer.promise instanceof Promise);
15 | });
16 | it("has resolve/reject props", () => {
17 | const defer = Promise.defer();
18 | assert(defer.resolve);
19 | assert(defer.reject);
20 | assert(defer.resolve.call);
21 | });
22 | it("resolves with the value", async () => {
23 | const defer = Promise.defer();
24 | defer.resolve(3);
25 | assert.equal(await defer.promise, 3);
26 | });
27 | it("resolves with the first value", async () => {
28 | const defer = Promise.defer();
29 | defer.resolve(3);
30 | defer.resolve(2);
31 | assert.equal(await defer.promise, 3);
32 | });
33 | it("resolves with the first value when rejected", async () => {
34 | const defer = Promise.defer();
35 | defer.resolve(3);
36 | defer.reject(2);
37 | assert.equal(await defer.promise, 3);
38 | });
39 | it("rejects with the err", async () => {
40 | const defer = Promise.defer();
41 | defer.reject(2);
42 | try {
43 | const p = await defer.promise;
44 | assert.fail();
45 | } catch(e) {
46 | assert.equal(e, 2);
47 | }
48 | });
49 | it("rejects with the first err", async () => {
50 | const defer = Promise.defer();
51 | defer.reject(2);
52 | defer.reject(3);
53 | try {
54 | const p = await defer.promise;
55 | assert.fail();
56 | } catch(e) {
57 | assert.equal(e, 2);
58 | }
59 | });
60 | });
--------------------------------------------------------------------------------
/tests/reflect.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("reflect", () => {
5 | it("does not reject on fullfillment", async () => {
6 | await Promise.resolve().reflect();
7 | });
8 | it("does not reject on rejection", async () => {
9 | await Promise.reject().reflect();
10 | });
11 | it("has a value on fullfillment", async () => {
12 | const obj = await Promise.resolve("Zirak loves jQuery").reflect();
13 | const value = obj.value();
14 | assert.equal(value, "Zirak loves jQuery");
15 | });
16 | it("is fulfilled on fullfillment", async () => {
17 | const obj = await Promise.resolve("Zirak loves jQuery").reflect();
18 | assert(obj.isFulfilled());
19 | const value = obj.value();
20 | assert.equal(value, "Zirak loves jQuery");
21 | });
22 | it("is not rejected on fullfillment", async () => {
23 | const obj = await Promise.resolve("Zirak loves jQuery").reflect();
24 | assert(!obj.isRejected());
25 | const value = obj.value();
26 | assert.equal(value, "Zirak loves jQuery");
27 | });
28 | it("is rejected on rejections", async () => {
29 | const obj = await Promise.reject("Zirak loves jQuery").reflect();
30 | assert(obj.isRejected());
31 | assert(!obj.isFulfilled());
32 | const value = obj.reason();
33 | assert.equal(value, "Zirak loves jQuery");
34 | });
35 | });
36 |
37 |
38 | describe("original Bluebird tests", function() {
39 | it("reflects", () => {
40 | return Promise.resolve(1).reflect().then(function(inspection) {
41 | //assert(inspection instanceof Promise.PromiseInspection);
42 | assert(inspection.isFulfilled());
43 | assert(inspection.value() === 1);
44 | });
45 | });
46 | it("reflects error", () => {
47 | return Promise.reject(1).reflect().then(function(inspection) {
48 | //assert(inspection instanceof Promise.PromiseInspection);
49 | assert(inspection.isRejected());
50 | assert(inspection.reason() === 1);
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/tests/setScheduler.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const assert = require("assert");
4 | const Promise = require("../promise.js");
5 |
6 | describe("setScheduler", () => {
7 | it("sets the scheduler in a simple case", async () => {
8 | let x = false;
9 | Promise.setScheduler(fn => {
10 | x = true;
11 | setTimeout(fn)
12 | });
13 | await Promise.resolve().then(() => {});
14 | assert(x);
15 | });
16 | it("does not override the global scheduler", async () => {
17 | let x = false;
18 | Promise.setScheduler(fn => {
19 | x = true;
20 | setTimeout(fn)
21 | });
22 | await Promise.resolve();
23 | assert(!x);
24 | });
25 | it("never sets a synchronous scheduler", () => {
26 | let x = false;
27 | Promise.setScheduler(fn => fn());
28 | Promise.resolve().then(() => x = true);
29 | assert(!x);
30 | });
31 | it("is possible to set a scheduler twice", async () => {
32 | let x = 0;
33 | let y = 0;
34 | Promise.setScheduler(fn => {
35 | x++;
36 | setTimeout(fn)
37 | });
38 | await Promise.resolve().then(() => {});
39 | assert.equal(x, 1);
40 | Promise.setScheduler(fn => {
41 | y++;
42 | setTimeout(fn)
43 | });
44 | await Promise.resolve().then(() => {});
45 | assert.equal(x, 1);
46 | assert.equal(y, 1);
47 | });
48 | it("calls the scheduler twice in chained calls", async () => {
49 | let x = 0;
50 | Promise.setScheduler(fn => {
51 | x++;
52 | setTimeout(fn)
53 | });
54 | await Promise.resolve().then(() => {}).then(() => {})
55 | assert.equal(x, 2);
56 | });
57 | after(() => Promise.setScheduler.disable());
58 | });
59 |
60 | describe("bluebird schedule tests", () => {
61 | it("should throw for non function", function() {
62 | try {
63 | Promise.setScheduler({});
64 | } catch (e) {
65 | return;
66 | }
67 | assert.fail();
68 | });
69 | after(() => Promise.setScheduler.disable());
70 | })
--------------------------------------------------------------------------------
/promiseFns/catch.js:
--------------------------------------------------------------------------------
1 | module.exports = (Bluebird) => {
2 | // think: super.catch
3 | const super_catch = Promise.prototype.catch;
4 |
5 | Bluebird.prototype.catch = Bluebird.prototype.caught = function catchFn(fn) {
6 | if (arguments.length > 1) {
7 | const filters = Array.prototype.slice.call(arguments, 0, -1);
8 | fn = arguments[arguments.length - 1];
9 |
10 | return super_catch.call(this, filterCatch(filters, fn));
11 | }
12 | else {
13 | return super_catch.call(this, fn);
14 | }
15 | };
16 |
17 | function filterCatch(filters, fn) {
18 | return (error) => {
19 | for (const filter of filters) {
20 | if (testFilter(filter, error)) {
21 | return fn(error); //TODO: deal with Bluebird.bind() here?
22 | }
23 | }
24 |
25 | return Bluebird.reject(error);
26 | };
27 | }
28 | };
29 |
30 | const FILTER_CONFIGS = [
31 | { // Error contructor
32 | filterTest: (t, error) => t && (t === Error || t.prototype instanceof Error),
33 | predicate: (t, error) => error instanceof t
34 | },
35 | { // function
36 | filterTest: (t, error) => typeof t === 'function',
37 | predicate: (fn, error) => fn(error)
38 | },
39 | { // else: Object shallow compare w/
40 | // note: we test the thrown error, not the filter argument as in previous filterTest()s. This is what bluebird does.
41 | filterTest: (o, error) => typeof error === 'function' || (typeof error === 'object' && error !== null),
42 | // To match Bluebird's behavior, uses ==, not === intentionally
43 | predicate: (filter, error) => !Object.keys(filter).some(key => filter[key] != error[key])
44 | }
45 | ];
46 |
47 | function testFilter(filterArgument, error) {
48 | // get the right predicate function for the type of filter the user supplied.
49 | const filterConfig = FILTER_CONFIGS.find(filterConfig =>
50 | filterConfig.filterTest(filterArgument, error));
51 | const { predicate } = filterConfig || {};
52 |
53 | // If not filters are valid, we jist return false. It's not an exception
54 | return predicate && predicate(filterArgument, error);
55 | }
--------------------------------------------------------------------------------
/tests/call.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const assert = require("assert");
4 | const testUtils = require("./helpers/util.js");
5 |
6 | const Promise = require("../promise.js");
7 |
8 |
9 | var c = {
10 | val: 3,
11 | method: function() {
12 | return [].slice.call(arguments).concat(this.val);
13 | }
14 | };
15 |
16 | describe("call", function() {
17 | it("0 args", function() {
18 | return Promise.resolve(c).call("method").then(function(res) {
19 | assert.deepEqual([3], res);
20 | });
21 | });
22 | it("1 args", function() {
23 | return Promise.resolve(c).call("method", 1).then(function(res) {
24 | assert.deepEqual([1, 3], res);
25 | });
26 | });
27 | it("2 args", function() {
28 | return Promise.resolve(c).call("method", 1, 2).then(function(res) {
29 | assert.deepEqual([1, 2, 3], res);
30 | });
31 | });
32 | it("3 args", function() {
33 | return Promise.resolve(c).call("method", 1, 2, 3).then(function(res) {
34 | assert.deepEqual([1, 2, 3, 3], res);
35 | });
36 | });
37 | it("10 args", function() {
38 | return Promise.resolve(c).call("method", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10).then(function(res) {
39 | assert.deepEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3], res);
40 | });
41 | });
42 | it("10 args called statically", function() {
43 | return Promise.call(c, "method", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10).then(function(res) {
44 | assert.deepEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3], res);
45 | });
46 | });
47 | it("method not found", function() {
48 | var promises = [
49 | Promise.resolve([]).call("abc").then(assert.fail, testUtils.noop),
50 | Promise.resolve([]).call("abc", 1, 2, 3, 4, 5, 6, 7).then(assert.fail, testUtils.noop),
51 | Promise.resolve([]).call("abc ").then(assert.fail, testUtils.noop),
52 | Promise.resolve(null).call("abc", 1, 2, 3, 4, 5, 6, 7).then(assert.fail, testUtils.noop),
53 | Promise.resolve(null).call("abc").then(assert.fail, testUtils.noop),
54 | Promise.resolve(null).call("abc ").then(assert.fail, testUtils.noop)
55 | ];
56 |
57 | return Promise.all(promises).then(function(errors) {
58 | for (var i = 0; i < errors.length; ++i) {
59 | var message = errors[i].message || errors[i].toString();
60 | assert(message.indexOf("doesn't exist on obj") >= 0);
61 | }
62 | });
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/tests/tap.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("tap", () => {
5 | it("taps a promise", async function() {
6 | const p = Promise.resolve("hello");
7 | let val = "";
8 | const returnValue = await p.tap(inner => val = inner + " world");
9 | assert.equal(val, "hello world");
10 | assert.equal(returnValue, "hello");
11 | });
12 | });
13 |
14 | // bluebird tests
15 |
16 | const specify = it;
17 | describe("tap bluebird original tests", () => {
18 | specify("passes through value", function() {
19 | return Promise.resolve("test").tap(function() {
20 | return 3;
21 | }).then(function(value){
22 | assert.equal(value, "test");
23 | });
24 | });
25 |
26 | specify("passes through value after returned promise is fulfilled", function() {
27 | var async = false;
28 | return Promise.resolve("test").tap(function() {
29 | return new Promise(function(r) {
30 | setTimeout(function(){
31 | async = true;
32 | r(3);
33 | }, 1);
34 | });
35 | }).then(function(value){
36 | assert(async);
37 | assert.equal(value, "test");
38 | });
39 | });
40 |
41 | specify("is not called on rejected promise", function() {
42 | var called = false;
43 | return Promise.reject("test").tap(function() {
44 | called = true;
45 | }).then(assert.fail, function(value){
46 | assert(!called);
47 | });
48 | });
49 |
50 | specify("passes immediate rejection", function() {
51 | var err = new Error();
52 | return Promise.resolve("test").tap(function() {
53 | throw err;
54 | }).tap(assert.fail).then(assert.fail, function(e){
55 | assert(err === e);
56 | });
57 | });
58 |
59 | specify("passes eventual rejection", function() {
60 | var err = new Error();
61 | return Promise.resolve("test").tap(function() {
62 | return new Promise(function(_, rej) {
63 | setTimeout(function(){
64 | rej(err);
65 | }, 1)
66 | });
67 | }).tap(assert.fail).then(assert.fail, function(e) {
68 | assert(err === e);
69 | });
70 | });
71 |
72 | specify("passes value", function() {
73 | return Promise.resolve(123).tap(function(a) {
74 | assert(a === 123);
75 | });
76 | });
77 | })
--------------------------------------------------------------------------------
/tests/get.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 | var join = Promise.join;
4 |
5 | describe("indexed getter", function() {
6 | var p = Promise.resolve([0, 1, 2, 3, 4, 5, 7, 5,10]);
7 | specify("gets positive index", function() {
8 | var first = p.get(0);
9 | var fourth = p.get(3);
10 | var last = p.get(8);
11 |
12 | return join(first, fourth, last, function(a, b, c) {
13 | assert(a === 0);
14 | assert(b === 3);
15 | assert(c === 10);
16 | });
17 | });
18 |
19 | specify("gets negative index", function() {
20 | var last = p.get(-1);
21 | var first = p.get(-20);
22 |
23 | return join(last, first, function(a, b) {
24 | assert.equal(a, 10);
25 | assert.equal(b, 0);
26 | });
27 | });
28 | });
29 |
30 | describe("identifier getter", function() {
31 | var p = Promise.resolve(new RegExp("", ""));
32 | specify("gets property", function() {
33 | var ci = p.get("ignoreCase");
34 | var g = p.get("global");
35 | var lastIndex = p.get("lastIndex");
36 | var multiline = p.get("multiline");
37 |
38 | return join(ci, g, lastIndex, multiline, function(ci, g, lastIndex, multiline) {
39 | assert(ci === false);
40 | assert(g === false);
41 | assert(lastIndex === 0);
42 | assert(multiline === false);
43 | });
44 | });
45 |
46 | specify("gets same property", function() {
47 | var o = {o: 1};
48 | var o2 = {o: 2};
49 | o = Promise.resolve(o).get("o");
50 | o2 = Promise.resolve(o2).get("o");
51 | return join(o, o2, function(one, two) {
52 | assert.strictEqual(1, one);
53 | assert.strictEqual(2, two);
54 | });
55 | });
56 | });
57 |
58 | describe("non identifier getters", function() {
59 | var p = Promise.resolve({"-": "val"});
60 | specify("get property", function() {
61 | return p.get("-").then(function(val) {
62 | assert(val === "val");
63 | });
64 | });
65 |
66 | specify("overflow cache", function() {
67 | var a = new Array(1024);
68 | var o = {};
69 | for (var i = 0; i < a.length; ++i) {
70 | a[i] = "get" + i;
71 | o["get" + i] = i*2;
72 | }
73 | var b = Promise.map(a, function(item, index) {
74 | return Promise.resolve(o).get(a[index]);
75 | }).then(function(items) {
76 | return items.filter((value, index) => value === index * 2);
77 | }).then(function(values) {
78 | assert.strictEqual(values.length, a.length);
79 | });
80 | return b;
81 | });
82 | });
--------------------------------------------------------------------------------
/tests/some.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("some", () => {
5 | it("rejects no promises passed", async () => {
6 | try {
7 | await Promise.some([], 1);
8 | assert.fail();
9 | } catch(e) {
10 | assert(e instanceof Promise.TypeError);
11 | }
12 | });
13 | it("rejects one rejected promise passed", async () => {
14 | try {
15 | await Promise.some([Promise.reject(2)], 1);
16 | assert.fail();
17 | } catch(errs) {
18 | errs.forEach(e => assert.equal(e, 2))
19 | }
20 | });
21 |
22 | it("rejects two rejected promises passed", async () => {
23 | try {
24 | await Promise.some([Promise.reject(0), Promise.reject(1)], 1);
25 | assert.fail();
26 | } catch(errs) {
27 | errs.forEach((e, i) => assert.equal(e, i))
28 | }
29 | });
30 |
31 |
32 | it("resolves with two rejections and a resolve", async () => {
33 | const [v] = await Promise.some([
34 | Promise.reject(0),
35 | Promise.reject(1),
36 | Promise.resolve(2)
37 | ], 1);
38 | assert.equal(v, 2);
39 | });
40 |
41 |
42 | it("resolves with two rejections and a resolve ordered", async () => {
43 | const [v] = await Promise.some([
44 | Promise.resolve(2),
45 | Promise.reject(0),
46 | Promise.reject(1),
47 | ], 1);
48 | assert.equal(v, 2);
49 | });
50 |
51 | it("resolves with async resolution", async () => {
52 | const [v] = await Promise.some([
53 | new Promise(r => setTimeout(() => r(2), 5)),
54 | Promise.reject(0),
55 | Promise.reject(1),
56 | ], 1);
57 | assert.equal(v, 2);
58 | });
59 |
60 | it("resolves with two items", async () => {
61 | const [one, oneToo] = await Promise.some([
62 | Promise.resolve(1),
63 | Promise.resolve(1)
64 | ], 2);
65 | assert.equal(one, 1);
66 | assert.equal(oneToo, 1);
67 | });
68 | it("resolves in order", async () => {
69 | const [one, two] = await Promise.some([
70 | new Promise(r => setTimeout(() => r(2), 5)),
71 | Promise.resolve(1)
72 | ], 2);
73 | assert.equal(one, 1);
74 | assert.equal(two, 2);
75 | });
76 |
77 | it("rejects as soon as the goal cannot be reached", async () => {
78 | try {
79 | await Promise.some([
80 | Promise.reject(0),
81 | Promise.reject(1),
82 | new Promise(() => {})
83 | ], 2);
84 | assert.fail();
85 | } catch(errs) {
86 | errs.forEach((e, i) => assert.equal(e, i))
87 | }
88 | });
89 | });
--------------------------------------------------------------------------------
/tests/error.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const assert = require("assert");
4 | const Promise = require("../promise.js");
5 |
6 | describe(".error", () => {
7 |
8 | it("Does not catch primitives", async () => {
9 | try {
10 | await Promise.reject(4).error(e => {});
11 | assert.fail();
12 | } catch (e) {
13 | assert.equal(e, 4);
14 | }
15 | });
16 |
17 |
18 | it("does not catch regular `Error`s", async () => {
19 | try {
20 | await Promise.reject(new Error()).error(e => {});
21 | assert.fail();
22 | } catch (e) {
23 | assert(!e.isOperational);
24 | }
25 | });
26 |
27 | it("does not catch regular `SyntaxError`s", async () => {
28 | try {
29 | await Promise.reject(new SyntaxError()).error(e => {});
30 | assert.fail();
31 | } catch (e) {
32 | assert(!e.isOperational);
33 | }
34 | });
35 |
36 | it("catches Promise.OperationalError instances", async () => {
37 | await Promise.reject(new Promise.OperationalError()).error(e => {});
38 | });
39 |
40 | it("catches Errors with isOperational true", async () => {
41 | var e = new Error();
42 | e.isOperational = true;
43 | await Promise.reject(e).error(e => {});
44 | });
45 |
46 |
47 | it("propagates on rethrow", async () => {
48 | try {
49 | await Promise.reject(new Promise.OperationalError()).error(e => { throw e;});
50 | assert.fail();
51 | } catch (e) {
52 | assert(e.isOperational);
53 | }
54 | });
55 |
56 |
57 | it("conserves .message", async () => {
58 | try {
59 | await Promise.reject(new Promise.OperationalError("HI")).error(e => { throw e;});
60 | assert.fail();
61 | } catch (e) {
62 | assert.equal(e.message, "HI");
63 | }
64 | });
65 | });
66 |
67 | describe("Weird errors -- bb test", function() {
68 | it("unwritable stack", function() {
69 | var e = new Error();
70 | var stack = e.stack;
71 | Object.defineProperty(e, "stack", {
72 | configurable: true,
73 | get: function() {return stack;},
74 | set: function() {throw new Error("cannot set");}
75 | });
76 | return new Promise(function(_, reject) {
77 | setTimeout(function() {
78 | reject(e);
79 | }, 1);
80 | }).catch(function(err) {
81 | assert.equal(e, err);
82 | });
83 | });
84 | });
85 |
86 | describe("OperationalError -- bb test - divergence!", function() {
87 | it("should not work without new", function() {
88 | try {
89 | var a = Promise.OperationalError("msg");
90 | } catch (e) {}
91 | if(a) assert.fail();
92 | });
93 | it("should work with new", function() {
94 | var a = new Promise.OperationalError("msg");
95 | assert.strictEqual(a.message, "msg");
96 | assert(a instanceof Error);
97 | });
98 | });
--------------------------------------------------------------------------------
/tests/join.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("join", () => {
5 | it("joins a single value", async () => {
6 | let called = false;
7 | await Promise.join(1, (one) => {
8 | assert.equal(one, 1);
9 | called = true;
10 | });
11 | assert.equal(called, true);
12 | });
13 |
14 | it("joins two values", async () => {
15 | let called = false;
16 | await Promise.join(1, 2, (one, two) => {
17 | assert.equal(one, 1);
18 | assert.equal(two, 2);
19 | called = true;
20 | });
21 | assert.equal(called, true);
22 | });
23 |
24 | it("joins a single promise", async () => {
25 | let called = false;
26 | await Promise.join(Promise.resolve(1), one => {
27 | assert.equal(one, 1);
28 | called = true;
29 | });
30 | assert.equal(called, true);
31 | });
32 |
33 | it("joins two promises", async () => {
34 | let called = false;
35 | await Promise.join(Promise.resolve(1), Promise.resolve(2), (one, two) => {
36 | assert.equal(one, 1);
37 | assert.equal(two, 2);
38 | called = true;
39 | });
40 | assert.equal(called, true);
41 | });
42 |
43 | it("joins mixed value", async () => {
44 | let called = false;
45 | await Promise.join(Promise.resolve(1), 2, (one, two) => {
46 | assert.equal(one, 1);
47 | assert.equal(two, 2);
48 | called = true;
49 | });
50 | assert.equal(called, true);
51 | });
52 | });
53 |
54 | const specify = it;
55 |
56 | describe("bluebird builtin tests", async () => {
57 |
58 | specify("should call last argument as a spread function", function() {
59 | return Promise.join(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3), function(a, b, c) {
60 | assert(a === 1);
61 | assert(b === 2);
62 | assert(c === 3);
63 | });
64 | });
65 |
66 |
67 | specify("gh-227", function() {
68 | function a() {
69 | return Promise.join(Promise.resolve(1), function () {
70 | throw new Error();
71 | });
72 | }
73 |
74 | return a().then(assert.fail, function(e) {});
75 | });
76 |
77 | specify("should not pass the callback as argument, <5 arguments", function() {
78 | return Promise.join(1, 2, 3, function() {
79 | assert.strictEqual(arguments.length, 3);
80 | });
81 | });
82 |
83 | specify("should not pass the callback as argument >5 arguments", function() {
84 | return Promise.join(1, 2, 3, 4, 5, 6, 7, function() {
85 | assert.strictEqual(arguments.length, 7);
86 | });
87 | });
88 |
89 | specify("should ensure asynchronity", function() {
90 | var sync = false;
91 | Promise.join(Promise.resolve(1), Promise.resolve(2), function() {
92 | sync = true;
93 | });
94 | assert.strictEqual(false, sync);
95 | })
96 |
97 | });
--------------------------------------------------------------------------------
/tests/map.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("map", () => {
5 | it("maps sync stuff", async () => {
6 | var arr = await Promise.map([1,2,3], x => x + 1);
7 | assert.deepEqual(arr, [2,3,4]);
8 | });
9 |
10 | it("maps async stuff", async () => {
11 | var arr = await Promise.map([1,2,3], async x => x + 1);
12 | assert.deepEqual(arr, [2,3,4]);
13 | });
14 |
15 | it("maps async stuff with promise input", async () => {
16 | var arr = await Promise.map([1,2, Promise.resolve(3)], async x => x + 1);
17 | assert.deepEqual(arr, [2,3,4]);
18 | });
19 |
20 | it("maps async stuff with multiple promise input", async () => {
21 | var arr = await Promise.map([1,Promise.resolve(2), Promise.resolve(3)], async x => x + 1);
22 | assert.deepEqual(arr, [2,3,4]);
23 | });
24 |
25 | it("maps stuff that's sometimes async", async () => {
26 | var arr = await Promise.map([1,2, Promise.resolve(3)], (x, i) => {
27 | if(i%2) return Promise.resolve(x+1);
28 | return x + 1;
29 | });
30 | assert.deepEqual(arr, [2,3,4]);
31 | });
32 | });
33 |
34 | const specify = it;
35 |
36 | describe("bluebird's tests for map", () => {
37 |
38 | function mapper(val) {
39 | return val * 2;
40 | }
41 |
42 | async function deferredMapper(val) {
43 | await new Promise(r => setTimeout(r, 5));
44 | return mapper(val);
45 | }
46 |
47 | specify("should map input values array", function() {
48 | var input = [1, 2, 3];
49 | return Promise.map(input, mapper).then(
50 | function(results) {
51 | assert.deepEqual(results, [2,4,6]);
52 | },
53 | assert.fail
54 | );
55 | });
56 |
57 | specify("should map input promises array", function() {
58 | var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
59 | return Promise.map(input, mapper).then(
60 | function(results) {
61 | assert.deepEqual(results, [2,4,6]);
62 | },
63 | assert.fail
64 | );
65 | });
66 |
67 | specify("should map mixed input array", function() {
68 | var input = [1, Promise.resolve(2), 3];
69 | return Promise.map(input, mapper).then(
70 | function(results) {
71 | assert.deepEqual(results, [2,4,6]);
72 | },
73 | assert.fail
74 | );
75 | });
76 |
77 | specify("should map input when mapper returns a promise", function() {
78 | var input = [1,2,3];
79 | return Promise.map(input, deferredMapper).then(
80 | function(results) {
81 | assert.deepEqual(results, [2,4,6]);
82 | },
83 | assert.fail
84 | );
85 | });
86 |
87 | specify("should accept a promise for an array", function() {
88 | return Promise.map(Promise.resolve([1, Promise.resolve(2), 3]), mapper).then(
89 | function(result) {
90 | assert.deepEqual(result, [2,4,6]);
91 | },
92 | assert.fail
93 | );
94 | });
95 | });
--------------------------------------------------------------------------------
/promise.js:
--------------------------------------------------------------------------------
1 | function factory() {
2 | class Bluebird extends Promise { }
3 | Bluebird.TypeError = TypeError; // Bluebird.TypeError is used in tests
4 |
5 | require('./promiseErrors/OperationalError')(Bluebird);
6 | require('./promiseErrors/AggregateError')(Bluebird);
7 | require('./promiseErrors/TimeoutError')(Bluebird);
8 |
9 | require('./promiseFns/delay')(Bluebird);
10 | require('./promiseFns/timeout')(Bluebird);
11 | require('./promiseFns/tap')(Bluebird);
12 | require('./promiseFns/props')(Bluebird);
13 | require('./promiseFns/spread')(Bluebird);
14 | require('./promiseFns/promisify')(Bluebird);
15 | require('./promiseFns/join')(Bluebird);
16 | require('./promiseFns/try')(Bluebird);
17 | require('./promiseFns/method')(Bluebird);
18 | require('./promiseFns/each')(Bluebird);
19 | require('./promiseFns/mapSeries')(Bluebird);
20 | require('./promiseFns/map')(Bluebird);
21 | require('./promiseFns/filter')(Bluebird);
22 | require('./promiseFns/get')(Bluebird);
23 | require('./promiseFns/call')(Bluebird);
24 | require('./promiseFns/reflect')(Bluebird);
25 | require('./promiseFns/finally')(Bluebird);
26 | require('./promiseFns/return')(Bluebird);
27 | require('./promiseFns/reduce')(Bluebird);
28 | require('./promiseFns/throw')(Bluebird);
29 | require('./promiseFns/catchReturn')(Bluebird);
30 | require('./promiseFns/catchThrow')(Bluebird);
31 | require('./promiseFns/catch')(Bluebird);
32 | require('./promiseFns/error')(Bluebird);
33 | require('./promiseFns/coroutine')(Bluebird);
34 | require('./promiseFns/cast')(Bluebird);
35 | require('./promiseFns/asCallback')(Bluebird);
36 | require('./promiseFns/noConflict')(Bluebird);
37 | require('./promiseFns/defer')(Bluebird);
38 | require('./promiseFns/done')(Bluebird);
39 | require('./promiseFns/suppressUnhandledRejections')(Bluebird);
40 | require('./promiseFns/onPossiblyUnhandledRejection')(Bluebird);
41 | require('./promiseFns/some')(Bluebird);
42 | require('./promiseFns/any')(Bluebird);
43 | require('./promiseFns/disposer')(Bluebird);
44 | require('./promiseFns/using')(Bluebird);
45 | require('./promiseFns/tapCatch')(Bluebird);
46 |
47 | const logger = {log: console.warn, active: true};
48 | //const warningThen = require("./promiseFns/then")(Bluebird, logger);
49 |
50 | require('./promiseFns/setScheduler')(Bluebird);
51 | Bluebird.config = (obj) => {
52 | // if(!obj.warnings) {
53 | // logger.active = false;
54 | // } else {
55 | // logger.active = true;
56 | // }
57 | };
58 | Bluebird.longStackTraces = () => {}; // long stack traces by debugger and not bb
59 | Bluebird.hasLongStackTraces = () => false;
60 |
61 | // converted from async to traditional .then() since native async/await return native promises.
62 | Bluebird.prototype.all = function all() {
63 | return this.then(r => Bluebird.all(r));
64 | };
65 |
66 | return Bluebird;
67 | }
68 | const copy = factory();
69 | copy.getNewLibraryCopy = () => {
70 | const newCopy = factory();
71 | newCopy.getNewLibraryCopy = copy.getNewLibraryCopy;
72 | return newCopy;
73 | };
74 |
75 | module.exports = copy;
76 |
--------------------------------------------------------------------------------
/promiseFns/using.js:
--------------------------------------------------------------------------------
1 | const unsafe = require("./utils/nextTick.js");
2 |
3 | module.exports = (Bluebird) => {
4 | Bluebird.using = function(...disposersAndFn) {
5 | let fn, error;
6 | const results = Array(disposersAndFn.length - 1);
7 | return new Bluebird((resolve, reject) => {
8 |
9 | fn = disposersAndFn.pop();
10 | if(typeof fn !== "function") {
11 | throw new TypeError("Non function passed to using");
12 | }
13 | let remaining = disposersAndFn.length;
14 | const rejectCount = () => {
15 | remaining--;
16 | if(remaining === 0) reject(error);
17 | }
18 | let failed = false;
19 | for(let i = 0; i < disposersAndFn.length; i++) {
20 | const disposer = disposersAndFn[i];
21 | const handleErr = e => {
22 | failed = true;
23 | // one failed, clean up all the others.
24 | unsafe(async () => {
25 | for(const item of results) {
26 | if(!item) continue;
27 | if(!item.disposer) continue;
28 | await item.disposer._cleanup();
29 | }
30 | error = e;
31 | rejectCount(); // reject with the error
32 | });
33 | };
34 | if(disposer._use) {
35 | disposer._use.then(resource => {
36 | if(failed) {
37 | //todo: hold reject until these finish?
38 | unsafe(async () => {
39 | await disposer._cleanup();
40 | rejectCount();
41 | });
42 | return;
43 | }
44 | results[i] = {resource, disposer};
45 | if(--remaining === 0) {
46 | resolve(results.map(x => x.resource));
47 | }
48 | }, handleErr);
49 | } else if (disposer.then) {
50 | Bluebird.resolve(disposer).then(resource => {
51 | results[i] = {resource};
52 | if(--remaining === 0) {
53 | resolve(results.map(x => x.resource));
54 | }
55 | }, handleErr);
56 | }
57 | }
58 | })
59 | .then(res => fn(...res)) // run the actual function the user passed
60 | .finally(v => new Bluebird((resolve) => {
61 | // clean up and wait for it
62 | unsafe(async () => {
63 | for(const disposer of results) {
64 | if(!disposer) continue; // guard against edge case
65 | if(!disposer.disposer) continue; // promise and not disposer
66 | await disposer.disposer._cleanup();
67 | }
68 | resolve();
69 | });
70 | }));
71 | };
72 | // Bluebird.prototype.disposer = function disposer(fn) {
73 | // return {
74 | // _use: this,
75 | // _cleanup: fn,
76 | // then() {
77 | // throw new Error("A disposer is not a promise. Use it with `Promise.using`.")
78 | // }
79 | // };
80 | // };
81 | };
--------------------------------------------------------------------------------
/promiseFns/coroutine.js:
--------------------------------------------------------------------------------
1 | const util = require("./util");
2 |
3 | let yieldHandlers = [];
4 |
5 | async function runGenerator(generator, args, yieldHandlers, thisContext) {
6 | let iterator = generator.apply(thisContext, args);
7 |
8 | // Loop state
9 | let value = undefined
10 | let done = false;
11 | let errorMode = false;
12 | let error = undefined;
13 |
14 | do {
15 | // 1. Advance iterator to next 'step'
16 | ({ value, done } = nextStep({ iterator, value, error, errorMode }));
17 |
18 | // 2. Process the yielded value
19 | // Only if !done, since the returned values are not processed like yielded ones.
20 | if (!done) {
21 | ({ value, error, errorMode } = await processValue({ value, yieldHandlers }));
22 | }
23 | } while (!done);
24 |
25 | return value;
26 | }
27 |
28 | // perform the next 'step' in iterator based on the current state (e.g. errorMode)
29 | function nextStep({ iterator, value, error, errorMode }) {
30 | if (errorMode) {
31 | return iterator.throw(error);
32 | }
33 | else {
34 | return iterator.next(value);
35 | }
36 | }
37 |
38 | // process a yielded value, pass through yieldHandlers, record error if (value as Promise) rejects.
39 | async function processValue({ value, yieldHandlers }) {
40 | try {
41 | // run through all yieldHandlers
42 | for (const yieldHandler of yieldHandlers) {
43 | let handlerResult = yieldHandler(value);
44 |
45 | // only replace our value if the handler returned something
46 | // (it's supposed to be a promise, but I check that later)
47 | if (handlerResult) {
48 | value = handlerResult;
49 | break;
50 | }
51 | }
52 |
53 | if (!util.isPromise(value)) {
54 | throw new TypeError('OMG, Not a promise.');
55 | }
56 |
57 | value = await value;
58 | return {
59 | value,
60 | error: undefined,
61 | errorMode: false
62 | };
63 | }
64 | catch (ex) {
65 | return {
66 | value: undefined,
67 | error: ex,
68 | errorMode: true
69 | };
70 | }
71 | }
72 |
73 | module.exports = (Bluebird) => {
74 | Bluebird.coroutine = function(generator, { yieldHandler = null } = {}) {
75 |
76 | return function(...args) {
77 |
78 | // add the provded yieldHandler to the global onces, if provided.
79 | let allYieldHandlers;
80 | if (yieldHandler) {
81 | allYieldHandlers = [ yieldHandler, ...yieldHandlers];
82 | }
83 | else {
84 | allYieldHandlers = yieldHandlers;
85 | }
86 |
87 | let result = runGenerator(generator, args, allYieldHandlers, this);
88 |
89 | // native async/await returns a native promise,
90 | // so wrap in Promise.resolve so this returns a "fake bluebird" promise instance
91 | return Bluebird.resolve(result);
92 | }
93 | };
94 |
95 | Bluebird.coroutine.addYieldHandler = function(newYieldHandler) {
96 | if (!newYieldHandler || typeof(newYieldHandler) !== 'function') {
97 | throw new TypeError('yieldHandler must be a function.');
98 | }
99 |
100 | yieldHandlers.push(newYieldHandler);
101 | }
102 |
103 | Bluebird.spawn = function(generator) {
104 | return Bluebird.coroutine(generator)();
105 | };
106 | };
--------------------------------------------------------------------------------
/tests/using.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | function disposer() {
5 | const obj = {closed: false};
6 | const d = Promise.resolve().then(() => {
7 | return obj;
8 | }).disposer(() => {
9 | obj.closed = true;
10 | });
11 | d._obj = obj;
12 | return d;
13 | }
14 | function rejectedDisposer(v) {
15 | const obj = {closed: false};
16 | return Promise.reject(v).then(() => {
17 | return obj;
18 | }).disposer(() => {
19 | obj.closed = true;
20 | });
21 | }
22 | describe("using", () => {
23 | it("works with a single resource", async () => {
24 | const d = disposer();
25 | const [o] = await Promise.using(d, (obj) => {
26 | assert(!obj.closed);
27 | return [obj];
28 | });
29 | assert(o.closed);
30 | });
31 |
32 | it("works with a single promise", async () => {
33 | let ran = false;
34 | await Promise.using(Promise.resolve(3), (three) => {
35 | ran = true;
36 | assert.equal(three, 3);
37 | });
38 | assert(ran);
39 | });
40 |
41 | it("works with two succeeding resources", async () => {
42 | const [d, e] = [disposer(), disposer()];
43 | const [o, t] = await Promise.using(d, e, (one, two) => {
44 | assert(!one.closed);
45 | assert(!two.closed);
46 | return [one, two];
47 | });
48 | assert(o.closed);
49 | assert(t.closed);
50 | });
51 |
52 | it("works with a failing resource", async () => {
53 | const d = rejectedDisposer(3);
54 | let called = false;
55 | const v = await (Promise.using(d, assert.fail).catch(e => {
56 | assert.equal(e, 3);
57 | called = true;
58 | }));
59 | assert(called);
60 | });
61 |
62 |
63 | it("works with one succeeding resource and one failing resource", async () => {
64 | const [d, e] = [disposer(), rejectedDisposer(3)];
65 | assert(!d._obj.closed);
66 | await Promise.using(d, e, assert.fail).catch(e => {
67 | assert.equal(e, 3);
68 | });
69 | assert(d._obj.closed);
70 | });
71 |
72 | it("works with one succeeding promise and one failing resource", async () => {
73 | const [d, e] = [Promise.resolve(), rejectedDisposer(3)];
74 | await Promise.using(d, e, assert.fail).catch(e => {
75 | assert.equal(e, 3);
76 | });
77 | });
78 |
79 | it("works with one succeeding resource and one failing promise", async () => {
80 | const [d, e] = [disposer(), Promise.reject(3)];
81 | assert(!d._obj.closed);
82 | await Promise.using(d, e, assert.fail).catch(e => {
83 | assert.equal(e, 3);
84 | });
85 | assert(d._obj.closed);
86 | });
87 |
88 | it("works with two succeeding resources and one failing resource", async () => {
89 | const [d, e, f] = [disposer(), rejectedDisposer(3), disposer()];
90 | assert(!d._obj.closed);
91 | assert(!f._obj.closed);
92 | await Promise.using(d, e, f, assert.fail).catch(e => {
93 | assert.equal(e, 3);
94 | });
95 | assert(d._obj.closed);
96 | assert(f._obj.closed);
97 | });
98 |
99 | it("works with three succeeding resources", async () => {
100 | const [d, e, f] = [disposer(), disposer(3), disposer()];
101 | assert(!d._obj.closed);
102 | assert(!f._obj.closed);
103 | await Promise.using(d, e, f, () => { });
104 | assert(d._obj.closed);
105 | assert(e._obj.closed);
106 | assert(f._obj.closed);
107 | });
108 | });
--------------------------------------------------------------------------------
/tests/helpers/assert_long_trace.js:
--------------------------------------------------------------------------------
1 | var assert = require("assert");
2 |
3 | function assertLongTrace(error, expectedJumpCount, expectedFramesForJumpsMap) {
4 | var envFramePattern = /(?:\(node.js:|\(module.js:|\(timers.js:|\bcheckTimers\b|\bdrainQueue\b|\btimerLoop\b|\b_onImmediate\b|\b_immediateCallback\b)/;
5 | var stack = error.stack.split("\n");
6 | var frameLinePattern = /(^\s+at|@|\s+\(No stack trace\))/;
7 | var previousEventPattern = /^From previous event/;
8 | var firstLine;
9 | for (var i = 0; i < stack.length; ++i) {
10 | if (previousEventPattern.test(stack[i])) {
11 | throw new Error("From previous event before any frames");
12 | }
13 | if (frameLinePattern.test(stack[i])) {
14 | firstLine = i - 1;
15 | break;
16 | }
17 | }
18 | var prev = stack[firstLine - 1];
19 | var jumpCount = 1;
20 | var jumpIndex = 0;
21 | var currentJumpFramesCount = 0;
22 | var envFramesCount = 0;
23 | for (var i = firstLine; i < stack.length; ++i) {
24 | var line = stack[i];
25 | if (previousEventPattern.test(line)) {
26 | var jumpContainsOnlyEnvFrames =
27 | currentJumpFramesCount === 0 && envFramesCount > 0;
28 | if (!jumpContainsOnlyEnvFrames) {
29 | if (previousEventPattern.test(prev)) {
30 | throw new Error("2 consecutive From previous events");
31 | }
32 | if (jumpIndex < expectedFramesForJumpsMap.length) {
33 | var expectedFrames = expectedFramesForJumpsMap[jumpIndex];
34 | var expectedMessage = typeof expectedFrames === "number"
35 | ? (expectedFrames + "")
36 | : (expectedFrames[0] + "-" + expectedFrames[1]);
37 | var message = "Expected " + (jumpIndex+1) + "th jump to contain " +
38 | expectedMessage + " frames " +
39 | "but it contains " + currentJumpFramesCount + " frames";
40 | if (typeof expectedFrames === "number") {
41 | assert(expectedFrames === currentJumpFramesCount, message);
42 | } else {
43 | assert(expectedFrames[0] <= currentJumpFramesCount &&
44 | currentJumpFramesCount <= expectedFrames[1],
45 | message);
46 | }
47 | }
48 | jumpCount++;
49 | jumpIndex++;
50 | }
51 | currentJumpFramesCount = 0;
52 | envFramesCount = 0;
53 | } else if (frameLinePattern.test(line)) {
54 | if (envFramePattern.test(line)) {
55 | envFramesCount++;
56 | } else {
57 | currentJumpFramesCount++;
58 | }
59 | }
60 | prev = line;
61 | }
62 | assert.strictEqual(
63 | previousEventPattern.test(stack[stack.length - 1]), false,
64 | "The last line cannot be 'From previous event:'");
65 | if (typeof expectedJumpCount === "number") {
66 | assert.strictEqual(expectedJumpCount, jumpCount, "Expected " +
67 | expectedJumpCount + " jumps but saw " + jumpCount + " jumps");
68 | } else {
69 | assert(expectedJumpCount[0] <= jumpCount &&
70 | jumpCount <= expectedJumpCount[1],
71 | "Expected " +
72 | expectedJumpCount[0] + "-" + expectedJumpCount[1] +
73 | " jumps but saw " + jumpCount + " jumps"
74 | );
75 | }
76 |
77 | if (jumpCount > (expectedFramesForJumpsMap.length + 1)) {
78 | throw new Error("All jumps except the last one require an "+
79 | "expected frame count. " +
80 | "Got expected frame counts for only " +
81 | expectedFramesForJumpsMap.length + " while " + (jumpCount-1) +
82 | " was expected");
83 | }
84 |
85 | }
86 | module.exports = assertLongTrace;
87 |
--------------------------------------------------------------------------------
/tests/return.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 | const join = Promise.join;
4 |
5 | function wrap(fn, ...args) {
6 | return function() {
7 | return fn.apply(this, args);
8 | }
9 | }
10 |
11 | function returnThenable(thenable, expected) {
12 | const promise = Promise.resolve(true)
13 | return promise.thenReturn(thenable).then(function(v){
14 | assert(v === expected);
15 | });
16 | }
17 |
18 | function returnThenableReject(thenable, expected) {
19 | const promise = Promise.resolve(true)
20 | return promise.thenReturn(thenable).then(assert.fail, function(v){
21 | assert(v === expected);
22 | });
23 | }
24 |
25 | function returnValue(value) {
26 | const promise = Promise.resolve(true)
27 | return promise.thenReturn(value).then(function(v){
28 | assert(v === value);
29 | });
30 | }
31 |
32 |
33 | function handlePromise(val, done) {
34 | if (val && typeof val.then === "function") {
35 | val.then(success(done), fail(done));
36 | }
37 | }
38 |
39 | function testRejected(reason, test) {
40 | specify("already-rejected", function (done) {
41 | handlePromise(test(rejected(reason), done), done);
42 | });
43 | }
44 |
45 | describe("thenReturn", function () {
46 | it("forwards errors", () => {
47 | return Promise.reject(10)
48 | .thenReturn(100)
49 | .then(assert.fail, assert.ok);
50 | });
51 | });
52 |
53 | describe("thenReturn", function () {
54 |
55 | describe("primitives", function() {
56 | specify("null", wrap(returnValue, null));
57 | specify("undefined", wrap(returnValue, void 0));
58 | specify("string", wrap(returnValue, "asd"));
59 | specify("number", wrap(returnValue, 3));
60 | specify("boolean", wrap(returnValue, true));
61 | });
62 |
63 | describe("objects", function() {
64 | specify("plain", wrap(returnValue, {}));
65 | specify("function", wrap(returnValue, function(){}));
66 | specify("built-in function", wrap(returnValue, Array));
67 | specify("built-in object", wrap(returnValue, Math));
68 | });
69 |
70 | describe("thenables", function() {
71 | describe("which fulfill", function() {
72 | specify("immediately", wrap(returnThenable, {
73 | then: function(f) {
74 | f(10);
75 | }
76 | }, 10));
77 | specify("eventually", wrap(returnThenable, {
78 | then: function(f) {
79 | setTimeout(function() {
80 | f(10);
81 | }, 1);
82 | }
83 | }, 10));
84 | });
85 | describe("which reject", function(){
86 | specify("immediately", wrap(returnThenableReject, {
87 | then: function(f, r) {
88 | r(10);
89 | }
90 | }, 10));
91 | specify("eventually", wrap(returnThenableReject, {
92 | then: function(f, r) {
93 | setTimeout(function() {
94 | r(10);
95 | }, 1);
96 | }
97 | }, 10));
98 | });
99 | });
100 |
101 | describe("promises", function() {
102 | describe("which fulfill", function() {
103 | specify("already", wrap(returnThenable, Promise.resolve(10), 10));
104 | });
105 | describe("which reject", function() {
106 | var alreadyRejected = Promise.reject(10);
107 | alreadyRejected.then(assert.fail, function(){});
108 | specify("already", wrap(returnThenableReject, alreadyRejected, 10));
109 | });
110 |
111 | });
112 |
113 | specify("doesn't swallow errors", function() {
114 | var e = {};
115 | testRejected(e, function(promise){
116 | return promise.thenReturn(3).then(assert.fail, function(err) {
117 | assert(err = e);
118 | });
119 | });
120 | });
121 | });
--------------------------------------------------------------------------------
/tests/helpers/thenables.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var adapter = global.adapter;
4 | var fulfilled = adapter.fulfilled;
5 | var rejected = adapter.rejected;
6 | var pending = adapter.pending;
7 |
8 | var other = { other: "other" }; // a value we don't want to be strict equal to
9 |
10 | exports.fulfilled = {
11 | "a synchronously-fulfilled custom thenable": function (value) {
12 | return {
13 | then: function (onFulfilled) {
14 | onFulfilled(value);
15 | }
16 | };
17 | },
18 |
19 | "an asynchronously-fulfilled custom thenable": function (value) {
20 | return {
21 | then: function (onFulfilled) {
22 | setTimeout(function () {
23 | onFulfilled(value);
24 | }, 1);
25 | }
26 | };
27 | },
28 |
29 | "a synchronously-fulfilled one-time thenable": function (value) {
30 | var numberOfTimesThenRetrieved = 0;
31 | var ret = Object.create(null, {
32 | then: {
33 | get: function () {
34 |
35 | if (numberOfTimesThenRetrieved === 0) {
36 | ++numberOfTimesThenRetrieved;
37 | return function (onFulfilled) {
38 | onFulfilled(value);
39 | };
40 | }
41 | return null;
42 | }
43 | }
44 | });
45 | return ret;
46 | },
47 |
48 | "a thenable that tries to fulfill twice": function (value) {
49 | return {
50 | then: function (onFulfilled) {
51 | onFulfilled(value);
52 | onFulfilled(other);
53 | }
54 | };
55 | },
56 |
57 | "a thenable that fulfills but then throws": function (value) {
58 | return {
59 | then: function (onFulfilled) {
60 | onFulfilled(value);
61 | throw other;
62 | }
63 | };
64 | },
65 |
66 | "an already-fulfilled promise": function (value) {
67 | return fulfilled(value);
68 | },
69 |
70 | "an eventually-fulfilled promise": function (value) {
71 | var tuple = pending();
72 | setTimeout(function () {
73 | tuple.fulfill(value);
74 | }, 1);
75 | return tuple.promise;
76 | }
77 | };
78 |
79 | exports.rejected = {
80 | "a synchronously-rejected custom thenable": function (reason) {
81 | return {
82 | then: function (onFulfilled, onRejected) {
83 | onRejected(reason);
84 | }
85 | };
86 | },
87 |
88 | "an asynchronously-rejected custom thenable": function (reason) {
89 | return {
90 | then: function (onFulfilled, onRejected) {
91 | setTimeout(function () {
92 | onRejected(reason);
93 | }, 1);
94 | }
95 | };
96 | },
97 |
98 | "a synchronously-rejected one-time thenable": function (reason) {
99 | var numberOfTimesThenRetrieved = 0;
100 | return Object.create(null, {
101 | then: {
102 | get: function () {
103 | if (numberOfTimesThenRetrieved === 0) {
104 | ++numberOfTimesThenRetrieved;
105 | return function (onFulfilled, onRejected) {
106 | onRejected(reason);
107 | };
108 | }
109 | return null;
110 | }
111 | }
112 | });
113 | },
114 |
115 | "a thenable that immediately throws in `then`": function (reason) {
116 | return {
117 | then: function () {
118 | throw reason;
119 | }
120 | };
121 | },
122 |
123 | "an object with a throwing `then` accessor": function (reason) {
124 | return Object.create(null, {
125 | then: {
126 | get: function () {
127 | throw reason;
128 | }
129 | }
130 | });
131 | },
132 |
133 | "an already-rejected promise": function (reason) {
134 | var ret = rejected(reason);
135 | ret.caught(function(){});
136 | return ret;
137 | },
138 |
139 | "an eventually-rejected promise": function (reason) {
140 | var tuple = pending();
141 | setTimeout(function () {
142 | tuple.reject(reason);
143 | }, 1);
144 | return tuple.promise;
145 | }
146 | };
147 |
--------------------------------------------------------------------------------
/tests/tapCatch.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var assert = require("assert");
3 | const Promise = require("../promise.js");
4 | var testUtils = require("./helpers/util.js");
5 | function rejection() {
6 | var error = new Error("test");
7 | var rejection = Promise.reject(error);
8 | rejection.err = error;
9 | return rejection;
10 | }
11 |
12 | describe("tapCatch", function () {
13 |
14 | specify("passes through rejection reason", function() {
15 | return rejection().tapCatch(function() {
16 | return 3;
17 | }).caught(function(value) {
18 | assert.equal(value.message, "test");
19 | });
20 | });
21 |
22 | specify("passes through reason after returned promise is fulfilled", function() {
23 | var async = false;
24 | return rejection().tapCatch(function() {
25 | return new Promise(function(r) {
26 | setTimeout(function(){
27 | async = true;
28 | r(3);
29 | }, 1);
30 | });
31 | }).caught(function(value) {
32 | assert(async);
33 | assert.equal(value.message, "test");
34 | });
35 | });
36 |
37 | specify("is not called on fulfilled promise", function() {
38 | var called = false;
39 | return Promise.resolve("test").tapCatch(function() {
40 | called = true;
41 | }).then(function(value){
42 | assert(!called);
43 | }, assert.fail);
44 | });
45 |
46 | specify("passes immediate rejection", function() {
47 | var err = new Error();
48 | return rejection().tapCatch(function() {
49 | throw err;
50 | }).tap(assert.fail).then(assert.fail, function(e) {
51 | assert(err === e);
52 | });
53 | });
54 |
55 | specify("passes eventual rejection", function() {
56 | var err = new Error();
57 | return rejection().tapCatch(function() {
58 | return new Promise(function(_, rej) {
59 | setTimeout(function(){
60 | rej(err);
61 | }, 1)
62 | });
63 | }).tap(assert.fail).then(assert.fail, function(e) {
64 | assert(err === e);
65 | });
66 | });
67 |
68 | specify("passes reason", function() {
69 | return rejection().tapCatch(function(a) {
70 | assert(a === rejection);
71 | }).then(assert.fail, function() {});
72 | });
73 |
74 | specify("Works with predicates", function() {
75 | var called = false;
76 | return Promise.reject(new TypeError).tapCatch(TypeError, function(err) {
77 | called = true;
78 | assert(err instanceof TypeError)
79 | }).then(assert.fail, function(err) {
80 | assert(called === true);
81 | assert(err instanceof TypeError);
82 | });
83 | });
84 | specify("Does not get called on predicates that don't match", function() {
85 | var called = false;
86 | return Promise.reject(new TypeError).tapCatch(ReferenceError, function(a) {
87 | called = true;
88 | }).then(assert.fail, function(err) {
89 | assert(called === false);
90 | assert(err instanceof TypeError);
91 | });
92 | });
93 |
94 | specify("Supports multiple predicates", function() {
95 | var calledA = false;
96 | var calledB = false;
97 | var calledC = false;
98 |
99 | var promiseA = Promise.reject(new ReferenceError).tapCatch(
100 | ReferenceError,
101 | TypeError,
102 | function (e) {
103 | assert(e instanceof ReferenceError);
104 | calledA = true;
105 | }
106 | ).catch(function () {});
107 |
108 | var promiseB = Promise.reject(new TypeError).tapCatch(
109 | ReferenceError,
110 | TypeError,
111 | function (e) {
112 | assert(e instanceof TypeError);
113 | calledB = true;
114 | }
115 | ).catch(function () {});
116 |
117 | var promiseC = Promise.reject(new SyntaxError).tapCatch(
118 | ReferenceError,
119 | TypeError,
120 | function (e) {
121 | calledC = true;
122 | }
123 | ).catch(function () {});
124 |
125 | return Promise.join(promiseA, promiseB, promiseC, function () {
126 | assert(calledA === true);
127 | assert(calledB === true);
128 | assert(calledC === false);
129 | });
130 | })
131 | });
132 |
--------------------------------------------------------------------------------
/tests/filter.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("filter", () => {
5 | it("filters sync stuff", async () => {
6 | var arr = await Promise.filter([1,2,3], x => x > 2);
7 | assert.deepEqual(arr, [3]);
8 | });
9 |
10 | it("filters async stuff", async () => {
11 | var arr = await Promise.filter([1,2,3], async x => x > 2);
12 | assert.deepEqual(arr, [3]);
13 | });
14 |
15 | it("filters async stuff with promise input", async () => {
16 | var arr = await Promise.filter([1,2, Promise.resolve(3)], async x => x > 2);
17 | assert.deepEqual(arr, [3]);
18 | });
19 |
20 | it("filters async stuff with multiple promise input", async () => {
21 | var arr = await Promise.filter([1,Promise.resolve(2), Promise.resolve(3)], async x => x > 2);
22 | assert.deepEqual(arr, [3]);
23 | });
24 |
25 | it("filters stuff that's sometimes async", async () => {
26 | var arr = await Promise.filter([1,2, Promise.resolve(3)], (x, i) => {
27 | if(i%2) return Promise.resolve(true);
28 | return false;
29 | });
30 | assert.deepEqual(arr, [2]);
31 | });
32 | });
33 |
34 | const specify = it;
35 |
36 | describe("Promise filter bluebird tests", function() {
37 |
38 | function ThrownError() {}
39 |
40 |
41 | var arr = [1,2,3];
42 |
43 | function assertArr(arr) {
44 | assert(arr.length === 2);
45 | assert(arr[0] === 1);
46 | assert(arr[1] === 3);
47 | }
48 |
49 | function assertErr(e) {
50 | assert(e instanceof ThrownError);
51 | }
52 |
53 | function assertFail() {
54 | assert.fail();
55 | }
56 |
57 | describe("should accept eventual booleans", function() {
58 | specify("immediately fulfilled", function() {
59 | return Promise.filter(arr, function(v) {
60 | return new Promise(function(r){
61 | r(v !== 2);
62 | });
63 | }).then(assertArr);
64 | });
65 |
66 | specify("already fulfilled", function() {
67 | return Promise.filter(arr, function(v) {
68 | return Promise.resolve(v !== 2);
69 | }).then(assertArr);
70 | });
71 |
72 | specify("eventually fulfilled", function() {
73 | return Promise.filter(arr, function(v) {
74 | return new Promise(function(r){
75 | setTimeout(function(){
76 | r(v !== 2);
77 | }, 1);
78 | });
79 | }).then(assertArr);
80 | });
81 |
82 | specify("immediately rejected", function() {
83 | return Promise.filter(arr, function(v) {
84 | return new Promise(function(v, r){
85 | r(new ThrownError());
86 | });
87 | }).then(assertFail, assertErr);
88 | });
89 | specify("already rejected", function() {
90 | return Promise.filter(arr, function(v) {
91 | return Promise.reject(new ThrownError());
92 | }).then(assertFail, assertErr);
93 | });
94 | specify("eventually rejected", function() {
95 | return Promise.filter(arr, function(v) {
96 | return new Promise(function(v, r){
97 | setTimeout(function(){
98 | r(new ThrownError());
99 | }, 1);
100 | });
101 | }).then(assertFail, assertErr);
102 | });
103 |
104 |
105 | specify("immediately fulfilled thenable", function() {
106 | return Promise.filter(arr, function(v) {
107 | return {
108 | then: function(f, r) {
109 | f(v !== 2);
110 | }
111 | };
112 | }).then(assertArr);
113 | });
114 | specify("eventually fulfilled thenable", function() {
115 | return Promise.filter(arr, function(v) {
116 | return {
117 | then: function(f, r) {
118 | setTimeout(function(){
119 | f(v !== 2);
120 | }, 1);
121 | }
122 | };
123 | }).then(assertArr);
124 | });
125 |
126 | specify("immediately rejected thenable", function() {
127 | return Promise.filter(arr, function(v) {
128 | return {
129 | then: function(f, r) {
130 | r(new ThrownError());
131 | }
132 | };
133 | }).then(assertFail, assertErr);
134 | });
135 | specify("eventually rejected thenable", function() {
136 | return Promise.filter(arr, function(v) {
137 | return {
138 | then: function(f, r) {
139 | setTimeout(function(){
140 | r(new ThrownError());
141 | }, 1);
142 | }
143 | };
144 | }).then(assertFail, assertErr);
145 | });
146 |
147 | });
148 | });
--------------------------------------------------------------------------------
/tests/each.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var assert = require("assert");
4 | //var testUtils = require("./helpers/util.js");
5 | const Promise = require("../promise.js");
6 |
7 | function promised(val) {
8 | return new Promise(function(f) {
9 | setTimeout(function() {
10 | f(val);
11 | }, 1);
12 | });
13 | }
14 |
15 | function thenabled(val, arr) {
16 | return {
17 | then: function(f){
18 | setTimeout(function() {
19 | if (arr) arr.push(val);
20 | f(val);
21 | }, 1);
22 | }
23 | };
24 | }
25 |
26 | if(!Promise.delay) { // for test independence
27 | Promise.delay = ms => new Promise(r => setTimeout(r, ms));
28 | }
29 |
30 | describe("Promise.each", function() {
31 |
32 | it("should not return the array's values mapped", function() {
33 | var a = [promised(1), promised(2), promised(3)];
34 | var b = [];
35 | return Promise.resolve(a).each(function(val) {
36 | b.push(3-val);
37 | return val + 2;
38 | }).then(function(ret) {
39 | assert.deepEqual(ret, [1,2,3]);
40 | }).catch(assert.fail);
41 | });
42 |
43 | it("takes value, index and length", function() {
44 | var a = [promised(1), promised(2), promised(3)];
45 | var b = [];
46 | return Promise.resolve(a).each(function(value, index, length) {
47 | b.push(value, index, length);
48 | }).then(function(ret) {
49 | assert.deepEqual(b, [1, 0, 3, 2, 1, 3, 3, 2, 3]);
50 | });
51 | });
52 |
53 | it("waits for returned promise before proceeding next", function() {
54 | var a = [promised(1), promised(2), promised(3)];
55 | var b = [];
56 | return Promise.resolve(a).each(function(value) {
57 | b.push(value);
58 | return Promise.delay(1).then(function(){
59 | b.push(value*2);
60 | });
61 | }).then(function(ret) {
62 | assert.deepEqual(b, [1,2,2,4,3,6]);
63 | });
64 | });
65 |
66 | it("waits for returned thenable before proceeding next", function() {
67 | var b = [1, 2, 3];
68 | var a = [thenabled(1), thenabled(2), thenabled(3)];
69 | return Promise.resolve(a).each(function(val) {
70 | b.push(val * 50);
71 | return thenabled(val * 500, b);
72 | }).then(function(ret) {
73 | assert.deepEqual(b, [1, 2, 3, 50, 500, 100, 1000, 150, 1500]);
74 | });
75 | });
76 |
77 | it("doesnt iterate with an empty array", function() {
78 | return Promise.each([], function(val) {
79 | throw new Error();
80 | }).then(function(ret) {
81 | assert.deepEqual(ret, []);
82 | });
83 | });
84 |
85 | it("iterates with an array of single item", function() {
86 | var b = [];
87 | return Promise.each([promised(1)], function(val) {
88 | b.push(val);
89 | return thenabled(val*2, b);
90 | }).then(function(ret) {
91 | assert.deepEqual(b, [1,2]);
92 | });
93 | });
94 | });
95 |
96 | describe("Promise.prototype.each", function() {
97 |
98 | it("should return the array's values", function() {
99 | var a = [promised(1), promised(2), promised(3)];
100 | return Promise.resolve(a).each(function(val) {
101 | return val;
102 | }).then(function(ret) {
103 | assert.deepEqual(ret, [1,2,3]);
104 | });
105 | });
106 |
107 |
108 | it("takes value, index and length", function() {
109 | var a = [promised(1), promised(2), promised(3)];
110 | var b = [];
111 | return Promise.resolve(a).each(function(value, index, length) {
112 | b.push(value, index, length);
113 | }).then(function(ret) {
114 | assert.deepEqual(b, [1, 0, 3, 2, 1, 3, 3, 2, 3]);
115 | });
116 | });
117 |
118 | it("waits for returned promise before proceeding next", function() {
119 | var a = [promised(1), promised(2), promised(3)];
120 | var b = [];
121 | return Promise.resolve(a).each(function(value) {
122 | b.push(value);
123 | return Promise.delay(1).then(function(){
124 | b.push(value*2);
125 | });
126 | }).then(function(ret) {
127 | assert.deepEqual(b, [1,2,2,4,3,6]);
128 | });
129 | });
130 |
131 | it("waits for returned thenable before proceeding next", function() {
132 | var b = [1, 2, 3];
133 | var a = [thenabled(1), thenabled(2), thenabled(3)];
134 | return Promise.resolve(a).each(function(val) {
135 | b.push(val * 50);
136 | return thenabled(val * 500, b);
137 | }).then(function(ret) {
138 | assert.deepEqual(b, [1, 2, 3, 50, 500, 100, 1000, 150, 1500]);
139 | });
140 | });
141 |
142 | it("doesnt iterate with an empty array", function() {
143 | return Promise.resolve([]).each(function(val) {
144 | throw new Error();
145 | }).then(function(ret) {
146 | assert.deepEqual(ret, []);
147 | });
148 | });
149 |
150 | it("iterates with an array of single item", function() {
151 | var b = [];
152 | return Promise.resolve([promised(1)]).each(function(val) {
153 | b.push(val);
154 | return thenabled(val*2, b);
155 | }).then(function(ret) {
156 | assert.deepEqual(b, [1,2]);
157 | });
158 | });
159 | });
160 |
161 | describe("mapSeries and each", function() {
162 | it.skip("is mixed", function() {
163 | return Promise.mapSeries([1, 2, 3], function(value) {
164 | return value * 2;
165 | }).then(function(result) {
166 | assert.deepEqual(result, [2, 4, 6]);
167 | }).then(function() {
168 | return Promise.each([1, 2, 3], function(value) {
169 | return value * 2;
170 | }).then(function(result) {
171 | assert.deepEqual(result, [1, 2, 3]);
172 | });
173 | }).thenReturn([1, 2, 3]).mapSeries(function(value) {
174 | return value * 2;
175 | }).then(function(result) {
176 | assert.deepEqual(result, [2, 4, 6]);
177 | }).thenReturn([1, 2, 3]).each(function(value) {
178 | return value * 2;
179 | }).then(function(result) {
180 | assert.deepEqual(result, [1, 2, 3]);
181 | });
182 | })
183 |
184 |
185 | });
--------------------------------------------------------------------------------
/tests/timers.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var assert = require("assert");
3 | var testUtils = require("./helpers/util.js");
4 | var Promise = require("../promise.js");
5 | // Promise.config({cancellation: true}) // TODO : Don't support cancellations at this point.
6 | var globalObject = typeof window !== "undefined" ? window : new Function("return this;")();
7 | /*
8 | Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
9 | Permission is hereby granted, free of charge, to any person obtaining a copy
10 | of this software and associated documentation files (the "Software"), to
11 | deal in the Software without restriction, including without limitation the
12 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13 | sell copies of the Software, and to permit persons to whom the Software is
14 | furnished to do so, subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be included in
17 | all copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 | IN THE SOFTWARE.
26 | */
27 |
28 | describe("timeout", function () {
29 | it("should do nothing if the promise fulfills quickly", async function() {
30 |
31 | Promise.delay(1).timeout(200).then(testUtils.noop);
32 | });
33 |
34 | it("should do nothing if the promise rejects quickly", function() {
35 | var goodError = new Error("haha!");
36 | return Promise.delay(1)
37 | .then(function () {
38 | throw goodError;
39 | })
40 | .timeout(200)
41 | .then(undefined, function (error) {
42 | assert(error === goodError);
43 | });
44 | });
45 |
46 | it("should reject with a timeout error if the promise is too slow", function() {
47 | return Promise.delay(1)
48 | .timeout(10)
49 | .caught(Promise.TimeoutError, function(){
50 | })
51 | });
52 |
53 | it("should reject with a custom timeout error if the promise is too slow and msg was provided", function() {
54 | return Promise.delay(1)
55 | .timeout(10, "custom")
56 | .caught(Promise.TimeoutError, function(e){
57 | assert(/custom/i.test(e.message));
58 | });
59 | });
60 |
61 | //Falls under cancellations
62 | it.skip("should cancel the parent promise once the timeout expires", function() {
63 | var didNotExecute = true;
64 | var wasRejectedWithTimeout = false;
65 |
66 | var p = Promise.delay(22).then(function() {
67 | didNotExecute = false;
68 | });
69 | p.timeout(11).thenReturn(10).caught(Promise.TimeoutError, function(e) {
70 | wasRejectedWithTimeout = true;
71 | })
72 | return Promise.delay(33).then(function() {
73 | assert(didNotExecute, "parent promise was not cancelled");
74 | assert(wasRejectedWithTimeout, "promise was not rejected with timeout");
75 | })
76 | });
77 |
78 | it("should not cancel the parent promise if there are multiple consumers", function() {
79 | var derivedNotCancelled = false;
80 | var p = Promise.delay(22);
81 | var derived = p.then(function() {
82 | derivedNotCancelled = true;
83 | })
84 | p.timeout(11).thenReturn(10).catch(()=>{});
85 | return Promise.delay(33).then(function() {
86 | assert(derivedNotCancelled, "derived promise was cancelled")
87 | })
88 | })
89 |
90 | var globalsAreReflectedInGlobalObject = (function(window) {
91 | var fn = function(id){return clearTimeout(id);};
92 | var old = window.clearTimeout;
93 | window.clearTimeout = fn;
94 | var ret = clearTimeout === fn;
95 | window.clearTimeout = old;
96 | return ret;
97 | })(globalObject);
98 |
99 | if (globalsAreReflectedInGlobalObject) {
100 |
101 |
102 | describe.skip("timer handle clearouts", function() {
103 | var expectedHandleType;
104 | before(function() {
105 | expectedHandleType = typeof (globalObject.setTimeout(function(){}, 1));
106 | });
107 |
108 |
109 | it("should clear timeouts with proper handle type when fulfilled", function() {
110 | var old = globalObject.clearTimeout;
111 | var handleType = "empty";
112 | globalObject.clearTimeout = function(handle) {
113 | handleType = typeof handle;
114 | globalObject.clearTimeout = old;
115 | };
116 |
117 | return Promise.delay(1).timeout(10000).then(function() {
118 | assert.strictEqual(expectedHandleType, handleType);
119 | });
120 | });
121 |
122 | it("should clear timeouts with proper handle type when rejected", function() {
123 | var old = globalObject.clearTimeout;
124 | var handleType = "empty";
125 | globalObject.clearTimeout = function(handle) {
126 | handleType = typeof handle;
127 | globalObject.clearTimeout = old;
128 | };
129 |
130 | return new Promise(function(_, reject) {
131 | setTimeout(reject, 10);
132 | }).timeout(10000).then(null, function() {
133 | assert.strictEqual(expectedHandleType, handleType);
134 | });
135 | });
136 | })
137 |
138 | }
139 | });
140 |
141 | describe("delay", function () {
142 | it("should not delay rejection", function() {
143 | var promise = Promise.reject(5).delay(1);
144 |
145 | promise.then(assert.fail, testUtils.noop);
146 |
147 | });
148 |
149 | it("should delay after resolution", function () {
150 | var promise1 = Promise.delay(1, "what");
151 | var promise2 = promise1.delay(1);
152 |
153 | return promise2.then(async function (value) {
154 | assert(value === "what");
155 | });
156 | });
157 |
158 | it("should resolve follower promise's value", function() {
159 | var resolveF;
160 | var f = new Promise(function() {
161 | resolveF = arguments[0];
162 | });
163 | var v = new Promise(function(f) {
164 | setTimeout(function() {
165 | f(3);
166 | }, 1);
167 | });
168 | resolveF(v);
169 | return Promise.delay(1, f).then(function(value) {
170 | assert.equal(value, 3);
171 | });
172 | });
173 |
174 | it("should reject with a custom message if a string was provided as a parameter", function() {
175 | var err = "Custom error string"
176 | return Promise.delay(1)
177 | .timeout(10, err)
178 | .caught(function(e){
179 | assert(e.m === err);
180 | });
181 | });
182 |
183 | });
184 |
--------------------------------------------------------------------------------
/tests/props.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Promise = require("../promise.js");
3 |
4 | describe("props", () => {
5 |
6 | it("reads the props of a simple object ", async () => {
7 | const o = {x:3, y: 5};
8 | const o2 = await Promise.resolve(o).props();
9 | assert.equal(o2.x, 3);
10 | assert.equal(o2.y, 5);
11 | });
12 |
13 | it("reads the props of an object where one is awaitable", async () => {
14 | const o = {x:3, y: Promise.resolve(5)};
15 | const o2 = await Promise.resolve(o).props();
16 | assert.equal(o2.x, 3);
17 | assert.equal(o2.y, 5);
18 | });
19 |
20 | it("reads the props of an object where all are awaitable", async () => {
21 | const o = {x: Promise.resolve(3), y: Promise.resolve(5)};
22 | const o2 = await Promise.resolve(o).props();
23 | assert.equal(o2.x, 3);
24 | assert.equal(o2.y, 5);
25 | });
26 |
27 | it("rejects when one of the props rejects", async () => {
28 | const o = {x: Promise.reject(new Error("Hello")), y: Promise.resolve(5)};
29 | try {
30 | const o2 = await Promise.resolve(o).props();
31 | assert.fail();
32 | } catch (e) {
33 | assert(e.message === "Hello");
34 | }
35 | });
36 | });
37 |
38 | const specify = it;
39 | describe("props bluebird original tests", () => {
40 | specify("should reject undefined", async function() {
41 | try {
42 | await Promise.props();
43 | assert.fail();
44 | } catch (e) {};
45 | });
46 |
47 | specify("should reject primitive", async function() {
48 | try {
49 | await Promise.props("str");
50 | assert.fail();
51 | } catch (e) {};
52 |
53 | });
54 |
55 | specify("should resolve to new object", function() {
56 | var o = {};
57 | return Promise.props(o).then(function(v){
58 | assert(v !== o);
59 | assert.deepEqual(o, v);
60 | });
61 | });
62 |
63 | specify("should resolve value properties", function() {
64 | var o = {
65 | one: 1,
66 | two: 2,
67 | three: 3
68 | };
69 | return Promise.props(o).then(function(v){
70 | assert.deepEqual({
71 | one: 1,
72 | two: 2,
73 | three: 3
74 | }, v);
75 | });
76 | });
77 |
78 | specify("should resolve immediate properties", function() {
79 | var o = {
80 | one: Promise.resolve(1),
81 | two: Promise.resolve(2),
82 | three: Promise.resolve(3)
83 | };
84 | return Promise.props(o).then(function(v){
85 | assert.deepEqual({
86 | one: 1,
87 | two: 2,
88 | three: 3
89 | }, v);
90 | });
91 | });
92 |
93 | // specify("should resolve eventual properties", function() {
94 | // var d1 = Promise.defer(),
95 | // d2 = Promise.defer(),
96 | // d3 = Promise.defer();
97 | // var o = {
98 | // one: d1.promise,
99 | // two: d2.promise,
100 | // three: d3.promise
101 | // };
102 |
103 | // setTimeout(function(){
104 | // d1.fulfill(1);
105 | // d2.fulfill(2);
106 | // d3.fulfill(3);
107 | // }, 1);
108 |
109 | // return Promise.props(o).then(function(v){
110 | // assert.deepEqual({
111 | // one: 1,
112 | // two: 2,
113 | // three: 3
114 | // }, v);
115 | // });
116 |
117 |
118 | // });
119 |
120 | // specify("should reject if any input promise rejects", function() {
121 | // var o = {
122 | // one: Promise.resolve(1),
123 | // two: Promise.reject(2),
124 | // three: Promise.resolve(3)
125 | // };
126 | // return Promise.props(o).then(assert.fail, function(v){
127 | // assert(v === 2);
128 | // });
129 | // });
130 |
131 | // specify("should accept a promise for an object", function() {
132 | // var o = {
133 | // one: Promise.resolve(1),
134 | // two: Promise.resolve(2),
135 | // three: Promise.resolve(3)
136 | // };
137 | // var d1 = Promise.defer();
138 | // setTimeout(function(){
139 | // d1.fulfill(o);
140 | // }, 1);
141 | // return Promise.props(d1.promise).then(function(v){
142 | // assert.deepEqual({
143 | // one: 1,
144 | // two: 2,
145 | // three: 3
146 | // }, v);
147 | // });
148 |
149 | // });
150 |
151 | // specify("should reject a promise for a primitive", function() {
152 | // var d1 = Promise.defer();
153 | // setTimeout(function(){
154 | // d1.fulfill("text");
155 | // }, 1);
156 | // return Promise.props(d1.promise).caught(TypeError, function(){
157 | // });
158 |
159 | // });
160 |
161 | // specify("should accept thenables in properties", function() {
162 | // var t1 = {then: function(cb){cb(1);}};
163 | // var t2 = {then: function(cb){cb(2);}};
164 | // var t3 = {then: function(cb){cb(3);}};
165 | // var o = {
166 | // one: t1,
167 | // two: t2,
168 | // three: t3
169 | // };
170 | // return Promise.props(o).then(function(v){
171 | // assert.deepEqual({
172 | // one: 1,
173 | // two: 2,
174 | // three: 3
175 | // }, v);
176 | // });
177 | // });
178 |
179 | // specify("should accept a thenable for thenables in properties", function() {
180 | // var o = {
181 | // then: function (f) {
182 | // f({
183 | // one: {
184 | // then: function (cb) {
185 | // cb(1);
186 | // }
187 | // },
188 | // two: {
189 | // then: function (cb) {
190 | // cb(2);
191 | // }
192 | // },
193 | // three: {
194 | // then: function (cb) {
195 | // cb(3);
196 | // }
197 | // }
198 | // });
199 | // }
200 | // };
201 | // return Promise.props(o).then(function(v){
202 | // assert.deepEqual({
203 | // one: 1,
204 | // two: 2,
205 | // three: 3
206 | // }, v);
207 | // });
208 | // });
209 |
210 | // specify("treats arrays for their properties", function() {
211 | // var o = [1,2,3];
212 |
213 | // return Promise.props(o).then(function(v){
214 | // assert.deepEqual({
215 | // 0: 1,
216 | // 1: 2,
217 | // 2: 3
218 | // }, v);
219 | // });
220 | // });
221 |
222 | // specify("works with es6 maps", function() {
223 | // return Promise.props(new Map([
224 | // ["a", Promise.resolve(1)],
225 | // ["b", Promise.resolve(2)],
226 | // ["c", Promise.resolve(3)]
227 | // ])).then(function(result) {
228 | // assert.strictEqual(result.get("a"), 1);
229 | // assert.strictEqual(result.get("b"), 2);
230 | // assert.strictEqual(result.get("c"), 3);
231 | // });
232 | // });
233 |
234 | // specify("doesn't await promise keys in es6 maps", function() {
235 | // var a = new Promise(function() {});
236 | // var b = new Promise(function() {});
237 | // var c = new Promise(function() {});
238 |
239 | // return Promise.props(new Map([
240 | // [a, Promise.resolve(1)],
241 | // [b, Promise.resolve(2)],
242 | // [c, Promise.resolve(3)]
243 | // ])).then(function(result) {
244 | // assert.strictEqual(result.get(a), 1);
245 | // assert.strictEqual(result.get(b), 2);
246 | // assert.strictEqual(result.get(c), 3);
247 | // });
248 | // });
249 |
250 | // specify("empty map should resolve to empty map", function() {
251 | // return Promise.props(new Map()).then(function(result) {
252 | // assert(result instanceof Map);
253 | // });
254 | // });
255 |
256 | });
--------------------------------------------------------------------------------
/promiseFns/promisify.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const util = require('./util');
4 | // indicates to use the context of the promisified function (it's true `this`)
5 | // like bluebird's `var THIS = {};`
6 | const USE_THIS = Symbol("USE_THIS");
7 | const IS_PROMISIFIED = Symbol("IS_PROMISIFIED");
8 | const IDENTIFIER_REGEX = /^[a-z$_][a-z$_0-9]*$/i;
9 | const THIS_ASSIGNMENT_PATTERN = /this\s*\.\s*\S+\s*=/;
10 | const NO_COPY_PROPS = [
11 | 'arity', // From BB.Firefox 4
12 | 'length',
13 | 'name',
14 | 'arguments',
15 | 'caller',
16 | 'callee',
17 | 'prototype',
18 | IS_PROMISIFIED
19 | ];
20 |
21 | const FORBIDDEN_PROTOTYPES = [
22 | Array.prototype,
23 | Function.prototype,
24 | Object.prototype
25 | ];
26 |
27 | module.exports = (Bluebird) => {
28 | Bluebird.promisify = function promisify(fn, { context = USE_THIS, multiArgs = false } = {}) {
29 | if(util.promisify && context === USE_THIS && !multiArgs) {
30 | let promisified = util.promisify(fn);
31 | return function(...args) { return Bluebird.resolve(promisified.call(this, ...args)); }
32 | }
33 | if (fn[IS_PROMISIFIED]) return fn;
34 |
35 | return promisifyFunction({ fn, context, multiArgs });
36 | };
37 |
38 | Bluebird.promisifyAll = function promisifyAll(obj, { context = null, multiArgs = false, suffix = "Async", filter = null, promisifier = null } = {}) {
39 | if (typeof suffix !== 'string' || !isIdentifier(suffix)) {
40 | throw new RangeError("The suffix should be a string matching [a-z$_][a-z$_0-9]*");
41 | }
42 |
43 | // promisify any class that is a property of obj
44 | const keys = inheritedDataKeys(obj);
45 | for (const key of keys) {
46 | const fn = obj[key];
47 |
48 | if (key !== 'constructor' && isClass(fn)) {
49 | internalPromisifyAll(fn.prototype, suffix, filter, promisifier, multiArgs);
50 | internalPromisifyAll(fn, suffix, filter, promisifier, multiArgs);
51 | }
52 | }
53 |
54 | return internalPromisifyAll(obj, suffix, filter, promisifier, multiArgs);
55 | };
56 |
57 | Bluebird.fromNode = // back compat alias
58 | Bluebird.fromCallback = function fromCallback(fn, { context = null, multiArgs = false } = {}) {
59 | return new Bluebird((resolve, reject) => {
60 | let resolver = createResolver({ resolve, reject, multiArgs });
61 |
62 | return fn.apply(context, [resolver]);
63 | });
64 | };
65 |
66 | function internalPromisifyAll(obj, suffix, filter, promisifier, multiArgs) {
67 | // 1. determine what to promisify.
68 | const functionKeys = inheritedDataKeys(obj).filter(key => shouldPromisify(key, suffix, obj, filter))
69 |
70 | // 2. Doooo it!
71 | for (const key of functionKeys) {
72 | let promisified;
73 |
74 | if (promisifier) {
75 | promisified = promisifier(obj[key], () => {
76 | return promisifyFunction({ fn: obj[key], context: USE_THIS, multiArgs, dynamicLookupKey: key });
77 | });
78 | }
79 | else {
80 | promisified = promisifyFunction({ fn: obj[key], context: USE_THIS, multiArgs, dynamicLookupKey: key });
81 | }
82 |
83 | obj[`${key}${suffix}`] = promisified;
84 | }
85 |
86 | return obj;
87 | }
88 |
89 | // roughly equivalent to BB's makeNodePromisified()
90 | function promisifyFunction({ fn, context, multiArgs, dynamicLookupKey }) {
91 | let promisifiedFunction = function(...args) {
92 |
93 | if (context === USE_THIS) {
94 | context = this;
95 | }
96 |
97 | // dynamic lookup
98 | if (context && dynamicLookupKey) {
99 | fn = context[dynamicLookupKey];
100 | }
101 |
102 | return new Bluebird((resolve, reject) => {
103 | let resolver = createResolver({ resolve, reject, multiArgs });
104 |
105 | try {
106 | return fn.apply(context, [...args, resolver]);
107 | }
108 | catch (err) {
109 | reject(ensureIsError(err));
110 | }
111 | });
112 | }
113 |
114 | copyFunctionProps(fn, promisifiedFunction);
115 | Object.defineProperty(promisifiedFunction, IS_PROMISIFIED, { enumerable: false, value: true });
116 |
117 | return promisifiedFunction;
118 | }
119 |
120 | // Generate the function given to a node-style async function as the callback.
121 | function createResolver({ resolve, reject, multiArgs }) {
122 | return (err, ...values) => {
123 | if (err) {
124 | err = ensureIsError(err); // if it's primitive, make in an Error
125 |
126 | if (err instanceof Error && Object.getPrototypeOf(err) === Error.prototype) {
127 | // if it's a base Error (not a custom error), make it an OperationalError
128 | err = Bluebird.OperationalError.fromError(ensureIsError(err));
129 | }
130 |
131 | reject(err);
132 | }
133 | else if (multiArgs) {
134 | resolve(Bluebird.all(values))
135 | }
136 | else {
137 | resolve(values[0]);
138 | }
139 | }
140 | }
141 | };
142 |
143 | function isPromisified(fn) {
144 | return fn[IS_PROMISIFIED];
145 | }
146 |
147 | function hasPromisified(obj, key, suffix) {
148 | let desc = Object.getOwnPropertyDescriptor(obj, key + suffix);
149 |
150 | if (desc == null) return false;
151 | if (desc.get || desc.set) return true; // bluebird does this if there is a getter/setter. it skips that prop
152 |
153 | return isPromisified(desc.value);
154 | }
155 |
156 | function shouldPromisify(key, suffix, obj, filter) {
157 | const fn = obj[key];
158 |
159 | // must be a function
160 | if (typeof fn !== 'function') return false;
161 |
162 | // must not already be promisified
163 | if (isPromisified(fn) || hasPromisified(obj, key, suffix)) return false;
164 |
165 | //must pass filter || defaultFilter
166 | let passesFilter = defaultFilter(key, fn, obj, true);
167 | passesFilter = filter ? filter(key, fn, obj, passesFilter) : passesFilter;
168 | if (!passesFilter) return false;
169 |
170 | return true;
171 | }
172 |
173 | function isIdentifier(name) {
174 | return IDENTIFIER_REGEX.test(name)
175 | }
176 |
177 | function defaultFilter(name) {
178 | return isIdentifier(name) &&
179 | name.charAt(0) !== "_" &&
180 | name !== "constructor";
181 | };
182 |
183 | /* Bluebird rules (at least the es5+ code path):
184 | - Get all object prop keys enumerable and now from object.
185 | - Do the same with it's prototype (and it's prototype)
186 | - Stop when you reach the end, or a prototype of Array, Object or Function
187 | */
188 | function inheritedDataKeys(obj) {
189 | let foundKeys = [];
190 | let visitedKeys = new Set();
191 |
192 | // opted not to make this recursive and just loop.
193 | while (obj && !FORBIDDEN_PROTOTYPES.includes(obj)) {
194 | let keys;
195 |
196 | try {
197 | keys = Object.getOwnPropertyNames(obj);
198 | }
199 | catch (e) {
200 | return foundKeys;
201 | }
202 |
203 | for (const key of keys) {
204 | if (!visitedKeys.has(key)) {
205 | visitedKeys.add(key);
206 |
207 | let desc = Object.getOwnPropertyDescriptor(obj, key);
208 | if (desc != null && desc.get == null && desc.set == null) {
209 | foundKeys.push(key);
210 | }
211 | }
212 | }
213 |
214 | obj = Object.getPrototypeOf(obj);
215 | }
216 |
217 | return foundKeys;
218 | }
219 |
220 | // lifted from BB. only a little formatting changed.
221 | function isClass(fn) {
222 | try {
223 | if (typeof fn === "function") {
224 | var keys = Object.getOwnPropertyNames(fn.prototype);
225 |
226 | let hasMethods = keys.length > 1;
227 | let hasMethodsOtherThanConstructor = keys.length > 0 && !(keys.length === 1 && keys[0] === 'constructor');
228 | let hasThisAssignmentAndStaticMethods = THIS_ASSIGNMENT_PATTERN.test(fn + "") && Object.getOwnPropertyNames(fn).length > 0;
229 |
230 | if (hasMethods || hasMethodsOtherThanConstructor ||
231 | hasThisAssignmentAndStaticMethods) {
232 | return true;
233 | }
234 | }
235 | return false;
236 | } catch (e) {
237 | return false;
238 | }
239 | }
240 |
241 | // Used when promisifying a function
242 | function copyFunctionProps(from, to) {
243 | Object.getOwnPropertyNames(from)
244 | .filter(key => !NO_COPY_PROPS.includes(key))
245 | .map(key => ([ key, Object.getOwnPropertyDescriptor(from, key) ]))
246 | .forEach(([ key, propertyDescriptor ]) => Object.defineProperty(to, key, propertyDescriptor));
247 | }
248 |
249 | function isPrimitive(val) {
250 | return val == null || val === true || val === false ||
251 | typeof val === "string" || typeof val === "number";
252 | }
253 |
254 | function ensureIsError(err) {
255 | if (!isPrimitive(err)) return err;
256 |
257 | let message;
258 |
259 | try {
260 | message = err + "";
261 | } catch (e) {
262 | message = "[no string representation]";
263 | }
264 |
265 | return new Error(message);
266 | }
267 |
--------------------------------------------------------------------------------
/tests/helpers/util.js:
--------------------------------------------------------------------------------
1 | var assert = require("assert");
2 | var token = {};
3 | module.exports = {
4 | awaitGlobalException: function(fn) {
5 | function replaceListeners(by) {
6 | var single = typeof by === "function";
7 | if (process.title === "browser") {
8 | var original = window.onerror;
9 | window.onerror = single ? function(message, file, line, column, e) {
10 | return by(e);
11 | } : by[0];
12 | return [original];
13 | } else {
14 | var original = process.listeners("uncaughtException");
15 | process.removeAllListeners("uncaughtException");
16 | if (single) by = [by];
17 | by.forEach(function(listener) {
18 | process.on("uncaughtException", listener);
19 | });
20 | return original;
21 | }
22 | }
23 | return new Promise(function(resolve, reject) {
24 | var listeners = replaceListeners(function(e) {
25 | var err;
26 | var ret;
27 | try {
28 | ret = fn(e);
29 | } catch (e) {
30 | err = e;
31 | }
32 | if (!err && ret === false) return;
33 | replaceListeners(listeners);
34 | Promise.delay(1).then(function() {
35 | if (err) reject(err);
36 | resolve();
37 | });
38 | });
39 | });
40 | },
41 |
42 | awaitLateQueue: function(fn) {
43 | return new Promise(function(res, rej) {
44 | Promise._async.invokeLater(function() {
45 | try {
46 | var result = fn();
47 | res(result);
48 | } catch(e) {
49 | rej(e);
50 | }
51 | }, null, null);
52 | });
53 | },
54 |
55 | awaitProcessExit: function(fn) {
56 | if (typeof process !== "undefined" && typeof process.execPath === "string") {
57 | var exit;
58 | return new Promise(function(resolve, reject) {
59 | exit = process.exit;
60 | process.exit = function(code) {
61 | try {
62 | assert(code != 0);
63 | fn();
64 | resolve();
65 | } catch (e) {
66 | reject(e);
67 | }
68 | };
69 | }).lastly(function() {
70 | process.exit = exit;
71 | });
72 | } else {
73 | return Promise.delay(1);
74 | }
75 | },
76 |
77 | addDeferred: function(Promise) {
78 | Promise.defer = Promise.pending = function() {
79 | var ret = {};
80 | ret.promise = new Promise(function(resolve, reject) {
81 | ret.resolve = ret.fulfill = resolve;
82 | ret.reject = reject;
83 | });
84 | return ret;
85 | };
86 | return Promise;
87 | },
88 |
89 | returnToken: function() {
90 | return token;
91 | },
92 |
93 | assertToken: function(val) {
94 | assert.strictEqual(token, val);
95 | },
96 |
97 | getSpy: function() {
98 | var resolve, reject;
99 | var promise = new Promise(function() {
100 | resolve = arguments[0];
101 | reject = arguments[1];
102 | }).timeout(500);
103 | var ret = function(fn) {
104 | ret.callback = fn;
105 | return ret.node;
106 | };
107 | ret.promise = promise;
108 | ret.node = function() {
109 | try {
110 | ret.callback.apply(this, arguments);
111 | resolve();
112 | } catch (e) {
113 | reject(e);
114 | }
115 | };
116 | return ret;
117 | },
118 |
119 | awaitDomainException: function(onError, fn) {
120 | var domain;
121 | var resolve, reject;
122 | var promise = new Promise(function() {
123 | resolve = arguments[0];
124 | reject = arguments[1];
125 | }).timeout(500);
126 | domain = require('domain').create();
127 | domain.on("error", function(e) {
128 | try {
129 | onError(e);
130 | resolve();
131 | } catch (err) {
132 | reject(err);
133 | }
134 | });
135 | domain.run(fn);
136 | return promise;
137 | },
138 |
139 | //Since there is only a single handler possible at a time, older
140 | //tests that are run just before this file could affect the results
141 | //that's why there is 500ms limit in grunt file between each test
142 | //beacuse the unhandled rejection handler will run within 100ms right now
143 | onUnhandledFail: function(testFunction) {
144 | return new Promise(function(resolve, reject) {
145 | var err = new Error("Reporting handled rejection as unhandled from: " +
146 | testFunction);
147 | Promise.onPossiblyUnhandledRejection(function() {
148 | reject(err);
149 | });
150 | Promise.delay(25).then(resolve);
151 | }).lastly(function() {
152 | Promise.onPossiblyUnhandledRejection(null);
153 | });
154 | },
155 |
156 | onUnhandledSucceed: function(testAgainst, count) {
157 | return new Promise(function(resolveTest, reject) {
158 | var total = typeof count === "number" ? count : 1;
159 | var cur = 0;
160 |
161 | function resolve(e) {
162 | cur++;
163 | if (cur >= total) {
164 | resolveTest(e);
165 | }
166 | }
167 |
168 | Promise.onPossiblyUnhandledRejection(function(e){
169 | if (testAgainst !== undefined) {
170 | try {
171 | if (typeof testAgainst === "function") {
172 | assert(testAgainst(e));
173 | }
174 | else {
175 | assert.equal(testAgainst, e);
176 | }
177 | resolve(e);
178 | }
179 | catch (e) {
180 | reject(e);
181 | }
182 | } else {
183 | resolve(e);
184 | }
185 | });
186 | Promise.delay(200).then(function() {
187 | var message = "Expected onPossiblyUnhandledRejection to be called " +
188 | total + " times but it was only called " + cur + " times";
189 | reject(new Error(message));
190 | });
191 | }).lastly(function() {
192 | Promise.onPossiblyUnhandledRejection(null);
193 | });
194 |
195 | },
196 |
197 | //Used in expressions like: onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
198 | //If strict mode is supported NFEs work, if it is not, NFEs don't work but arguments.callee does
199 | isStrictModeSupported: (function() {
200 | try {
201 | new Function("'use strict'; with({});");
202 | return false;
203 | }
204 | catch (e) {
205 | return true;
206 | }
207 | })(),
208 |
209 | noop: function(v) {
210 | return v;
211 | },
212 |
213 | isSubset: function(subset, superset) {
214 | var i, subsetLen;
215 |
216 | subsetLen = subset.length;
217 |
218 | if (subsetLen > superset.length) {
219 | return false;
220 | }
221 |
222 | for(i = 0; i -1;
233 | },
234 |
235 | fakeResolved: function(val) {
236 | return {
237 | then: function(callback) {
238 | return fakeResolved(callback ? callback(val) : val);
239 | }
240 | };
241 | },
242 |
243 | fakeRejected: function(reason) {
244 | return {
245 | then: function(callback, errback) {
246 | return errback ? fakeResolved(errback(reason)) : fakeRejected(reason);
247 | }
248 | };
249 | },
250 |
251 | assertFulfilled: function(p, v) {
252 | assert.strictEqual(p.value(), v);
253 | },
254 |
255 | assertRejected: function(p, v) {
256 | assert.strictEqual(p.error(), v);
257 | },
258 |
259 | ecmaScript6Collections: (typeof Set === "function" &&
260 | typeof Symbol !== "undefined" &&
261 | Symbol.iterator &&
262 | typeof ((new Set())[Symbol.iterator]().next) === "function"),
263 |
264 | ecmaScript5: (function() {"use strict"
265 | return this === undefined;
266 | })(),
267 | isNodeJS: typeof process !== "undefined" && typeof process.execPath === "string"
268 | };
269 |
270 | if (module.exports.isNodeJS) {
271 | var version = process.versions.node.split(".").map(Number);
272 | module.exports.isRecentNode = version[0] > 0;
273 | module.exports.isOldNode = !module.exports.isRecentNode;
274 | } else {
275 | module.exports.isOldNode = false;
276 | module.exports.isRecentNode = false;
277 | }
278 |
--------------------------------------------------------------------------------
/tests/spread.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var assert = require("assert");
3 | var testUtils = require("./helpers/util.js");
4 |
5 | const Promise = require('../promise');
6 | /*!
7 | *
8 | Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
9 | Permission is hereby granted, free of charge, to any person obtaining a copy
10 | of this software and associated documentation files (the "Software"), to
11 | deal in the Software without restriction, including without limitation the
12 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13 | sell copies of the Software, and to permit persons to whom the Software is
14 | furnished to do so, subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be included in
17 | all copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 | IN THE SOFTWARE.
26 | */
27 |
28 | describe("spread", function () {
29 | it("spreads values across arguments", function () {
30 | return Promise.resolve([1, 2, 3]).spread(function (a, b) {
31 | assert.equal(b,2);
32 | });
33 | });
34 |
35 | it("spreads promises for arrays across arguments", function () {
36 | return Promise.resolve([Promise.resolve(10)])
37 | .all()
38 | .spread(function (value) {
39 | assert.equal(value,10);
40 | });
41 | });
42 |
43 | it("spreads arrays of promises across arguments", function () {
44 | var deferredA = Promise.defer();
45 | var deferredB = Promise.defer();
46 |
47 | var promise = Promise.resolve([deferredA.promise, deferredB.promise]).all().spread(
48 | function (a, b) {
49 | assert.equal(a,10);
50 | assert.equal(b,20);
51 | });
52 |
53 | Promise.delay(1).then(function () {
54 | deferredA.resolve(10);
55 | });
56 | Promise.delay(1).then(function () {
57 | deferredB.resolve(20);
58 | });
59 |
60 | return promise;
61 | });
62 |
63 | it("spreads arrays of thenables across arguments", function () {
64 | var p1 = {
65 | then: function(v) {
66 | v(10);
67 | }
68 | };
69 | var p2 = {
70 | then: function(v) {
71 | v(20);
72 | }
73 | };
74 |
75 | var promise = Promise.resolve([p1, p2]).all().spread(function (a, b) {
76 | assert.equal(a,10);
77 | assert.equal(b,20);
78 | });
79 | return promise;
80 | });
81 |
82 | it("should wait for promises in the returned array even when not calling .all", function() {
83 | var d1 = Promise.defer();
84 | var d2 = Promise.defer();
85 | var d3 = Promise.defer();
86 | setTimeout(function(){
87 | d1.resolve(1);
88 | d2.resolve(2);
89 | d3.resolve(3);
90 | }, 1);
91 | return Promise.resolve().then(function(){
92 | return [d1.promise, d2.promise, d3.promise];
93 | }).all().spread(function(a, b, c){
94 | assert(a === 1);
95 | assert(b === 2);
96 | assert(c === 3);
97 | });
98 | });
99 |
100 | it("should wait for thenables in the returned array even when not calling .all", function() {
101 | var t1 = {
102 | then: function(fn) {
103 | setTimeout(function(){
104 | fn(1);
105 | }, 1);
106 | }
107 | };
108 | var t2 = {
109 | then: function(fn) {
110 | setTimeout(function(){
111 | fn(2);
112 | }, 1);
113 | }
114 | };
115 | var t3 = {
116 | then: function(fn) {
117 | setTimeout(function(){
118 | fn(3);
119 | }, 1);
120 | }
121 | };
122 | return Promise.resolve().then(function(){
123 | return [t1, t2, t3];
124 | }).all().spread(function(a, b, c){
125 | assert(a === 1);
126 | assert(b === 2);
127 | assert(c === 3);
128 | });
129 | });
130 |
131 | it("should wait for promises in an array that a returned promise resolves to even when not calling .all", function() {
132 | var d1 = Promise.defer();
133 | var d2 = Promise.defer();
134 | var d3 = Promise.defer();
135 | var defer = Promise.defer();
136 |
137 | setTimeout(function(){
138 | defer.resolve([d1.promise, d2.promise, d3.promise]);
139 | setTimeout(function(){
140 | d1.resolve(1);
141 | d2.resolve(2);
142 | d3.resolve(3);
143 | }, 1);
144 | }, 1);
145 |
146 | return Promise.resolve().then(function(){
147 | return defer.promise;
148 | }).all().spread(function(a, b, c){
149 | assert(a === 1);
150 | assert(b === 2);
151 | assert(c === 3);
152 | });
153 | });
154 |
155 | it("should wait for thenables in an array that a returned thenable resolves to even when not calling .all", function() {
156 | var t1 = {
157 | then: function(fn) {
158 | setTimeout(function(){
159 | fn(1);
160 | }, 1);
161 | }
162 | };
163 | var t2 = {
164 | then: function(fn) {
165 | setTimeout(function(){
166 | fn(2);
167 | }, 1);
168 | }
169 | };
170 | var t3 = {
171 | then: function(fn) {
172 | setTimeout(function(){
173 | fn(3);
174 | }, 1);
175 | }
176 | };
177 |
178 | var thenable = {
179 | then: function(fn) {
180 | setTimeout(function(){
181 | fn([t1, t2, t3])
182 | }, 1);
183 | }
184 | };
185 |
186 | return Promise.resolve().then(function(){
187 | return thenable;
188 | }).all().spread(function(a, b, c){
189 | assert(a === 1);
190 | assert(b === 2);
191 | assert(c === 3);
192 | });
193 | });
194 |
195 | it("should reject with error when non array is the ultimate value to be spread", function(){
196 | return Promise.resolve().then(function(){
197 | return 3
198 | }).spread(function(a, b, c){
199 | assert.fail();
200 | }).then(assert.fail, function(e){
201 | })
202 | });
203 |
204 | specify("gh-235", function() {
205 | var P = Promise;
206 | return P.resolve(1).then(function(x) {
207 | return [x, P.resolve(2)]
208 | }).spread(function(x, y) {
209 | return P.all([P.resolve(3), P.resolve(4)]);
210 | }).then(function(a) {
211 | assert.deepEqual([3, 4], a);
212 | });
213 | })
214 |
215 | specify("error when passed non-function", function() {
216 | return Promise.resolve(3)
217 | .spread()
218 | .then(assert.fail)
219 | .caught(Promise.TypeError, function() {});
220 | });
221 |
222 | specify("error when resolution is non-spredable", function() {
223 | return Promise.resolve(3)
224 | .spread(function(){})
225 | .then(assert.fail)
226 | .caught(Promise.TypeError, function() {});
227 | });
228 | });
229 |
230 | /*
231 | Based on When.js tests
232 |
233 | Open Source Initiative OSI - The MIT License
234 |
235 | http://www.opensource.org/licenses/mit-license.php
236 |
237 | Copyright (c) 2011 Brian Cavalier
238 |
239 | Permission is hereby granted, free of charge, to any person obtaining
240 | a copy of this software and associated documentation files (the
241 | "Software"), to deal in the Software without restriction, including
242 | without limitation the rights to use, copy, modify, merge, publish,
243 | distribute, sublicense, and/or sell copies of the Software, and to
244 | permit persons to whom the Software is furnished to do so, subject to
245 | the following conditions:
246 |
247 | The above copyright notice and this permission notice shall be
248 | included in all copies or substantial portions of the Software.
249 |
250 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
251 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
252 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
253 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
254 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
255 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
256 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
257 |
258 | describe("Promise.spread-test", function () {
259 | var slice = [].slice;
260 |
261 | specify("should return a promise", function() {
262 | assert(typeof (Promise.defer().promise.spread(function(){}).then) === "function");
263 | });
264 |
265 | specify("should apply onFulfilled with array as argument list", function() {
266 | var expected = [1, 2, 3];
267 | return Promise.resolve(expected).spread(function() {
268 | assert.deepEqual(slice.call(arguments), expected);
269 | });
270 | });
271 |
272 | specify("should resolve array contents", function() {
273 | var expected = [Promise.resolve(1), 2, Promise.resolve(3)];
274 | return Promise.resolve(expected).all().spread(function() {
275 | assert.deepEqual(slice.call(arguments), [1, 2, 3]);
276 | });
277 | });
278 |
279 | specify("should reject if any item in array rejects", function() {
280 | var expected = [Promise.resolve(1), 2, Promise.reject(3)];
281 | return Promise.resolve(expected).all()
282 | .spread(assert.fail)
283 | .then(assert.fail, function() {});
284 | });
285 |
286 | specify("should apply onFulfilled with array as argument list", function() {
287 | var expected = [1, 2, 3];
288 | return Promise.resolve(Promise.resolve(expected)).spread(function() {
289 | assert.deepEqual(slice.call(arguments), expected);
290 | });
291 | });
292 |
293 | specify("should resolve array contents", function() {
294 | var expected = [Promise.resolve(1), 2, Promise.resolve(3)];
295 | return Promise.resolve(Promise.resolve(expected)).all().spread(function() {
296 | assert.deepEqual(slice.call(arguments), [1, 2, 3]);
297 | });
298 | });
299 |
300 | specify("should reject if input is a rejected promise", function() {
301 | var expected = Promise.reject([1, 2, 3]);
302 | return Promise.resolve(expected)
303 | .spread(assert.fail)
304 | .then(assert.fail, function() {});
305 | });
306 | });
--------------------------------------------------------------------------------
/tests/catchFilter.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const assert = require("assert");
4 | const testUtils = require("./helpers/util.js");
5 | const Promise = require("../promise.js");
6 |
7 | testUtils.addDeferred(Promise);
8 |
9 | var CustomError = function(){};
10 |
11 | CustomError.prototype = new Error();
12 |
13 | var predicateFilter = function(e) {
14 | return (/invalid/).test(e.message);
15 | }
16 |
17 | function BadError(msg) {
18 | this.message = msg;
19 | return this;
20 | }
21 |
22 | function predicatesUndefined(e) {
23 | return e === void 0;
24 | }
25 |
26 | function predicatesPrimitiveString(e) {
27 | return /^asd$/.test(e);
28 | }
29 |
30 | var token = {};
31 | var returnToken = function() {
32 | return token;
33 | };
34 |
35 | var assertToken = function(val) {
36 | assert.strictEqual(token, val);
37 | };
38 |
39 | describe("A promise handler that throws a TypeError must be caught", function() {
40 |
41 | specify("in a middle.caught filter", function() {
42 | var a = Promise.defer();
43 | a.fulfill(3);
44 | return a.promise.then(function(){
45 | a.b.c.d()
46 | }).then(assert.fail).caught(SyntaxError, function(e){
47 | assert.fail();
48 | }).caught(Promise.TypeError, returnToken)
49 | .then(assertToken);
50 | });
51 |
52 |
53 | specify("in a generic.caught filter that comes first", function() {
54 | var a = Promise.defer();
55 |
56 | a.fulfill(3);
57 | return a.promise.then(function(){
58 | a.b.c.d()
59 | }).then(assert.fail, returnToken).caught(SyntaxError, function(e){
60 | assert.fail();
61 | }).caught(Promise.TypeError, function(e){
62 | assert.fail();
63 | }).then(assertToken);
64 |
65 | });
66 |
67 | specify("in an explicitly generic.caught filter that comes first", function() {
68 | var a = Promise.defer();
69 | a.fulfill(3);
70 |
71 | return a.promise.then(function(){
72 | a.b.c.d()
73 | })
74 | .then(assert.fail)
75 | .caught(Error, returnToken)
76 | .caught(SyntaxError, assert.fail)
77 | .caught(Promise.TypeError, assert.fail)
78 | .then(assertToken);
79 | });
80 |
81 | specify("in a specific handler after thrown in generic", function() {
82 | var a = Promise.defer();
83 | a.fulfill(3);
84 |
85 | return a.promise.then(function(){
86 | a.b.c.d()
87 | }).then(assert.fail, function(e){
88 | throw e
89 | }).caught(SyntaxError, assert.fail)
90 | .then(assert.fail)
91 | .caught(Promise.TypeError, returnToken)
92 | .then(assertToken);
93 |
94 |
95 | });
96 |
97 |
98 | specify("in a multi-filter handler", function() {
99 | var a = Promise.defer();
100 | a.fulfill(3);
101 |
102 | return a.promise.then(function(){
103 | a.b.c.d()
104 | })
105 | .then(assert.fail)
106 | .caught(SyntaxError, TypeError, returnToken)
107 | .then(assertToken);
108 | });
109 |
110 |
111 | specify("in a specific handler after non-matching multi.caught handler", function() {
112 | var a = Promise.defer();
113 | a.fulfill(3);
114 |
115 | return a.promise.then(function(){
116 | a.b.c.d()
117 | })
118 | .then(assert.fail)
119 | .caught(SyntaxError, CustomError, assert.fail)
120 | .caught(Promise.TypeError, returnToken)
121 | .then(assertToken)
122 | });
123 |
124 | });
125 |
126 |
127 | describe("A promise handler that throws a custom error", function() {
128 |
129 | specify("Is filtered if inheritance was done even remotely properly", function() {
130 | var a = Promise.defer();
131 | var b = new CustomError();
132 | a.fulfill(3);
133 | return a.promise.then(function(){
134 | throw b;
135 | })
136 | .then(assert.fail)
137 | .caught(SyntaxError, assert.fail)
138 | .caught(Promise.TypeError, assert.fail)
139 | .caught(CustomError, function(e){
140 | assert.equal(e, b);
141 | return token;
142 | })
143 | .then(assertToken);
144 |
145 |
146 | });
147 |
148 | specify("Is filtered along with built-in errors", function() {
149 | var a = Promise.defer();
150 | var b = new CustomError();
151 | a.fulfill(3);
152 | return a.promise.then(function(){
153 | throw b;
154 | })
155 | .then(assert.fail)
156 | .caught(Promise.TypeError, SyntaxError, CustomError, returnToken)
157 | .caught(assert.fail)
158 | .then(assertToken)
159 | });
160 |
161 | specify("Throws after matched type handler throws", function() {
162 | var err = new Promise.TypeError();
163 | var err2 = new Error();
164 | return Promise.reject(err).caught(Promise.TypeError, function() {
165 | throw err2;
166 | }).then(assert.fail, function(e) {
167 | assert.strictEqual(err2, e);
168 | });
169 | });
170 | });
171 |
172 | describe("A promise handler that throws a CustomError must be caught", function() {
173 | specify("in a middle.caught filter", function() {
174 | var a = Promise.defer();
175 | a.fulfill(3);
176 |
177 | return a.promise.then(function(){
178 | throw new CustomError()
179 | })
180 | .caught(SyntaxError, assert.fail)
181 | .caught(CustomError, returnToken)
182 | .then(assertToken);
183 | });
184 |
185 |
186 | specify("in a generic.caught filter that comes first", function() {
187 | var a = Promise.defer();
188 | a.fulfill(3);
189 |
190 | return a.promise.then(function(){
191 | throw new CustomError()
192 | }).then(assert.fail, returnToken)
193 | .caught(SyntaxError, assert.fail)
194 | .caught(CustomError, assert.fail)
195 | .then(assertToken)
196 | });
197 |
198 | specify("in an explicitly generic.caught filter that comes first", function() {
199 | var a = Promise.defer();
200 | a.fulfill(3);
201 |
202 | return a.promise.then(function(){
203 | throw new CustomError()
204 | })
205 | .caught(Error, returnToken)
206 | .caught(SyntaxError, assert.fail)
207 | .caught(CustomError, assert.fail)
208 | .then(assertToken);
209 | });
210 |
211 | specify("in a specific handler after thrown in generic", function() {
212 | var a = Promise.defer();
213 | a.fulfill(3);
214 |
215 | return a.promise.then(function(){
216 | throw new CustomError()
217 | }).then(assert.fail, function(e){
218 | throw e
219 | })
220 | .caught(SyntaxError, assert.fail)
221 | .caught(CustomError, returnToken)
222 | .then(assertToken);
223 |
224 | });
225 |
226 |
227 | specify("in a multi-filter handler", function() {
228 | var a = Promise.defer();
229 | a.fulfill(3);
230 |
231 | return a.promise.then(function(){
232 | throw new CustomError()
233 | })
234 | .caught(SyntaxError, CustomError, returnToken)
235 | .then(assertToken)
236 |
237 | });
238 |
239 |
240 | specify("in a specific handler after non-matching multi.caught handler", function() {
241 | var a = Promise.defer();
242 | a.fulfill(3);
243 |
244 | return a.promise.then(function(){
245 | throw new CustomError()
246 | })
247 | .caught(SyntaxError, TypeError, assert.fail)
248 | .caught(CustomError, returnToken)
249 | .then(assertToken);
250 | });
251 |
252 | });
253 |
254 | describe("A promise handler that is caught in a filter", function() {
255 |
256 | specify("is continued normally after returning a promise in filter", function() {
257 | var a = Promise.defer();
258 | var c = Promise.defer();
259 | var b = new CustomError();
260 | a.fulfill(3);
261 | setTimeout(function(){
262 | c.fulfill(3);
263 | }, 1);
264 | return a.promise.then(function(){
265 | throw b;
266 | }).caught(SyntaxError, function(e){
267 | assert.fail();
268 | }).caught(Promise.TypeError, function(e){
269 | assert.fail();
270 | }).caught(CustomError, function(e){
271 | assert.equal(e, b);
272 | return c.promise.thenReturn(token);
273 | }).then(assertToken, assert.fail, assert.fail);
274 | });
275 |
276 | specify("is continued normally after returning a promise in original handler", function() {
277 | var a = Promise.defer();
278 | var c = Promise.defer();
279 | a.fulfill(3);
280 | setTimeout(function(){
281 | c.fulfill(3);
282 | }, 1);
283 | return a.promise.then(function(){
284 | return c.promise;
285 | }).caught(SyntaxError, function(e){
286 | assert.fail();
287 | }).caught(Promise.TypeError, function(e){
288 | assert.fail();
289 | }).caught(CustomError, function(e){
290 | assert.fail();
291 | });
292 |
293 | });
294 | });
295 |
296 | describe("A promise handler with a predicate filter", function() {
297 |
298 | specify("will catch a thrown thing matching the filter", function() {
299 | var a = Promise.defer();
300 | a.fulfill(3);
301 | return a.promise.then(function(){
302 | throw new Error("horrible invalid error string");
303 | }).caught(predicateFilter, returnToken)
304 | .then(assertToken);
305 |
306 | });
307 | specify("will NOT catch a thrown thing not matching the filter", function() {
308 | var a = Promise.defer();
309 | a.fulfill(3);
310 | return a.promise.then(function(){
311 | throw new Error("horrible valid error string");
312 | }).caught(predicateFilter, function(e){
313 | assert.fail();
314 | }).then(assert.fail, function(){})
315 | });
316 |
317 | specify("will assert.fail when a predicate is a bad error class", function() {
318 | var a = Promise.defer();
319 | a.fulfill(3);
320 | return a.promise.then(function(){
321 | throw new Error("horrible custom error");
322 | }).caught(BadError, function(e){
323 | assert.fail();
324 | }).then(assert.fail, returnToken)
325 | .then(assertToken);
326 |
327 | });
328 |
329 | specify("will catch a thrown undefiend", function(){
330 | var a = Promise.defer();
331 | a.fulfill(3);
332 | return a.promise.then(function(){
333 | throw void 0;
334 | }).caught(function(e) { return false }, function(e){
335 | assert.fail();
336 | }).caught(predicatesUndefined, returnToken)
337 | .then(assertToken);
338 |
339 | });
340 |
341 | specify("will catch a thrown string", function(){
342 | var a = Promise.defer();
343 | a.fulfill(3);
344 | return a.promise.then(function(){
345 | throw "asd";
346 | }).caught(function(e) { return false }, function(e){
347 | assert.fail();
348 | }).caught(predicatesPrimitiveString, returnToken)
349 | .then(assertToken);
350 |
351 | });
352 |
353 | specify("will assert.fail when a predicate throws", function() {
354 | var a = Promise.defer();
355 | a.fulfill(3);
356 | return a.promise.then(function(){
357 | throw new CustomError("error happens");
358 | }).then(assert.fail, function(e) { return e.f.g; }, function(e){
359 | assert.fail();
360 | }).caught(TypeError, returnToken)
361 | .then(assertToken);
362 | });
363 | });
364 |
365 | describe("object property predicates", function() {
366 | specify("matches 1 property loosely", function() {
367 | var e = new Error();
368 | e.code = "3";
369 | return Promise.resolve()
370 | .then(function() {
371 | throw e;
372 | })
373 | .caught({code: 3}, function(err) {
374 | assert.equal(e, err);
375 | });
376 | });
377 |
378 | specify("matches 2 properties loosely", function() {
379 | var e = new Error();
380 | e.code = "3";
381 | e.code2 = "3";
382 | return Promise.resolve()
383 | .then(function() {
384 | throw e;
385 | })
386 | .caught({code: 3, code2: 3}, function(err) {
387 | assert.equal(e, err);
388 | });
389 | });
390 |
391 | specify("doesn't match inequal properties", function() {
392 | var e = new Error();
393 | e.code = "3";
394 | e.code2 = "4";
395 | return Promise.resolve()
396 | .then(function() {
397 | throw e;
398 | })
399 | .caught({code: 3, code2: 3}, function(err) {
400 | assert.fail();
401 | })
402 | .caught(function(v) {return v === e}, function() {});
403 | });
404 |
405 | specify("doesn't match primitives even if the property matches", function() {
406 | var e = "string";
407 | var length = e.length;
408 | return Promise.resolve()
409 | .then(function() {
410 | throw e;
411 | })
412 | .caught({length: length}, function(err) {
413 | assert.fail();
414 | })
415 | .caught(function(v) {return typeof v === "string"}, function(err) {
416 |
417 | assert.equal(e, err);
418 | });
419 | });
420 | });
421 |
--------------------------------------------------------------------------------
/tests/reduce.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var assert = require("assert");
4 | const Promise = require("../promise.js");
5 | //var testUtils = require("./helpers/util.js");
6 |
7 |
8 | function promised(val) {
9 | return new Promise(function(f) {
10 | setTimeout(function() {
11 | f(val);
12 | }, 1);
13 | });
14 | }
15 | function promising(val) {
16 | return function() {
17 | return promised(val);
18 | }
19 | }
20 | function promisingThen(val) {
21 | return function() {
22 | return promised(val).then(function(resolved) {
23 | return resolved;
24 | });
25 | }
26 | }
27 |
28 | function thenabled(val) {
29 | return {
30 | then: function(f){
31 | setTimeout(function() {
32 | f(val);
33 | }, 1);
34 | }
35 | };
36 | }
37 | function thenabling(val) {
38 | return function() { return thenabled(val); }
39 | }
40 |
41 | function evaluate(val) {
42 | if (typeof val === 'function') {
43 | val = val();
44 | }
45 | if (Array.isArray(val)) {
46 | val = val.map(function(member) {
47 | return evaluate(member);
48 | });
49 | }
50 | return val;
51 | }
52 |
53 |
54 | var ACCUM_CRITERIA = [
55 | { value: 0, desc: "that is resolved" },
56 | { value: promising(0), desc: "as a Promise" },
57 | { value: promisingThen(0), desc: "as a deferred Promise" },
58 | { value: thenabling(0), desc: "as a thenable" },
59 | ];
60 |
61 | var VALUES_CRITERIA = [
62 | { value: [], total: 0, desc: "and no values" },
63 | { value: [ 1 ], total: 1, desc: "and a single resolved value" },
64 | { value: [ 1, 2, 3 ], total: 6, desc: "and multiple resolved values" },
65 | { value: [ promising(1) ], total: 1, desc: "and a single Promise" },
66 | { value: [
67 | promising(1),
68 | promising(2),
69 | promising(3)
70 | ], total: 6, desc: "and multiple Promises" },
71 | { value: [
72 | promisingThen(1)
73 | ], total: 1, desc: "and a single deferred Promise" },
74 | { value: [
75 | promisingThen(1),
76 | promisingThen(2),
77 | promisingThen(3)
78 | ], total: 6, desc: "and multiple deferred Promises" },
79 | { value: [
80 | thenabling(1)
81 | ], total: 1, desc: "and a single thenable" },
82 | { value: [
83 | thenabling(1),
84 | thenabling(2),
85 | thenabling(3)
86 | ], total: 6, desc: "and multiple thenables" },
87 | { value: [
88 | thenabling(1),
89 | promisingThen(2),
90 | promising(3),
91 | 4
92 | ], total: 10, desc: "and a blend of values" },
93 | ];
94 |
95 | var ERROR = new Error("BOOM");
96 |
97 |
98 | describe("Promise.prototype.reduce", function() {
99 | it("works with no values", function() {
100 | return Promise.resolve([]).reduce(function(total, value) {
101 | return total + value + 5;
102 | }).then(function(total) {
103 | assert.strictEqual(total, undefined);
104 | });
105 | });
106 |
107 | it("works with a single value", function() {
108 | return Promise.resolve([ 1 ]).reduce(function(total, value) {
109 | return total + value + 5;
110 | }).then(function(total) {
111 | assert.strictEqual(total, 1);
112 | });
113 | });
114 |
115 | it("works when the iterator returns a value", function() {
116 | return Promise.resolve([ 1, 2, 3 ]).reduce(function(total, value) {
117 | return total + value + 5;
118 | }).then(function(total) {
119 | assert.strictEqual(total, (1 + 2+5 + 3+5));
120 | });
121 | });
122 |
123 | it("works when the iterator returns a Promise", function() {
124 | return Promise.resolve([ 1, 2, 3 ]).reduce(function(total, value) {
125 | return promised(5).then(function(bonus) {
126 | return total + value + bonus;
127 | });
128 | }).then(function(total) {
129 | assert.strictEqual(total, (1 + 2+5 + 3+5));
130 | });
131 | });
132 |
133 | it("works when the iterator returns a thenable", function() {
134 | return Promise.resolve([ 1, 2, 3 ]).reduce(function(total, value) {
135 | return thenabled(total + value + 5);
136 | }).then(function(total) {
137 | assert.strictEqual(total, (1 + 2+5 + 3+5));
138 | });
139 | });
140 | });
141 |
142 | describe("Promise.reduce", function() {
143 |
144 | it("should allow returning values", function() {
145 | var a = [promised(1), promised(2), promised(3)];
146 |
147 | return Promise.reduce(a, function(total, a) {
148 | return total + a + 5;
149 | }, 0).then(function(total){
150 | assert.equal(total, 1+5 + 2+5 + 3+5);
151 | });
152 | });
153 |
154 | it("should allow returning promises", function() {
155 | var a = [promised(1), promised(2), promised(3)];
156 |
157 | return Promise.reduce(a, function(total, a) {
158 | return promised(5).then(function(b) {
159 | return total + a + b;
160 | });
161 | }, 0).then(function(total){
162 | assert.equal(total, 1+5 + 2+5 + 3+5);
163 | });
164 | });
165 |
166 | it("should allow returning thenables", function() {
167 | var b = [1,2,3];
168 | var a = [];
169 |
170 | return Promise.reduce(b, function(total, cur) {
171 | a.push(cur);
172 | return thenabled(3);
173 | }, 0).then(function(total) {
174 | assert.equal(total, 3);
175 | assert.deepEqual(a, b);
176 | });
177 | });
178 |
179 | it("propagates error", function() {
180 | var a = [promised(1), promised(2), promised(3)];
181 | var e = new Error("asd");
182 | return Promise.reduce(a, function(total, a) {
183 | if (a > 2) {
184 | throw e;
185 | }
186 | return total + a + 5;
187 | }, 0).then(assert.fail, function(err) {
188 | assert.equal(err, e);
189 | });
190 | });
191 |
192 | describe("with no initial accumulator or values", function() {
193 | it("works when the iterator returns a value", function() {
194 | return Promise.reduce([], function(total, value) {
195 | return total + value + 5;
196 | }).then(function(total){
197 | assert.strictEqual(total, undefined);
198 | });
199 | });
200 |
201 | it("works when the iterator returns a Promise", function() {
202 | return Promise.reduce([], function(total, value) {
203 | return promised(5).then(function(bonus) {
204 | return total + value + bonus;
205 | });
206 | }).then(function(total){
207 | assert.strictEqual(total, undefined);
208 | });
209 | });
210 |
211 | it("works when the iterator returns a thenable", function() {
212 | return Promise.reduce([], function(total, value) {
213 | return thenabled(total + value + 5);
214 | }).then(function(total){
215 | assert.strictEqual(total, undefined);
216 | });
217 | });
218 | });
219 |
220 | describe("with an initial accumulator value", function() {
221 | ACCUM_CRITERIA.forEach(function(criteria) {
222 | var initial = criteria.value;
223 |
224 | describe(criteria.desc, function() {
225 | VALUES_CRITERIA.forEach(function(criteria) {
226 | var values = criteria.value;
227 | var valueTotal = criteria.total;
228 |
229 | describe(criteria.desc, function() {
230 | it("works when the iterator returns a value", function() {
231 | return Promise.reduce(evaluate(values), function(total, value) {
232 | return total + value + 5;
233 | }, evaluate(initial)).then(function(total){
234 | assert.strictEqual(total, valueTotal + (values.length * 5));
235 | });
236 | });
237 |
238 | it("works when the iterator returns a Promise", function() {
239 | return Promise.reduce(evaluate(values), function(total, value) {
240 | return promised(5).then(function(bonus) {
241 | return total + value + bonus;
242 | });
243 | }, evaluate(initial)).then(function(total){
244 | assert.strictEqual(total, valueTotal + (values.length * 5));
245 | });
246 | });
247 |
248 | it("works when the iterator returns a thenable", function() {
249 | return Promise.reduce(evaluate(values), function(total, value) {
250 | return thenabled(total + value + 5);
251 | }, evaluate(initial)).then(function(total){
252 | assert.strictEqual(total, valueTotal + (values.length * 5));
253 | });
254 | });
255 | });
256 | });
257 | });
258 | });
259 |
260 | it("propagates an initial Error", function() {
261 | var initial = Promise.reject(ERROR);
262 | var values = [
263 | thenabling(1),
264 | promisingThen(2)(),
265 | promised(3),
266 | 4
267 | ];
268 |
269 | return Promise.reduce(values, function(total, value) {
270 | return value;
271 | }, initial).then(assert.fail, function(err) {
272 | assert.equal(err, ERROR);
273 | });
274 | });
275 |
276 | it("propagates a value's Error", function() {
277 | var initial = 0;
278 | var values = [
279 | thenabling(1),
280 | promisingThen(2)(),
281 | Promise.reject(ERROR),
282 | promised(3),
283 | 4
284 | ];
285 |
286 | return Promise.reduce(values, function(total, value) {
287 | return value;
288 | }, initial).then(assert.fail, function(err) {
289 | assert.equal(err, ERROR);
290 | });
291 | });
292 |
293 | it("propagates an Error from the iterator", function() {
294 | var initial = 0;
295 | var values = [
296 | thenabling(1),
297 | promisingThen(2)(),
298 | promised(3),
299 | 4
300 | ];
301 |
302 | return Promise.reduce(values, function(total, value) {
303 | if (value === 2) {
304 | throw ERROR;
305 | }
306 | return value;
307 | }, initial).then(assert.fail, function(err) {
308 | assert.equal(err, ERROR);
309 | });
310 | });
311 | });
312 |
313 | describe("with a 0th value acting as an accumulator", function() {
314 | it("acts this way when an accumulator value is provided yet `undefined`", function() {
315 | return Promise.reduce([ 1, 2, 3 ], function(total, value) {
316 | return ((total === void 0) ? 0 : total) + value + 5;
317 | }, undefined).then(function(total){
318 | assert.strictEqual(total, (1 + 2+5 + 3+5));
319 | });
320 | });
321 |
322 | it("survives an `undefined` 0th value", function() {
323 | return Promise.reduce([ undefined, 1, 2, 3 ], function(total, value) {
324 | return ((total === void 0) ? 0 : total) + value + 5;
325 | }).then(function(total){
326 | assert.strictEqual(total, (1+5 + 2+5 + 3+5));
327 | });
328 | });
329 |
330 | ACCUM_CRITERIA.forEach(function(criteria) {
331 | var zeroth = criteria.value;
332 |
333 | describe(criteria.desc, function() {
334 | VALUES_CRITERIA.forEach(function(criteria) {
335 | var values = criteria.value;
336 | var zerothAndValues = [ zeroth ].concat(values);
337 | var valueTotal = criteria.total;
338 |
339 | describe(criteria.desc, function() {
340 | it("works when the iterator returns a value", function() {
341 | return Promise.reduce(evaluate(zerothAndValues), function(total, value) {
342 | return total + value + 5;
343 | }).then(function(total){
344 | assert.strictEqual(total, valueTotal + (values.length * 5));
345 | });
346 | });
347 |
348 | it("works when the iterator returns a Promise", function() {
349 | return Promise.reduce(evaluate(zerothAndValues), function(total, value) {
350 | return promised(5).then(function(bonus) {
351 | return total + value + bonus;
352 | });
353 | }).then(function(total){
354 | assert.strictEqual(total, valueTotal + (values.length * 5));
355 | });
356 | });
357 |
358 | it("works when the iterator returns a thenable", function() {
359 | return Promise.reduce(evaluate(zerothAndValues), function(total, value) {
360 | return thenabled(total + value + 5);
361 | }).then(function(total){
362 | assert.strictEqual(total, valueTotal + (values.length * 5));
363 | });
364 | });
365 | });
366 | });
367 | });
368 | });
369 |
370 | it("propagates an initial Error", function() {
371 | var values = [
372 | Promise.reject(ERROR),
373 | thenabling(1),
374 | promisingThen(2)(),
375 | promised(3),
376 | 4
377 | ];
378 |
379 | return Promise.reduce(values, function(total, value) {
380 | return value;
381 | }).then(assert.fail, function(err) {
382 | assert.equal(err, ERROR);
383 | });
384 | });
385 |
386 | it("propagates a value's Error", function() {
387 | var values = [
388 | 0,
389 | thenabling(1),
390 | promisingThen(2)(),
391 | Promise.reject(ERROR),
392 | promised(3),
393 | 4
394 | ];
395 |
396 | return Promise.reduce(values, function(total, value) {
397 | return value;
398 | }).then(assert.fail, function(err) {
399 | assert.equal(err, ERROR);
400 | });
401 | });
402 |
403 | it("propagates an Error from the iterator", function() {
404 | var values = [
405 | 0,
406 | thenabling(1),
407 | promisingThen(2)(),
408 | promised(3),
409 | 4
410 | ];
411 |
412 | return Promise.reduce(values, function(total, value) {
413 | if (value === 2) {
414 | throw ERROR;
415 | }
416 | return value;
417 | }).then(assert.fail, function(err) {
418 | assert.equal(err, ERROR);
419 | });
420 | });
421 | });
422 |
423 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bluebird-api
2 |
3 | ## Introduction
4 | [Bluebird](https://github.com/petkaantonov/bluebird) compatible API on top of native promises as a drop-in replacement.
5 |
6 | WIP - contributions welcome.
7 |
8 | ## Getting started
9 |
10 | * bluebird-api depends on node 7.0 and above
11 |
12 | ```
13 | $ npm install bluebird-api
14 | ```
15 | Then :
16 | ```js
17 | const Promise = require('bluebird-api');
18 | ```
19 | ## Contributing
20 | 1. [Fork](https://github.com/benjamingr/bluebird-api#fork-destination-box) the repository
21 | 2. ``git clone https://github.com/**your GH user**/bluebird-api``
22 | 3. ``cd bluebird-api``
23 | 4. ``npm install``
24 | 5. ``npm test``
25 |
26 | Before committing changes run `npm run build`
27 |
28 | > ***Commit build along with your changes to the repo.***
29 |
30 | ## Why
31 | There are [many reasons to use bluebird](http://bluebirdjs.com/docs/why-bluebird.html).
32 | As native Promise implementations improve, bluebird-api allows keeping the same code but enjoying the benefits of improved native Promises.
33 |
34 | ## API
35 |
36 | http://bluebirdjs.com/docs/api-reference.html
37 |
38 | ### Core
39 |
40 | #### [Promise.then](http://bluebirdjs.com/docs/api/then.html)
41 | ```js
42 | .then(
43 | [function(any value) fulfilledHandler],
44 | [function(any error) rejectedHandler]
45 | ) -> Promise
46 |
47 | ```
48 |
49 | #### [Promise.catch](http://bluebirdjs.com/docs/api/catch.html)
50 |
51 | `.catch` is a convenience method for handling errors in promise chains. It comes in two variants - A catch-all variant similar to the synchronous `catch(e) {` block. This variant is compatible with native promises. - A filtered variant (like other non-JS languages typically have) that lets you only handle specific errors. ***This variant is usually preferable and is significantly safer.***
52 |
53 |
54 | #### [Promise.error](http://bluebirdjs.com/docs/api/error.html)
55 |
56 | ```js
57 | .error([function(any error) rejectedHandler]) -> Promise
58 | ```
59 |
60 | Like `.catch` but instead of catching all types of exceptions, it only catches operational errors.
61 |
62 | Note, "errors" mean errors, as in objects that are `instanceof Error` - not strings, numbers and so on.
63 |
64 | #### [Promise.spread](http://bluebirdjs.com/docs/api/spread.html)
65 | Like calling .then, but the fulfillment value must be an array, which is flattened to the formal parameters of the fulfillment handler.
66 | ```js
67 | .spread([function(any values...) fulfilledHandler]) -> Promise
68 | ```
69 |
70 |
71 | #### [Promise.finally](http://bluebirdjs.com/docs/api/finally.html)
72 |
73 | ```js
74 | .finally(function() handler) -> Promise
75 |
76 | .lastly(function() handler) -> Promise
77 | ```
78 |
79 | Pass a handler that will be called regardless of this promise's fate. Returns a new promise chained from this promise. There are special semantics for .finally in that the final value cannot be modified from the handler.
80 |
81 | > Note: using `.finally` for resource management has better alternatives, see [resource management](http://bluebirdjs.com/docs/api/resource-management.html)
82 |
83 | #### [Promise.join](http://bluebirdjs.com/docs/api/promise.join.html)
84 | For coordinating multiple concurrent discrete promises. While ``.all`` is good for handling a dynamically sized list of uniform promises, ``Promise.join`` is much easier (and more performant) to use when you have a fixed amount of discrete promises that you want to coordinate concurrently. The final parameter, handler function, will be invoked with the result values of all of the fufilled promises.
85 | ```js
86 | Promise.join(Promise|any values..., function handler) -> Promise
87 | ```
88 | #### [Promise.try](http://bluebirdjs.com/docs/api/promise.try.html)
89 | Start the chain of promises with ``Promise.try``. Any synchronous exceptions will be turned into rejections on the returned promise.
90 | ```js
91 | Promise.try(function() fn) -> Promise
92 | ```
93 | #### [Promise.method](http://bluebirdjs.com/docs/api/promise.method.html)
94 | Returns a new function that wraps the given function ``fn``. The new function will always return a promise that is fulfilled with the original functions return values or rejected with thrown exceptions from the original function.
95 | ```js
96 | Promise.method(function(...arguments) fn) -> function
97 | ```
98 |
99 |
100 | #### [Promise.suppressUnhandledRejections](http://bluebirdjs.com/docs/api/suppressunhandledrejections.html)
101 |
102 | ```js
103 | .suppressUnhandledRejections() -> undefined
104 |
105 | ```
106 |
107 | Basically sugar for doing:
108 | ```js
109 | somePromise.catch(function(){});
110 | ```
111 |
112 | Which is needed in case error handlers are attached asynchronously to the promise later, which would otherwise result in premature unhandled rejection reporting.
113 |
114 |
115 | #### [Promise.onPossiblyUnhandledRejection](http://bluebirdjs.com/docs/api/promise.onpossiblyunhandledrejection.html)
116 |
117 | ```js
118 | Promise.onPossiblyUnhandledRejection(function(any error, Promise promise) handler) -> undefined
119 | ```
120 |
121 | > Note: this hook is specific to the bluebird instance its called on, application developers should use [global rejection events](http://bluebirdjs.com/docs/api/error-management-configuration.html#global-rejection-events)
122 |
123 | Add `handler` as the handler to call when there is a possibly unhandled rejection. The default handler logs the error stack to stderr or `console.error` in browsers.
124 |
125 | ```js
126 | Promise.onPossiblyUnhandledRejection(function(e, promise) {
127 | throw e;
128 | });
129 | ```
130 |
131 | Passing no value or a non-function will have the effect of removing any kind of handling for possibly unhandled rejections.
132 |
133 |
134 |
135 | ### Timers
136 | #### [.delay](http://bluebirdjs.com/docs/api/delay.html)
137 | Returns a promise that will be resolved with ms milliseconds.
138 | ```js
139 | .delay(int ms) -> Promise
140 | ```
141 | #### [.timeout](http://bluebirdjs.com/docs/api/timeout.html)
142 | Returns a promise that will be fulfilled with this promise's fulfillment value or rejection reason. However, if this promise is not fulfilled or rejected within ms milliseconds, the returned promise is rejected with a TimeoutError or the error as the reason.
143 | ```js
144 | .timeout(int ms, [String message="operation timed out"]) -> Promise
145 | ---
146 | .timeout(int ms, [Error error]) -> Promise
147 | ```
148 |
149 | ### Promisification
150 | #### [Promise.promisify](http://bluebirdjs.com/docs/api/promise.promisify.html)
151 | Returns a function that will wrap the given ``nodeFunction``. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument.
152 | ```js
153 | Promise.promisify(
154 | function(any arguments..., function callback) nodeFunction,
155 | [Object { multiArgs: boolean=false, context: any=this } options]
156 | ) -> function
157 | ```
158 |
159 | #### [Promise.asCallback](http://bluebirdjs.com/docs/api/ascallback.html)
160 | ```js
161 | .asCallback(
162 | [function(any error, any value) callback],
163 | [Object {spread: boolean=false} options]
164 | ) -> this
165 | ```
166 | ```js
167 | .nodeify(
168 | [function(any error, any value) callback],
169 | [Object {spread: boolean=false} options]
170 | ) -> this
171 | ```
172 |
173 | Register a node-style callback on this promise. When this promise is either fulfilled or rejected, the node callback will be called back with the node.js convention where error reason is the first argument and success value is the second argument. The error argument will be `null` in case of success.
174 |
175 | Returns back this promise instead of creating a new one. If the `callback` argument is not a function, this method does not do anything.
176 |
177 |
178 |
179 | ### Collections
180 | #### [.props](http://bluebirdjs.com/docs/api/props.html)
181 | Like ``.all`` but for object properties or ``Map``s entries instead of iterated values. Returns a promise that is fulfilled when all the properties of the object or the ``Map``'s values are fulfilled. The promise's fulfillment value is an object or a ``Map`` with fulfillment values at respective keys to the original object or a ``Map``. If any promise in the object or ``Map`` rejects, the returned promise is rejected with the rejection reason.
182 | ```js
183 | .props(Object|Map|Promise