├── .eslintrc ├── .gitignore ├── .jshintrc ├── .release.json ├── .spmignore ├── .travis.yml ├── Brocfile.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── auto.js ├── config └── versionTemplate.txt ├── es6-promise.d.ts ├── lib ├── es6-promise.auto.js ├── es6-promise.js └── es6-promise │ ├── -internal.js │ ├── asap.js │ ├── enumerator.js │ ├── polyfill.js │ ├── promise.js │ ├── promise │ ├── all.js │ ├── race.js │ ├── reject.js │ └── resolve.js │ ├── then.js │ └── utils.js ├── package.json ├── server ├── .jshintrc └── index.js ├── test ├── extension-test.js ├── index.html ├── index.js ├── scheduler-test.js ├── test-adapter.js └── worker.js ├── testem.js ├── vendor └── loader.js └── yarn.lock /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "ember", 3 | 4 | "parser": "babel-eslint", 5 | 6 | "ecmaFeatures": { 7 | modules: true, 8 | blockBindings: true, 9 | arrowFunctions: true, 10 | objectLiteralShorthandMethods: true, 11 | objectLiteralShorthandProperties: true, 12 | templateStrings: true 13 | }, 14 | 15 | "rules": { 16 | "indent": [ 2, "tab", { "SwitchCase": 1 } ], 17 | "object-shorthand": [ 2, "always" ], 18 | "prefer-const": 0, 19 | "comma-dangle": 0, 20 | "spaced-comment": 1, 21 | "object-curly-spacing": [2, "always"], 22 | "arrow-spacing": [ 1, { before: true, after: true } ], 23 | "array-bracket-spacing": [ 2, "always" ], 24 | "no-restricted-syntax": 0, 25 | "no-warning-comments": [ 0, { "terms": [ "todo", "fixme", "xxx" ], "location": "start" } ], 26 | "no-ternary": 0, 27 | "no-nested-ternary": 2, 28 | "brace-style": [ 2, "stroustrup" ], 29 | "no-else-return": 0 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /promises_tests 3 | /main.js 4 | /tmp 5 | /docs 6 | /dist 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "console", 4 | "require", 5 | "equal", 6 | "test", 7 | "testBoth", 8 | "testWithDefault", 9 | "raises", 10 | "deepEqual", 11 | "start", 12 | "stop", 13 | "ok", 14 | "strictEqual", 15 | "module", 16 | "expect" 17 | ], 18 | 19 | "esnext": true, 20 | "proto": true, 21 | "node" : true, 22 | "browser" : true, 23 | 24 | "boss" : true, 25 | "curly": false, 26 | "debug": false, 27 | "devel": false, 28 | "eqeqeq": true, 29 | "evil": true, 30 | "forin": false, 31 | "immed": false, 32 | "laxbreak": false, 33 | "newcap": true, 34 | "noarg": true, 35 | "noempty": false, 36 | "nonew": false, 37 | "nomen": false, 38 | "onevar": false, 39 | "plusplus": false, 40 | "regexp": false, 41 | "undef": true, 42 | "sub": true, 43 | "strict": false, 44 | "white": false, 45 | "eqnull": true 46 | } 47 | -------------------------------------------------------------------------------- /.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "non-interactive": true, 3 | "dry-run": false, 4 | "verbose": false, 5 | "force": false, 6 | "pkgFiles": ["package.json", "bower.json"], 7 | "increment": "patch", 8 | "commitMessage": "Release %s", 9 | "tagName": "v%s", 10 | "tagAnnotation": "Release %s", 11 | "buildCommand": "npm run-script build:production", 12 | "dist": { 13 | "repo": "git@github.com:components/es6-promise.git", 14 | "stageDir": "tmp/stage", 15 | "base": "dist", 16 | "files": ["**/*", "../package.json", "../bower.json"] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.spmignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /tmp 3 | /tasks 4 | /test 5 | /vendor 6 | /.jshintrc 7 | /.npmignore 8 | /.travis.yml 9 | /Gruntfile.js 10 | /component.json 11 | /index.html 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "9" 4 | - "8" 5 | - "6" 6 | - "4" 7 | 8 | before_install: 9 | - nvm install 6 10 | - nvm use 6 11 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH 12 | 13 | before_script: 14 | - nvm use $TRAVIS_NODE_VERSION 15 | env: 16 | global: 17 | - EMBER_ENV=production 18 | -------------------------------------------------------------------------------- /Brocfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint node:true, undef:true, unused:true */ 4 | const Rollup = require('broccoli-rollup'); 5 | const Babel = require('broccoli-babel-transpiler'); 6 | const merge = require('broccoli-merge-trees'); 7 | const uglify = require('broccoli-uglify-js'); 8 | const version = require('git-repo-version'); 9 | const watchify = require('broccoli-watchify'); 10 | const concat = require('broccoli-concat'); 11 | const fs = require('fs'); 12 | 13 | const stew = require('broccoli-stew'); 14 | 15 | const find = stew.find; 16 | const mv = stew.mv; 17 | const rename = stew.rename; 18 | const env = stew.env; 19 | const map = stew.map; 20 | 21 | const lib = find('lib'); 22 | 23 | // test stuff 24 | const testDir = find('test'); 25 | const testFiles = find('test/{index.html,worker.js}'); 26 | 27 | const json3 = mv(find('node_modules/json3/lib/{json3.js}'), 'node_modules/json3/lib/', 'test/'); 28 | // mocha doesn't browserify correctly 29 | const mocha = mv(find('node_modules/mocha/mocha.{js,css}'), 'node_modules/mocha/', 'test/'); 30 | 31 | const testVendor = merge([ json3, mocha ]); 32 | 33 | 34 | const es5 = new Babel(lib, { 35 | plugins: [ 36 | 'transform-es2015-arrow-functions', 37 | 'transform-es2015-computed-properties', 38 | 'transform-es2015-shorthand-properties', 39 | 'transform-es2015-template-literals', 40 | 'transform-es2015-parameters', 41 | 'transform-es2015-destructuring', 42 | 'transform-es2015-spread', 43 | 'transform-es2015-block-scoping', 44 | 'transform-es2015-constants', 45 | ['transform-es2015-classes', { loose: true }], 46 | 'babel6-plugin-strip-class-callcheck' 47 | ] 48 | }); 49 | 50 | function rollupConfig(entry) { 51 | return new Rollup(es5, { 52 | rollup: { 53 | input: 'lib/' + entry, 54 | output: [ 55 | { 56 | format: 'umd', 57 | name: 'ES6Promise', 58 | file: entry, 59 | sourcemap: 'inline' 60 | } 61 | ] 62 | } 63 | }); 64 | } 65 | 66 | // build RSVP itself 67 | const es6Promise = rollupConfig('es6-promise.js') 68 | const es6PromiseAuto = rollupConfig('es6-promise.auto.js') 69 | 70 | const testBundle = watchify(merge([ 71 | mv(es6Promise, 'test'), 72 | testDir 73 | ]), { 74 | browserify: { debug: true, entries: ['./test/index.js'] } 75 | }); 76 | 77 | const header = stew.map(find('config/versionTemplate.txt'), content => content.replace(/VERSION_PLACEHOLDER_STRING/, version())); 78 | 79 | function concatAs(outputFile) { 80 | return merge([ 81 | concat(merge([es6Promise, header]), { 82 | headerFiles: ['config/versionTemplate.txt'], 83 | inputFiles: ['es6-promise.js'], 84 | outputFile: outputFile 85 | }), 86 | 87 | concat(merge([es6PromiseAuto, header]), { 88 | headerFiles: ['config/versionTemplate.txt'], 89 | inputFiles: ['es6-promise.auto.js'], 90 | outputFile: outputFile.replace('es6-promise', 'es6-promise.auto'), 91 | }), 92 | 93 | ]); 94 | } 95 | 96 | function production() { 97 | let result; 98 | env('production', () => { 99 | result = uglify(concatAs('es6-promise.min.js'), { 100 | compress: true, 101 | mangle: true, 102 | }); 103 | }) 104 | return result; 105 | } 106 | 107 | function development() { 108 | return concatAs('es6-promise.js'); 109 | } 110 | 111 | module.exports = merge([ 112 | merge([ 113 | production(), 114 | development(), 115 | ].filter(Boolean)), 116 | // test stuff 117 | testFiles, 118 | testVendor, 119 | mv(testBundle, 'test') 120 | ]); 121 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Master 2 | 3 | # 4.2.5 4 | 5 | * remove old try/catch performance hacks, modern runtimes do not require these tricks 6 | 7 | # 4.2.4 8 | 9 | * [Fixes #305] Confuse webpack 10 | 11 | # 4.2.3 12 | 13 | * Cleanup testem related build configuration 14 | * Use `prepublishOnly` instead of `prepublish` (thanks @rhysd) 15 | * Add Node.js 9, 8 to testing matrix 16 | * drop now unused s3 deployment files 17 | * internal cleanup (thanks to @bekzod, @mariusschulz) 18 | * Fixup Changelog 19 | 20 | # 4.2.2 21 | 22 | * Ensure PROMISE_ID works correctly 23 | * internal cleanup (thanks yo @mariusschulz) 24 | 25 | # 4.2.1 26 | 27 | * drop bower support 28 | 29 | # 4.2.0 30 | 31 | * drop `dist` from git repo 32 | * add `Promise.prototype.finally` 33 | * update various build related dependencies 34 | * add CDN links 35 | 36 | # 4.1.0 37 | 38 | * [BUGFIX] Fix memory leak [#269] 39 | * [BUGFIX] Auto Bundles within an AMD Environment [#263] 40 | 41 | # 4.0.5 42 | 43 | * fix require('es6-promise/auto') for Node < 4 44 | 45 | # 4.0.4 46 | 47 | * fix asap when using https://github.com/Kinvey/titanium-sdk 48 | 49 | # 4.0.3 50 | 51 | * fix Readme links 52 | 53 | # 4.0.2 54 | 55 | * fix require('es6-promise/auto'); 56 | 57 | # 4.0.0 58 | 59 | * no longer polyfill automatically, if needed one can still invoke 60 | `require('es6-promise/auto')` directly. 61 | 62 | # 3.3.1 63 | 64 | * fix links in readme 65 | 66 | # 3.3.0 67 | 68 | * support polyfil on WebMAF (playstation env) 69 | * fix tampering related bug global `constructor` was referenced by mistake. 70 | * provide TS Typings 71 | * increase compatibliity with sinon.useFakeTimers(); 72 | * update build tools (use rollup) 73 | * directly export promise; 74 | 75 | # 3.2.2 76 | 77 | * IE8: use isArray 78 | * update build dependencies 79 | 80 | # 3.2.1 81 | 82 | * fix race tampering issue 83 | * use eslint 84 | * fix Promise.all tampering 85 | * remove unused code 86 | * fix issues with NWJS/electron 87 | 88 | # 3.2.0 89 | 90 | * improve tamper resistence of Promise.all Promise.race and 91 | Promise.prototype.then (note, this isn't complete, but addresses an exception 92 | when used \w core-js, follow up work will address entirely) 93 | * remove spec incompatible then chaining fast-path 94 | * add eslint 95 | * update build deps 96 | 97 | # 3.1.2 98 | 99 | * fix node detection issues with NWJS/electron 100 | 101 | # 3.1.0 102 | 103 | * improve performance of Promise.all when it encounters a non-promise input object input 104 | * then/resolve tamper protection 105 | * reduce AST size of promise constructor, to facilitate more inlining 106 | * Update README.md with details about PhantomJS requirement for running tests 107 | * Mangle and compress the minified version 108 | 109 | # 3.0.2 110 | 111 | * correctly bump both bower and package.json versions 112 | 113 | # 3.0.1 114 | 115 | * no longer include dist/test in npm releases 116 | 117 | # 3.0.0 118 | 119 | * use nextTick() instead of setImmediate() to schedule microtasks with node 0.10. Later versions of 120 | nodes are not affected as they were already using nextTick(). Note that using nextTick() might 121 | trigger a depreciation warning on 0.10 as described at https://github.com/cujojs/when/issues/410. 122 | The reason why nextTick() is preferred is that is setImmediate() would schedule a macrotask 123 | instead of a microtask and might result in a different scheduling. 124 | If needed you can revert to the former behavior as follow: 125 | 126 | var Promise = require('es6-promise').Promise; 127 | Promise._setScheduler(setImmediate); 128 | 129 | # 2.3.0 130 | 131 | * #121: Ability to override the internal asap implementation 132 | * #120: Use an ascii character for an apostrophe, for source maps 133 | 134 | # 2.2.0 135 | 136 | * #116: Expose asap() and a way to override the scheduling mechanism on Promise 137 | * Lock to v0.2.3 of ember-cli 138 | 139 | # 2.1.1 140 | 141 | * Fix #100 via #105: tell browserify to ignore vertx require 142 | * Fix #101 via #102: "follow thenable state, not own state" 143 | 144 | # 2.1.0 145 | 146 | * #59: Automatic polyfill. No need to invoke `ES6Promise.polyfill()` anymore. 147 | * ... (see the commit log) 148 | 149 | # 2.0.0 150 | 151 | * re-sync with RSVP. Many large performance improvements and bugfixes. 152 | 153 | # 1.0.0 154 | 155 | * first subset of RSVP 156 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ES6-Promise (subset of [rsvp.js](https://github.com/tildeio/rsvp.js)) [![Build Status](https://travis-ci.org/stefanpenner/es6-promise.svg?branch=master)](https://travis-ci.org/stefanpenner/es6-promise) 2 | 3 | This is a polyfill of the [ES6 Promise](http://www.ecma-international.org/ecma-262/6.0/#sec-promise-constructor). The implementation is a subset of [rsvp.js](https://github.com/tildeio/rsvp.js) extracted by @jakearchibald, if you're wanting extra features and more debugging options, check out the [full library](https://github.com/tildeio/rsvp.js). 4 | 5 | For API details and how to use promises, see the JavaScript Promises HTML5Rocks article. 6 | 7 | ## Downloads 8 | 9 | * [es6-promise 27.86 KB (7.33 KB gzipped)](https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.js) 10 | * [es6-promise-auto 27.78 KB (7.3 KB gzipped)](https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.auto.js) - Automatically provides/replaces `Promise` if missing or broken. 11 | * [es6-promise-min 6.17 KB (2.4 KB gzipped)](https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.min.js) 12 | * [es6-promise-auto-min 6.19 KB (2.4 KB gzipped)](https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.auto.min.js) - Minified version of `es6-promise-auto` above. 13 | 14 | ## CDN 15 | 16 | To use via a CDN include this in your html: 17 | 18 | ```html 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ``` 28 | 29 | ## Node.js 30 | 31 | To install: 32 | 33 | ```sh 34 | yarn add es6-promise 35 | ``` 36 | 37 | or 38 | 39 | ```sh 40 | npm install es6-promise 41 | ``` 42 | 43 | To use: 44 | 45 | ```js 46 | var Promise = require('es6-promise').Promise; 47 | ``` 48 | 49 | 50 | ## Usage in IE<9 51 | 52 | `catch` and `finally` are reserved keywords in IE<9, meaning 53 | `promise.catch(func)` or `promise.finally(func)` throw a syntax error. To work 54 | around this, you can use a string to access the property as shown in the 55 | following example. 56 | 57 | However most minifiers will automatically fix this for you, making the 58 | resulting code safe for old browsers and production: 59 | 60 | ```js 61 | promise['catch'](function(err) { 62 | // ... 63 | }); 64 | ``` 65 | 66 | ```js 67 | promise['finally'](function() { 68 | // ... 69 | }); 70 | ``` 71 | 72 | ## Auto-polyfill 73 | 74 | To polyfill the global environment (either in Node or in the browser via CommonJS) use the following code snippet: 75 | 76 | ```js 77 | require('es6-promise').polyfill(); 78 | ``` 79 | 80 | Alternatively 81 | 82 | ```js 83 | require('es6-promise/auto'); 84 | ``` 85 | 86 | Notice that we don't assign the result of `polyfill()` to any variable. The `polyfill()` method will patch the global environment (in this case to the `Promise` name) when called. 87 | 88 | ## Building & Testing 89 | 90 | You will need to have PhantomJS installed globally in order to run the tests. 91 | 92 | `npm install -g phantomjs` 93 | 94 | * `npm run build` to build 95 | * `npm test` to run tests 96 | * `npm start` to run a build watcher, and webserver to test 97 | * `npm run test:server` for a testem test runner and watching builder 98 | -------------------------------------------------------------------------------- /auto.js: -------------------------------------------------------------------------------- 1 | // This file can be required in Browserify and Node.js for automatic polyfill 2 | // To use it: require('es6-promise/auto'); 3 | 'use strict'; 4 | module.exports = require('./').polyfill(); 5 | -------------------------------------------------------------------------------- /config/versionTemplate.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * @overview es6-promise - a tiny implementation of Promises/A+. 3 | * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) 4 | * @license Licensed under MIT license 5 | * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE 6 | * @version VERSION_PLACEHOLDER_STRING 7 | */ 8 | -------------------------------------------------------------------------------- /es6-promise.d.ts: -------------------------------------------------------------------------------- 1 | export interface Thenable { 2 | then (onFulfilled?: (value: R) => U | Thenable, onRejected?: (error: any) => U | Thenable): Thenable; 3 | then (onFulfilled?: (value: R) => U | Thenable, onRejected?: (error: any) => void): Thenable; 4 | } 5 | 6 | export class Promise implements Thenable { 7 | /** 8 | * If you call resolve in the body of the callback passed to the constructor, 9 | * your promise is fulfilled with result object passed to resolve. 10 | * If you call reject your promise is rejected with the object passed to resolve. 11 | * For consistency and debugging (eg stack traces), obj should be an instanceof Error. 12 | * Any errors thrown in the constructor callback will be implicitly passed to reject(). 13 | */ 14 | constructor (callback: (resolve : (value?: R | Thenable) => void, reject: (error?: any) => void) => void); 15 | 16 | /** 17 | * onFulfilled is called when/if "promise" resolves. onRejected is called when/if "promise" rejects. 18 | * Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called. 19 | * Both callbacks have a single parameter , the fulfillment value or rejection reason. 20 | * "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve. 21 | * If an error is thrown in the callback, the returned promise rejects with that error. 22 | * 23 | * @param onFulfilled called when/if "promise" resolves 24 | * @param onRejected called when/if "promise" rejects 25 | */ 26 | then (onFulfilled?: (value: R) => U | Thenable, onRejected?: (error: any) => U | Thenable): Promise; 27 | then (onFulfilled?: (value: R) => U | Thenable, onRejected?: (error: any) => void): Promise; 28 | 29 | /** 30 | * Sugar for promise.then(undefined, onRejected) 31 | * 32 | * @param onRejected called when/if "promise" rejects 33 | */ 34 | catch (onRejected?: (error: any) => U | Thenable): Promise; 35 | 36 | /** 37 | * onSettled is invoked when/if the "promise" settles (either rejects or fulfills). 38 | * The returned promise is settled when the `Thenable` returned by `onFinally` settles; 39 | * it is rejected if `onFinally` throws or rejects; otherwise it assumes the state of the 40 | * original Promise. 41 | * 42 | * @param onFinally called when/if "promise" settles 43 | 44 | */ 45 | finally (onFinally?: () => any | Thenable): Promise; 46 | 47 | /** 48 | * Make a new promise from the thenable. 49 | * A thenable is promise-like in as far as it has a "then" method. 50 | */ 51 | static resolve (): Promise; 52 | static resolve (value: R | Thenable): Promise; 53 | 54 | /** 55 | * Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error 56 | */ 57 | static reject (error: any): Promise; 58 | 59 | /** 60 | * Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects. 61 | * the array passed to all can be a mixture of promise-like objects and other objects. 62 | * The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value. 63 | */ 64 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable, T9 | Thenable, T10 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; 65 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable, T9 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; 66 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; 67 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; 68 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6]>; 69 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable]): Promise<[T1, T2, T3, T4, T5]>; 70 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable ]): Promise<[T1, T2, T3, T4]>; 71 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable]): Promise<[T1, T2, T3]>; 72 | static all(values: [T1 | Thenable, T2 | Thenable]): Promise<[T1, T2]>; 73 | static all(values: [T1 | Thenable]): Promise<[T1]>; 74 | static all(values: Array>): Promise; 75 | 76 | /** 77 | * Make a Promise that fulfills when any item fulfills, and rejects if any item rejects. 78 | */ 79 | static race (promises: (R | Thenable)[]): Promise; 80 | } 81 | 82 | /** 83 | * The polyfill method will patch the global environment (in this case to the Promise name) when called. 84 | */ 85 | export function polyfill (): void; 86 | -------------------------------------------------------------------------------- /lib/es6-promise.auto.js: -------------------------------------------------------------------------------- 1 | import Promise from './es6-promise'; 2 | Promise.polyfill(); 3 | export default Promise; 4 | -------------------------------------------------------------------------------- /lib/es6-promise.js: -------------------------------------------------------------------------------- 1 | import Promise from './es6-promise/promise'; 2 | import polyfill from './es6-promise/polyfill'; 3 | 4 | // Strange compat.. 5 | Promise.polyfill = polyfill; 6 | Promise.Promise = Promise; 7 | export default Promise; 8 | -------------------------------------------------------------------------------- /lib/es6-promise/-internal.js: -------------------------------------------------------------------------------- 1 | import { 2 | objectOrFunction, 3 | isFunction 4 | } from './utils'; 5 | 6 | import { 7 | asap 8 | } from './asap'; 9 | 10 | import originalThen from './then'; 11 | import originalResolve from './promise/resolve'; 12 | 13 | export const PROMISE_ID = Math.random().toString(36).substring(2); 14 | 15 | function noop() {} 16 | 17 | const PENDING = void 0; 18 | const FULFILLED = 1; 19 | const REJECTED = 2; 20 | 21 | function selfFulfillment() { 22 | return new TypeError("You cannot resolve a promise with itself"); 23 | } 24 | 25 | function cannotReturnOwn() { 26 | return new TypeError('A promises callback cannot return that same promise.'); 27 | } 28 | 29 | function tryThen(then, value, fulfillmentHandler, rejectionHandler) { 30 | try { 31 | then.call(value, fulfillmentHandler, rejectionHandler); 32 | } catch(e) { 33 | return e; 34 | } 35 | } 36 | 37 | function handleForeignThenable(promise, thenable, then) { 38 | asap(promise => { 39 | let sealed = false; 40 | let error = tryThen(then, thenable, value => { 41 | if (sealed) { return; } 42 | sealed = true; 43 | if (thenable !== value) { 44 | resolve(promise, value); 45 | } else { 46 | fulfill(promise, value); 47 | } 48 | }, reason => { 49 | if (sealed) { return; } 50 | sealed = true; 51 | 52 | reject(promise, reason); 53 | }, 'Settle: ' + (promise._label || ' unknown promise')); 54 | 55 | if (!sealed && error) { 56 | sealed = true; 57 | reject(promise, error); 58 | } 59 | }, promise); 60 | } 61 | 62 | function handleOwnThenable(promise, thenable) { 63 | if (thenable._state === FULFILLED) { 64 | fulfill(promise, thenable._result); 65 | } else if (thenable._state === REJECTED) { 66 | reject(promise, thenable._result); 67 | } else { 68 | subscribe(thenable, undefined, value => resolve(promise, value), 69 | reason => reject(promise, reason)) 70 | } 71 | } 72 | 73 | function handleMaybeThenable(promise, maybeThenable, then) { 74 | if (maybeThenable.constructor === promise.constructor && 75 | then === originalThen && 76 | maybeThenable.constructor.resolve === originalResolve) { 77 | handleOwnThenable(promise, maybeThenable); 78 | } else { 79 | if (then === undefined) { 80 | fulfill(promise, maybeThenable); 81 | } else if (isFunction(then)) { 82 | handleForeignThenable(promise, maybeThenable, then); 83 | } else { 84 | fulfill(promise, maybeThenable); 85 | } 86 | } 87 | } 88 | 89 | function resolve(promise, value) { 90 | if (promise === value) { 91 | reject(promise, selfFulfillment()); 92 | } else if (objectOrFunction(value)) { 93 | let then; 94 | try { 95 | then = value.then; 96 | } catch (error) { 97 | reject(promise, error); 98 | return; 99 | } 100 | handleMaybeThenable(promise, value, then); 101 | } else { 102 | fulfill(promise, value); 103 | } 104 | } 105 | 106 | function publishRejection(promise) { 107 | if (promise._onerror) { 108 | promise._onerror(promise._result); 109 | } 110 | 111 | publish(promise); 112 | } 113 | 114 | function fulfill(promise, value) { 115 | if (promise._state !== PENDING) { return; } 116 | 117 | promise._result = value; 118 | promise._state = FULFILLED; 119 | 120 | if (promise._subscribers.length !== 0) { 121 | asap(publish, promise); 122 | } 123 | } 124 | 125 | function reject(promise, reason) { 126 | if (promise._state !== PENDING) { return; } 127 | promise._state = REJECTED; 128 | promise._result = reason; 129 | 130 | asap(publishRejection, promise); 131 | } 132 | 133 | function subscribe(parent, child, onFulfillment, onRejection) { 134 | let { _subscribers } = parent; 135 | let { length } = _subscribers; 136 | 137 | parent._onerror = null; 138 | 139 | _subscribers[length] = child; 140 | _subscribers[length + FULFILLED] = onFulfillment; 141 | _subscribers[length + REJECTED] = onRejection; 142 | 143 | if (length === 0 && parent._state) { 144 | asap(publish, parent); 145 | } 146 | } 147 | 148 | function publish(promise) { 149 | let subscribers = promise._subscribers; 150 | let settled = promise._state; 151 | 152 | if (subscribers.length === 0) { return; } 153 | 154 | let child, callback, detail = promise._result; 155 | 156 | for (let i = 0; i < subscribers.length; i += 3) { 157 | child = subscribers[i]; 158 | callback = subscribers[i + settled]; 159 | 160 | if (child) { 161 | invokeCallback(settled, child, callback, detail); 162 | } else { 163 | callback(detail); 164 | } 165 | } 166 | 167 | promise._subscribers.length = 0; 168 | } 169 | 170 | function invokeCallback(settled, promise, callback, detail) { 171 | let hasCallback = isFunction(callback), 172 | value, error, succeeded = true; 173 | 174 | if (hasCallback) { 175 | try { 176 | value = callback(detail); 177 | } catch (e) { 178 | succeeded = false; 179 | error = e; 180 | } 181 | 182 | if (promise === value) { 183 | reject(promise, cannotReturnOwn()); 184 | return; 185 | } 186 | } else { 187 | value = detail; 188 | } 189 | 190 | if (promise._state !== PENDING) { 191 | // noop 192 | } else if (hasCallback && succeeded) { 193 | resolve(promise, value); 194 | } else if (succeeded === false) { 195 | reject(promise, error); 196 | } else if (settled === FULFILLED) { 197 | fulfill(promise, value); 198 | } else if (settled === REJECTED) { 199 | reject(promise, value); 200 | } 201 | } 202 | 203 | function initializePromise(promise, resolver) { 204 | try { 205 | resolver(function resolvePromise(value){ 206 | resolve(promise, value); 207 | }, function rejectPromise(reason) { 208 | reject(promise, reason); 209 | }); 210 | } catch(e) { 211 | reject(promise, e); 212 | } 213 | } 214 | 215 | let id = 0; 216 | function nextId() { 217 | return id++; 218 | } 219 | 220 | function makePromise(promise) { 221 | promise[PROMISE_ID] = id++; 222 | promise._state = undefined; 223 | promise._result = undefined; 224 | promise._subscribers = []; 225 | } 226 | 227 | export { 228 | nextId, 229 | makePromise, 230 | noop, 231 | resolve, 232 | reject, 233 | fulfill, 234 | subscribe, 235 | publish, 236 | publishRejection, 237 | initializePromise, 238 | invokeCallback, 239 | FULFILLED, 240 | REJECTED, 241 | PENDING, 242 | handleMaybeThenable 243 | }; 244 | -------------------------------------------------------------------------------- /lib/es6-promise/asap.js: -------------------------------------------------------------------------------- 1 | let len = 0; 2 | let vertxNext; 3 | let customSchedulerFn; 4 | 5 | export var asap = function asap(callback, arg) { 6 | queue[len] = callback; 7 | queue[len + 1] = arg; 8 | len += 2; 9 | if (len === 2) { 10 | // If len is 2, that means that we need to schedule an async flush. 11 | // If additional callbacks are queued before the queue is flushed, they 12 | // will be processed by this flush that we are scheduling. 13 | if (customSchedulerFn) { 14 | customSchedulerFn(flush); 15 | } else { 16 | scheduleFlush(); 17 | } 18 | } 19 | } 20 | 21 | export function setScheduler(scheduleFn) { 22 | customSchedulerFn = scheduleFn; 23 | } 24 | 25 | export function setAsap(asapFn) { 26 | asap = asapFn; 27 | } 28 | 29 | const browserWindow = (typeof window !== 'undefined') ? window : undefined; 30 | const browserGlobal = browserWindow || {}; 31 | const BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; 32 | const isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; 33 | 34 | // test for web worker but not in IE10 35 | const isWorker = typeof Uint8ClampedArray !== 'undefined' && 36 | typeof importScripts !== 'undefined' && 37 | typeof MessageChannel !== 'undefined'; 38 | 39 | // node 40 | function useNextTick() { 41 | // node version 0.10.x displays a deprecation warning when nextTick is used recursively 42 | // see https://github.com/cujojs/when/issues/410 for details 43 | return () => process.nextTick(flush); 44 | } 45 | 46 | // vertx 47 | function useVertxTimer() { 48 | if (typeof vertxNext !== 'undefined') { 49 | return function() { 50 | vertxNext(flush); 51 | }; 52 | } 53 | 54 | return useSetTimeout(); 55 | } 56 | 57 | function useMutationObserver() { 58 | let iterations = 0; 59 | const observer = new BrowserMutationObserver(flush); 60 | const node = document.createTextNode(''); 61 | observer.observe(node, { characterData: true }); 62 | 63 | return () => { 64 | node.data = (iterations = ++iterations % 2); 65 | }; 66 | } 67 | 68 | // web worker 69 | function useMessageChannel() { 70 | const channel = new MessageChannel(); 71 | channel.port1.onmessage = flush; 72 | return () => channel.port2.postMessage(0); 73 | } 74 | 75 | function useSetTimeout() { 76 | // Store setTimeout reference so es6-promise will be unaffected by 77 | // other code modifying setTimeout (like sinon.useFakeTimers()) 78 | const globalSetTimeout = setTimeout; 79 | return () => globalSetTimeout(flush, 0); 80 | } 81 | 82 | const queue = new Array(1000); 83 | function flush() { 84 | for (let i = 0; i < len; i+=2) { 85 | let callback = queue[i]; 86 | let arg = queue[i+1]; 87 | 88 | callback(arg); 89 | 90 | queue[i] = undefined; 91 | queue[i+1] = undefined; 92 | } 93 | 94 | len = 0; 95 | } 96 | 97 | function attemptVertx() { 98 | try { 99 | const vertx = Function('return this')().require('vertx'); 100 | vertxNext = vertx.runOnLoop || vertx.runOnContext; 101 | return useVertxTimer(); 102 | } catch(e) { 103 | return useSetTimeout(); 104 | } 105 | } 106 | 107 | let scheduleFlush; 108 | // Decide what async method to use to triggering processing of queued callbacks: 109 | if (isNode) { 110 | scheduleFlush = useNextTick(); 111 | } else if (BrowserMutationObserver) { 112 | scheduleFlush = useMutationObserver(); 113 | } else if (isWorker) { 114 | scheduleFlush = useMessageChannel(); 115 | } else if (browserWindow === undefined && typeof require === 'function') { 116 | scheduleFlush = attemptVertx(); 117 | } else { 118 | scheduleFlush = useSetTimeout(); 119 | } 120 | -------------------------------------------------------------------------------- /lib/es6-promise/enumerator.js: -------------------------------------------------------------------------------- 1 | import { 2 | isArray, 3 | isMaybeThenable 4 | } from './utils'; 5 | import { 6 | noop, 7 | reject, 8 | fulfill, 9 | subscribe, 10 | FULFILLED, 11 | REJECTED, 12 | PENDING, 13 | handleMaybeThenable 14 | } from './-internal'; 15 | 16 | import then from './then'; 17 | import Promise from './promise'; 18 | import originalResolve from './promise/resolve'; 19 | import originalThen from './then'; 20 | import { makePromise, PROMISE_ID } from './-internal'; 21 | 22 | function validationError() { 23 | return new Error('Array Methods must be provided an Array'); 24 | }; 25 | 26 | export default class Enumerator { 27 | constructor(Constructor, input) { 28 | this._instanceConstructor = Constructor; 29 | this.promise = new Constructor(noop); 30 | 31 | if (!this.promise[PROMISE_ID]) { 32 | makePromise(this.promise); 33 | } 34 | 35 | if (isArray(input)) { 36 | this.length = input.length; 37 | this._remaining = input.length; 38 | 39 | this._result = new Array(this.length); 40 | 41 | if (this.length === 0) { 42 | fulfill(this.promise, this._result); 43 | } else { 44 | this.length = this.length || 0; 45 | this._enumerate(input); 46 | if (this._remaining === 0) { 47 | fulfill(this.promise, this._result); 48 | } 49 | } 50 | } else { 51 | reject(this.promise, validationError()); 52 | } 53 | } 54 | _enumerate(input) { 55 | for (let i = 0; this._state === PENDING && i < input.length; i++) { 56 | this._eachEntry(input[i], i); 57 | } 58 | } 59 | 60 | _eachEntry(entry, i) { 61 | let c = this._instanceConstructor; 62 | let { resolve } = c; 63 | 64 | if (resolve === originalResolve) { 65 | let then; 66 | let error; 67 | let didError = false; 68 | try { 69 | then = entry.then; 70 | } catch (e) { 71 | didError = true; 72 | error = e; 73 | } 74 | 75 | if (then === originalThen && 76 | entry._state !== PENDING) { 77 | this._settledAt(entry._state, i, entry._result); 78 | } else if (typeof then !== 'function') { 79 | this._remaining--; 80 | this._result[i] = entry; 81 | } else if (c === Promise) { 82 | let promise = new c(noop); 83 | if (didError) { 84 | reject(promise, error); 85 | } else { 86 | handleMaybeThenable(promise, entry, then); 87 | } 88 | this._willSettleAt(promise, i); 89 | } else { 90 | this._willSettleAt(new c(resolve => resolve(entry)), i); 91 | } 92 | } else { 93 | this._willSettleAt(resolve(entry), i); 94 | } 95 | } 96 | 97 | _settledAt(state, i, value) { 98 | let { promise } = this; 99 | 100 | if (promise._state === PENDING) { 101 | this._remaining--; 102 | 103 | if (state === REJECTED) { 104 | reject(promise, value); 105 | } else { 106 | this._result[i] = value; 107 | } 108 | } 109 | 110 | if (this._remaining === 0) { 111 | fulfill(promise, this._result); 112 | } 113 | } 114 | 115 | _willSettleAt(promise, i) { 116 | let enumerator = this; 117 | 118 | subscribe( 119 | promise, undefined, 120 | value => enumerator._settledAt(FULFILLED, i, value), 121 | reason => enumerator._settledAt(REJECTED, i, reason) 122 | ); 123 | } 124 | }; 125 | -------------------------------------------------------------------------------- /lib/es6-promise/polyfill.js: -------------------------------------------------------------------------------- 1 | /*global self*/ 2 | import Promise from './promise'; 3 | 4 | export default function polyfill() { 5 | let local; 6 | 7 | if (typeof global !== 'undefined') { 8 | local = global; 9 | } else if (typeof self !== 'undefined') { 10 | local = self; 11 | } else { 12 | try { 13 | local = Function('return this')(); 14 | } catch (e) { 15 | throw new Error('polyfill failed because global object is unavailable in this environment'); 16 | } 17 | } 18 | 19 | let P = local.Promise; 20 | 21 | if (P) { 22 | var promiseToString = null; 23 | try { 24 | promiseToString = Object.prototype.toString.call(P.resolve()); 25 | } catch(e) { 26 | // silently ignored 27 | } 28 | 29 | if (promiseToString === '[object Promise]' && !P.cast){ 30 | return; 31 | } 32 | } 33 | 34 | local.Promise = Promise; 35 | } 36 | -------------------------------------------------------------------------------- /lib/es6-promise/promise.js: -------------------------------------------------------------------------------- 1 | import { 2 | isFunction 3 | } from './utils'; 4 | import { 5 | noop, 6 | nextId, 7 | PROMISE_ID, 8 | initializePromise 9 | } from './-internal'; 10 | import { 11 | asap, 12 | setAsap, 13 | setScheduler 14 | } from './asap'; 15 | 16 | import all from './promise/all'; 17 | import race from './promise/race'; 18 | import Resolve from './promise/resolve'; 19 | import Reject from './promise/reject'; 20 | import then from './then'; 21 | 22 | function needsResolver() { 23 | throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); 24 | } 25 | 26 | function needsNew() { 27 | throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); 28 | } 29 | 30 | /** 31 | Promise objects represent the eventual result of an asynchronous operation. The 32 | primary way of interacting with a promise is through its `then` method, which 33 | registers callbacks to receive either a promise's eventual value or the reason 34 | why the promise cannot be fulfilled. 35 | 36 | Terminology 37 | ----------- 38 | 39 | - `promise` is an object or function with a `then` method whose behavior conforms to this specification. 40 | - `thenable` is an object or function that defines a `then` method. 41 | - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). 42 | - `exception` is a value that is thrown using the throw statement. 43 | - `reason` is a value that indicates why a promise was rejected. 44 | - `settled` the final resting state of a promise, fulfilled or rejected. 45 | 46 | A promise can be in one of three states: pending, fulfilled, or rejected. 47 | 48 | Promises that are fulfilled have a fulfillment value and are in the fulfilled 49 | state. Promises that are rejected have a rejection reason and are in the 50 | rejected state. A fulfillment value is never a thenable. 51 | 52 | Promises can also be said to *resolve* a value. If this value is also a 53 | promise, then the original promise's settled state will match the value's 54 | settled state. So a promise that *resolves* a promise that rejects will 55 | itself reject, and a promise that *resolves* a promise that fulfills will 56 | itself fulfill. 57 | 58 | 59 | Basic Usage: 60 | ------------ 61 | 62 | ```js 63 | let promise = new Promise(function(resolve, reject) { 64 | // on success 65 | resolve(value); 66 | 67 | // on failure 68 | reject(reason); 69 | }); 70 | 71 | promise.then(function(value) { 72 | // on fulfillment 73 | }, function(reason) { 74 | // on rejection 75 | }); 76 | ``` 77 | 78 | Advanced Usage: 79 | --------------- 80 | 81 | Promises shine when abstracting away asynchronous interactions such as 82 | `XMLHttpRequest`s. 83 | 84 | ```js 85 | function getJSON(url) { 86 | return new Promise(function(resolve, reject){ 87 | let xhr = new XMLHttpRequest(); 88 | 89 | xhr.open('GET', url); 90 | xhr.onreadystatechange = handler; 91 | xhr.responseType = 'json'; 92 | xhr.setRequestHeader('Accept', 'application/json'); 93 | xhr.send(); 94 | 95 | function handler() { 96 | if (this.readyState === this.DONE) { 97 | if (this.status === 200) { 98 | resolve(this.response); 99 | } else { 100 | reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); 101 | } 102 | } 103 | }; 104 | }); 105 | } 106 | 107 | getJSON('/posts.json').then(function(json) { 108 | // on fulfillment 109 | }, function(reason) { 110 | // on rejection 111 | }); 112 | ``` 113 | 114 | Unlike callbacks, promises are great composable primitives. 115 | 116 | ```js 117 | Promise.all([ 118 | getJSON('/posts'), 119 | getJSON('/comments') 120 | ]).then(function(values){ 121 | values[0] // => postsJSON 122 | values[1] // => commentsJSON 123 | 124 | return values; 125 | }); 126 | ``` 127 | 128 | @class Promise 129 | @param {Function} resolver 130 | Useful for tooling. 131 | @constructor 132 | */ 133 | 134 | class Promise { 135 | constructor(resolver) { 136 | this[PROMISE_ID] = nextId(); 137 | this._result = this._state = undefined; 138 | this._subscribers = []; 139 | 140 | if (noop !== resolver) { 141 | typeof resolver !== 'function' && needsResolver(); 142 | this instanceof Promise ? initializePromise(this, resolver) : needsNew(); 143 | } 144 | } 145 | 146 | /** 147 | The primary way of interacting with a promise is through its `then` method, 148 | which registers callbacks to receive either a promise's eventual value or the 149 | reason why the promise cannot be fulfilled. 150 | 151 | ```js 152 | findUser().then(function(user){ 153 | // user is available 154 | }, function(reason){ 155 | // user is unavailable, and you are given the reason why 156 | }); 157 | ``` 158 | 159 | Chaining 160 | -------- 161 | 162 | The return value of `then` is itself a promise. This second, 'downstream' 163 | promise is resolved with the return value of the first promise's fulfillment 164 | or rejection handler, or rejected if the handler throws an exception. 165 | 166 | ```js 167 | findUser().then(function (user) { 168 | return user.name; 169 | }, function (reason) { 170 | return 'default name'; 171 | }).then(function (userName) { 172 | // If `findUser` fulfilled, `userName` will be the user's name, otherwise it 173 | // will be `'default name'` 174 | }); 175 | 176 | findUser().then(function (user) { 177 | throw new Error('Found user, but still unhappy'); 178 | }, function (reason) { 179 | throw new Error('`findUser` rejected and we're unhappy'); 180 | }).then(function (value) { 181 | // never reached 182 | }, function (reason) { 183 | // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. 184 | // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. 185 | }); 186 | ``` 187 | If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. 188 | 189 | ```js 190 | findUser().then(function (user) { 191 | throw new PedagogicalException('Upstream error'); 192 | }).then(function (value) { 193 | // never reached 194 | }).then(function (value) { 195 | // never reached 196 | }, function (reason) { 197 | // The `PedgagocialException` is propagated all the way down to here 198 | }); 199 | ``` 200 | 201 | Assimilation 202 | ------------ 203 | 204 | Sometimes the value you want to propagate to a downstream promise can only be 205 | retrieved asynchronously. This can be achieved by returning a promise in the 206 | fulfillment or rejection handler. The downstream promise will then be pending 207 | until the returned promise is settled. This is called *assimilation*. 208 | 209 | ```js 210 | findUser().then(function (user) { 211 | return findCommentsByAuthor(user); 212 | }).then(function (comments) { 213 | // The user's comments are now available 214 | }); 215 | ``` 216 | 217 | If the assimliated promise rejects, then the downstream promise will also reject. 218 | 219 | ```js 220 | findUser().then(function (user) { 221 | return findCommentsByAuthor(user); 222 | }).then(function (comments) { 223 | // If `findCommentsByAuthor` fulfills, we'll have the value here 224 | }, function (reason) { 225 | // If `findCommentsByAuthor` rejects, we'll have the reason here 226 | }); 227 | ``` 228 | 229 | Simple Example 230 | -------------- 231 | 232 | Synchronous Example 233 | 234 | ```javascript 235 | let result; 236 | 237 | try { 238 | result = findResult(); 239 | // success 240 | } catch(reason) { 241 | // failure 242 | } 243 | ``` 244 | 245 | Errback Example 246 | 247 | ```js 248 | findResult(function(result, err){ 249 | if (err) { 250 | // failure 251 | } else { 252 | // success 253 | } 254 | }); 255 | ``` 256 | 257 | Promise Example; 258 | 259 | ```javascript 260 | findResult().then(function(result){ 261 | // success 262 | }, function(reason){ 263 | // failure 264 | }); 265 | ``` 266 | 267 | Advanced Example 268 | -------------- 269 | 270 | Synchronous Example 271 | 272 | ```javascript 273 | let author, books; 274 | 275 | try { 276 | author = findAuthor(); 277 | books = findBooksByAuthor(author); 278 | // success 279 | } catch(reason) { 280 | // failure 281 | } 282 | ``` 283 | 284 | Errback Example 285 | 286 | ```js 287 | 288 | function foundBooks(books) { 289 | 290 | } 291 | 292 | function failure(reason) { 293 | 294 | } 295 | 296 | findAuthor(function(author, err){ 297 | if (err) { 298 | failure(err); 299 | // failure 300 | } else { 301 | try { 302 | findBoooksByAuthor(author, function(books, err) { 303 | if (err) { 304 | failure(err); 305 | } else { 306 | try { 307 | foundBooks(books); 308 | } catch(reason) { 309 | failure(reason); 310 | } 311 | } 312 | }); 313 | } catch(error) { 314 | failure(err); 315 | } 316 | // success 317 | } 318 | }); 319 | ``` 320 | 321 | Promise Example; 322 | 323 | ```javascript 324 | findAuthor(). 325 | then(findBooksByAuthor). 326 | then(function(books){ 327 | // found books 328 | }).catch(function(reason){ 329 | // something went wrong 330 | }); 331 | ``` 332 | 333 | @method then 334 | @param {Function} onFulfilled 335 | @param {Function} onRejected 336 | Useful for tooling. 337 | @return {Promise} 338 | */ 339 | 340 | /** 341 | `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same 342 | as the catch block of a try/catch statement. 343 | 344 | ```js 345 | function findAuthor(){ 346 | throw new Error('couldn't find that author'); 347 | } 348 | 349 | // synchronous 350 | try { 351 | findAuthor(); 352 | } catch(reason) { 353 | // something went wrong 354 | } 355 | 356 | // async with promises 357 | findAuthor().catch(function(reason){ 358 | // something went wrong 359 | }); 360 | ``` 361 | 362 | @method catch 363 | @param {Function} onRejection 364 | Useful for tooling. 365 | @return {Promise} 366 | */ 367 | catch(onRejection) { 368 | return this.then(null, onRejection); 369 | } 370 | 371 | /** 372 | `finally` will be invoked regardless of the promise's fate just as native 373 | try/catch/finally behaves 374 | 375 | Synchronous example: 376 | 377 | ```js 378 | findAuthor() { 379 | if (Math.random() > 0.5) { 380 | throw new Error(); 381 | } 382 | return new Author(); 383 | } 384 | 385 | try { 386 | return findAuthor(); // succeed or fail 387 | } catch(error) { 388 | return findOtherAuther(); 389 | } finally { 390 | // always runs 391 | // doesn't affect the return value 392 | } 393 | ``` 394 | 395 | Asynchronous example: 396 | 397 | ```js 398 | findAuthor().catch(function(reason){ 399 | return findOtherAuther(); 400 | }).finally(function(){ 401 | // author was either found, or not 402 | }); 403 | ``` 404 | 405 | @method finally 406 | @param {Function} callback 407 | @return {Promise} 408 | */ 409 | finally(callback) { 410 | let promise = this; 411 | let constructor = promise.constructor; 412 | 413 | if ( isFunction(callback) ) { 414 | return promise.then(value => constructor.resolve(callback()).then(() => value), 415 | reason => constructor.resolve(callback()).then(() => { throw reason; })); 416 | } 417 | 418 | return promise.then(callback, callback); 419 | } 420 | } 421 | 422 | Promise.prototype.then = then; 423 | export default Promise; 424 | Promise.all = all; 425 | Promise.race = race; 426 | Promise.resolve = Resolve; 427 | Promise.reject = Reject; 428 | Promise._setScheduler = setScheduler; 429 | Promise._setAsap = setAsap; 430 | Promise._asap = asap; 431 | 432 | -------------------------------------------------------------------------------- /lib/es6-promise/promise/all.js: -------------------------------------------------------------------------------- 1 | import Enumerator from '../enumerator'; 2 | 3 | /** 4 | `Promise.all` accepts an array of promises, and returns a new promise which 5 | is fulfilled with an array of fulfillment values for the passed promises, or 6 | rejected with the reason of the first passed promise to be rejected. It casts all 7 | elements of the passed iterable to promises as it runs this algorithm. 8 | 9 | Example: 10 | 11 | ```javascript 12 | let promise1 = resolve(1); 13 | let promise2 = resolve(2); 14 | let promise3 = resolve(3); 15 | let promises = [ promise1, promise2, promise3 ]; 16 | 17 | Promise.all(promises).then(function(array){ 18 | // The array here would be [ 1, 2, 3 ]; 19 | }); 20 | ``` 21 | 22 | If any of the `promises` given to `all` are rejected, the first promise 23 | that is rejected will be given as an argument to the returned promises's 24 | rejection handler. For example: 25 | 26 | Example: 27 | 28 | ```javascript 29 | let promise1 = resolve(1); 30 | let promise2 = reject(new Error("2")); 31 | let promise3 = reject(new Error("3")); 32 | let promises = [ promise1, promise2, promise3 ]; 33 | 34 | Promise.all(promises).then(function(array){ 35 | // Code here never runs because there are rejected promises! 36 | }, function(error) { 37 | // error.message === "2" 38 | }); 39 | ``` 40 | 41 | @method all 42 | @static 43 | @param {Array} entries array of promises 44 | @param {String} label optional string for labeling the promise. 45 | Useful for tooling. 46 | @return {Promise} promise that is fulfilled when all `promises` have been 47 | fulfilled, or rejected if any of them become rejected. 48 | @static 49 | */ 50 | export default function all(entries) { 51 | return new Enumerator(this, entries).promise; 52 | } 53 | -------------------------------------------------------------------------------- /lib/es6-promise/promise/race.js: -------------------------------------------------------------------------------- 1 | import { 2 | isArray 3 | } from "../utils"; 4 | 5 | /** 6 | `Promise.race` returns a new promise which is settled in the same way as the 7 | first passed promise to settle. 8 | 9 | Example: 10 | 11 | ```javascript 12 | let promise1 = new Promise(function(resolve, reject){ 13 | setTimeout(function(){ 14 | resolve('promise 1'); 15 | }, 200); 16 | }); 17 | 18 | let promise2 = new Promise(function(resolve, reject){ 19 | setTimeout(function(){ 20 | resolve('promise 2'); 21 | }, 100); 22 | }); 23 | 24 | Promise.race([promise1, promise2]).then(function(result){ 25 | // result === 'promise 2' because it was resolved before promise1 26 | // was resolved. 27 | }); 28 | ``` 29 | 30 | `Promise.race` is deterministic in that only the state of the first 31 | settled promise matters. For example, even if other promises given to the 32 | `promises` array argument are resolved, but the first settled promise has 33 | become rejected before the other promises became fulfilled, the returned 34 | promise will become rejected: 35 | 36 | ```javascript 37 | let promise1 = new Promise(function(resolve, reject){ 38 | setTimeout(function(){ 39 | resolve('promise 1'); 40 | }, 200); 41 | }); 42 | 43 | let promise2 = new Promise(function(resolve, reject){ 44 | setTimeout(function(){ 45 | reject(new Error('promise 2')); 46 | }, 100); 47 | }); 48 | 49 | Promise.race([promise1, promise2]).then(function(result){ 50 | // Code here never runs 51 | }, function(reason){ 52 | // reason.message === 'promise 2' because promise 2 became rejected before 53 | // promise 1 became fulfilled 54 | }); 55 | ``` 56 | 57 | An example real-world use case is implementing timeouts: 58 | 59 | ```javascript 60 | Promise.race([ajax('foo.json'), timeout(5000)]) 61 | ``` 62 | 63 | @method race 64 | @static 65 | @param {Array} promises array of promises to observe 66 | Useful for tooling. 67 | @return {Promise} a promise which settles in the same way as the first passed 68 | promise to settle. 69 | */ 70 | export default function race(entries) { 71 | /*jshint validthis:true */ 72 | let Constructor = this; 73 | 74 | if (!isArray(entries)) { 75 | return new Constructor((_, reject) => reject(new TypeError('You must pass an array to race.'))); 76 | } else { 77 | return new Constructor((resolve, reject) => { 78 | let length = entries.length; 79 | for (let i = 0; i < length; i++) { 80 | Constructor.resolve(entries[i]).then(resolve, reject); 81 | } 82 | }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/es6-promise/promise/reject.js: -------------------------------------------------------------------------------- 1 | import { 2 | noop, 3 | reject as _reject 4 | } from '../-internal'; 5 | 6 | /** 7 | `Promise.reject` returns a promise rejected with the passed `reason`. 8 | It is shorthand for the following: 9 | 10 | ```javascript 11 | let promise = new Promise(function(resolve, reject){ 12 | reject(new Error('WHOOPS')); 13 | }); 14 | 15 | promise.then(function(value){ 16 | // Code here doesn't run because the promise is rejected! 17 | }, function(reason){ 18 | // reason.message === 'WHOOPS' 19 | }); 20 | ``` 21 | 22 | Instead of writing the above, your code now simply becomes the following: 23 | 24 | ```javascript 25 | let promise = Promise.reject(new Error('WHOOPS')); 26 | 27 | promise.then(function(value){ 28 | // Code here doesn't run because the promise is rejected! 29 | }, function(reason){ 30 | // reason.message === 'WHOOPS' 31 | }); 32 | ``` 33 | 34 | @method reject 35 | @static 36 | @param {Any} reason value that the returned promise will be rejected with. 37 | Useful for tooling. 38 | @return {Promise} a promise rejected with the given `reason`. 39 | */ 40 | export default function reject(reason) { 41 | /*jshint validthis:true */ 42 | let Constructor = this; 43 | let promise = new Constructor(noop); 44 | _reject(promise, reason); 45 | return promise; 46 | } 47 | -------------------------------------------------------------------------------- /lib/es6-promise/promise/resolve.js: -------------------------------------------------------------------------------- 1 | import { 2 | noop, 3 | resolve as _resolve 4 | } from '../-internal'; 5 | 6 | /** 7 | `Promise.resolve` returns a promise that will become resolved with the 8 | passed `value`. It is shorthand for the following: 9 | 10 | ```javascript 11 | let promise = new Promise(function(resolve, reject){ 12 | resolve(1); 13 | }); 14 | 15 | promise.then(function(value){ 16 | // value === 1 17 | }); 18 | ``` 19 | 20 | Instead of writing the above, your code now simply becomes the following: 21 | 22 | ```javascript 23 | let promise = Promise.resolve(1); 24 | 25 | promise.then(function(value){ 26 | // value === 1 27 | }); 28 | ``` 29 | 30 | @method resolve 31 | @static 32 | @param {Any} value value that the returned promise will be resolved with 33 | Useful for tooling. 34 | @return {Promise} a promise that will become fulfilled with the given 35 | `value` 36 | */ 37 | export default function resolve(object) { 38 | /*jshint validthis:true */ 39 | let Constructor = this; 40 | 41 | if (object && typeof object === 'object' && object.constructor === Constructor) { 42 | return object; 43 | } 44 | 45 | let promise = new Constructor(noop); 46 | _resolve(promise, object); 47 | return promise; 48 | } 49 | -------------------------------------------------------------------------------- /lib/es6-promise/then.js: -------------------------------------------------------------------------------- 1 | import { 2 | invokeCallback, 3 | subscribe, 4 | FULFILLED, 5 | REJECTED, 6 | noop, 7 | makePromise, 8 | PROMISE_ID 9 | } from './-internal'; 10 | 11 | import { asap } from './asap'; 12 | 13 | export default function then(onFulfillment, onRejection) { 14 | const parent = this; 15 | 16 | const child = new this.constructor(noop); 17 | 18 | if (child[PROMISE_ID] === undefined) { 19 | makePromise(child); 20 | } 21 | 22 | const { _state } = parent; 23 | 24 | if (_state) { 25 | const callback = arguments[_state - 1]; 26 | asap(() => invokeCallback(_state, child, callback, parent._result)); 27 | } else { 28 | subscribe(parent, child, onFulfillment, onRejection); 29 | } 30 | 31 | return child; 32 | } 33 | -------------------------------------------------------------------------------- /lib/es6-promise/utils.js: -------------------------------------------------------------------------------- 1 | export function objectOrFunction(x) { 2 | let type = typeof x; 3 | return x !== null && (type === 'object' || type === 'function'); 4 | } 5 | 6 | export function isFunction(x) { 7 | return typeof x === 'function'; 8 | } 9 | 10 | export function isMaybeThenable(x) { 11 | return x !== null && typeof x === 'object'; 12 | } 13 | 14 | let _isArray; 15 | if (Array.isArray) { 16 | _isArray = Array.isArray; 17 | } else { 18 | _isArray = x => Object.prototype.toString.call(x) === '[object Array]'; 19 | } 20 | 21 | export const isArray = _isArray; 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6-promise", 3 | "description": "A lightweight library that provides tools for organizing asynchronous code", 4 | "version": "4.2.8", 5 | "author": "Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)", 6 | "browser": { 7 | "vertx": false 8 | }, 9 | "bugs": { 10 | "url": "https://github.com/stefanpenner/es6-promise/issues" 11 | }, 12 | "dependencies": {}, 13 | "devDependencies": { 14 | "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", 15 | "babel-plugin-transform-es2015-block-scoping": "^6.24.1", 16 | "babel-plugin-transform-es2015-classes": "^6.24.1", 17 | "babel-plugin-transform-es2015-computed-properties": "^6.24.1", 18 | "babel-plugin-transform-es2015-constants": "^6.1.4", 19 | "babel-plugin-transform-es2015-destructuring": "^6.23.0", 20 | "babel-plugin-transform-es2015-parameters": "^6.24.1", 21 | "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", 22 | "babel-plugin-transform-es2015-spread": "^6.22.0", 23 | "babel-plugin-transform-es2015-template-literals": "^6.22.0", 24 | "babel6-plugin-strip-class-callcheck": "^6.0.0", 25 | "broccoli-babel-transpiler": "^6.0.0", 26 | "broccoli-concat": "^3.1.0", 27 | "broccoli-merge-trees": "^2.0.0", 28 | "broccoli-rollup": "^2.0.0", 29 | "broccoli-stew": "^1.5.0", 30 | "broccoli-uglify-js": "^0.2.0", 31 | "broccoli-watchify": "^1.0.1", 32 | "ember-cli": "2.18.0-beta.2", 33 | "ember-cli-dependency-checker": "^2.1.0", 34 | "git-repo-version": "1.0.1", 35 | "json3": "^3.3.2", 36 | "mocha": "^4.0.1", 37 | "promises-aplus-tests-phantom": "^2.1.0-revise" 38 | }, 39 | "directories": { 40 | "lib": "lib" 41 | }, 42 | "files": [ 43 | "dist", 44 | "lib", 45 | "es6-promise.d.ts", 46 | "auto.js", 47 | "!dist/test" 48 | ], 49 | "homepage": "https://github.com/stefanpenner/es6-promise", 50 | "jsdelivr": "dist/es6-promise.auto.min.js", 51 | "keywords": [ 52 | "futures", 53 | "polyfill", 54 | "promise", 55 | "promises" 56 | ], 57 | "license": "MIT", 58 | "main": "dist/es6-promise.js", 59 | "namespace": "es6-promise", 60 | "repository": { 61 | "type": "git", 62 | "url": "git://github.com/stefanpenner/es6-promise.git" 63 | }, 64 | "scripts": { 65 | "build": "ember build --environment production", 66 | "prepublishOnly": "ember build --environment production", 67 | "start": "ember s", 68 | "test": "ember test", 69 | "test:browser": "ember test --launch PhantomJS", 70 | "test:node": "ember test --launch Mocha", 71 | "test:server": "ember test --server" 72 | }, 73 | "spm": { 74 | "main": "dist/es6-promise.js" 75 | }, 76 | "typings": "es6-promise.d.ts", 77 | "unpkg": "dist/es6-promise.auto.min.js" 78 | } 79 | -------------------------------------------------------------------------------- /server/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true 3 | } 4 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.get('/', function(req, res) { 3 | res.redirect('/test/'); 4 | }) 5 | }; 6 | -------------------------------------------------------------------------------- /test/extension-test.js: -------------------------------------------------------------------------------- 1 | /*global describe, specify, it, assert */ 2 | 3 | if (typeof Object.getPrototypeOf !== "function") { 4 | Object.getPrototypeOf = "".__proto__ === String.prototype 5 | ? function (object) { 6 | return object.__proto__; 7 | } 8 | : function (object) { 9 | // May break if the constructor has been tampered with 10 | return object.constructor.prototype; 11 | }; 12 | } 13 | 14 | var g = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this; 15 | var Promise = g.adapter.Promise; 16 | var assert = require('assert'); 17 | 18 | describe('tampering', function() { 19 | var resolve = Promise.resolve; 20 | 21 | afterEach(function() { 22 | Promise.resolve = resolve; 23 | }); 24 | 25 | describe('then assimilation', function() { 26 | it('tampered resolved and then', function() { 27 | var one = Promise.resolve(1); 28 | var two = Promise.resolve(2); 29 | var thenCalled = 0; 30 | var resolveCalled = 0; 31 | 32 | two.then = function() { 33 | thenCalled++; 34 | return Promise.prototype.then.apply(this, arguments); 35 | }; 36 | 37 | Promise.resolve = function(x) { 38 | resolveCalled++; 39 | return new Promise(function(resolve) { resolve(x); }); 40 | }; 41 | 42 | return one.then(function() { 43 | return two; 44 | }).then(function(value) { 45 | assert.equal(thenCalled, 1, 'expected then to be called once'); 46 | assert.equal(resolveCalled, 0, 'expected resolve to be called once'); 47 | assert.equal(value, 2, 'expected fulfillment value to be 2'); 48 | }); 49 | }); 50 | 51 | it('tampered resolved', function() { 52 | var one = Promise.resolve(1); 53 | var two = Promise.resolve(2); 54 | var thenCalled = 0; 55 | var resolveCalled = 0; 56 | 57 | Promise.resolve = function(x) { 58 | resolveCalled++; 59 | return new Promise(function(resolve) { resolve(x); }); 60 | }; 61 | 62 | return one.then(function() { 63 | return two; 64 | }).then(function(value) { 65 | assert.equal(resolveCalled, 0, 'expected resolve to be called once'); 66 | assert.equal(value, 2, 'expected fulfillment value to be 2'); 67 | }); 68 | }); 69 | 70 | describe('alternative constructor', function() { 71 | it('tampered resolved and then', function() { 72 | var one = Promise.resolve(1); 73 | var two = Promise.resolve(2); 74 | var thenCalled = 0; 75 | var resolveCalled = 0; 76 | var invokedAlternativeConstructor = 0; 77 | 78 | two.then = function() { 79 | thenCalled++; 80 | return Promise.prototype.then.apply(this, arguments); 81 | }; 82 | 83 | function AlternativeConstructor(executor) { 84 | invokedAlternativeConstructor++; 85 | var followers = this.followers = []; 86 | executor(function(value) { 87 | followers.forEach(function(onFulfillment) { 88 | onFulfillment(value) 89 | }); 90 | }, function() { 91 | throw TypeError('No Rejections supported'); 92 | }); 93 | } 94 | 95 | AlternativeConstructor.resolve = function(x) { 96 | resolveCalled++; 97 | return new Promise(function(resolve) { resolve(x); }); 98 | }; 99 | 100 | AlternativeConstructor.prototype.then = function(onFulfillment, onRejection) { 101 | this.followers.push(onFulfillment); 102 | }; 103 | 104 | AlternativeConstructor.resolve = function(x) { 105 | resolveCalled++; 106 | return new Promise(function(resolve) { resolve(x); }); 107 | }; 108 | 109 | one.constructor = AlternativeConstructor 110 | 111 | return one.then(function() { 112 | return two; 113 | }).then(function(value) { 114 | 115 | assert.equal(invokedAlternativeConstructor, 1, 'expected AlternativeConstructor to be invoked once'); 116 | assert.equal(thenCalled, 1, 'expected then to be called once'); 117 | assert.equal(resolveCalled, 0, 'expected resolve to be called once'); 118 | assert.equal(value, 2, 'expected fulfillment value to be 2'); 119 | }); 120 | }); 121 | }); 122 | 123 | describe('Promise.all', function() { 124 | it('tampered resolved and then', function() { 125 | var two = Promise.resolve(2); 126 | var thenCalled = 0; 127 | var resolveCalled = 0; 128 | 129 | two.then = function() { 130 | thenCalled++; 131 | return Promise.prototype.then.apply(this, arguments); 132 | }; 133 | 134 | Promise.resolve = function(x) { 135 | resolveCalled++; 136 | return new Promise(function(resolve) { resolve(x); }); 137 | }; 138 | 139 | return Promise.all([two]).then(function(value) { 140 | assert.equal(thenCalled, 1); 141 | assert.equal(resolveCalled, 1); 142 | assert.deepEqual(value, [2]); 143 | }); 144 | }); 145 | 146 | it('alternative constructor and tampered then', function() { 147 | var two = Promise.resolve(2); 148 | var thenCalled = 0; 149 | var resolveCalled = 0; 150 | 151 | two.then = function() { 152 | thenCalled++; 153 | return Promise.prototype.then.apply(this, arguments); 154 | }; 155 | 156 | function AlternativeConstructor(executor) { 157 | var followers = this.followers = []; 158 | executor(function(value) { 159 | followers.forEach(function(onFulfillment) { 160 | onFulfillment(value) 161 | }); 162 | }, function() { 163 | throw TypeError('No Rejections supported'); 164 | }); 165 | } 166 | 167 | AlternativeConstructor.resolve = function(x) { 168 | resolveCalled++; 169 | return new Promise(function(resolve) { resolve(x); }); 170 | }; 171 | 172 | AlternativeConstructor.prototype.then = function(onFulfillment, onRejection) { 173 | this.followers.push(onFulfillment); 174 | }; 175 | 176 | return Promise.all.call(AlternativeConstructor, [two]).then(function(value) { 177 | assert.equal(thenCalled, 1); 178 | assert.equal(resolveCalled, 1); 179 | assert.deepEqual(value, [2]); 180 | }); 181 | }); 182 | }); 183 | 184 | describe('core-js species test', function() { 185 | it('foreign thenable has correct internal slots', function() { 186 | var p = Promise.resolve(); 187 | 188 | function NewConstructor(it) { 189 | it(function(){}, function(){}) 190 | } 191 | 192 | p.constructor = NewConstructor; 193 | 194 | var f = p.then(function(){}); 195 | assert(f instanceof NewConstructor); 196 | }); 197 | }); 198 | describe('Promise.race', function() { 199 | it('tampered resolved and then', function() { 200 | var two = Promise.resolve(2); 201 | var thenCalled = 0; 202 | var resolveCalled = 0; 203 | 204 | two.then = function() { 205 | thenCalled++; 206 | return Promise.prototype.then.apply(this, arguments); 207 | }; 208 | 209 | Promise.resolve = function(x) { 210 | resolveCalled++; 211 | return new Promise(function(resolve) { resolve(x); }); 212 | }; 213 | 214 | return Promise.race([two]).then(function(value) { 215 | assert.equal(thenCalled, 1); 216 | assert.equal(resolveCalled, 1); 217 | assert.deepEqual(value, 2); 218 | }); 219 | }); 220 | 221 | it('alternative constructor and tampered then', function() { 222 | var two = Promise.resolve(2); 223 | var thenCalled = 0; 224 | var resolveCalled = 0; 225 | 226 | two.then = function() { 227 | thenCalled++; 228 | return Promise.prototype.then.apply(this, arguments); 229 | }; 230 | 231 | function AlternativeConstructor(executor) { 232 | var followers = this.followers = []; 233 | executor(function(value) { 234 | followers.forEach(function(onFulfillment) { 235 | onFulfillment(value) 236 | }); 237 | }, function() { 238 | throw TypeError('No Rejections supported'); 239 | }); 240 | } 241 | 242 | AlternativeConstructor.resolve = function(x) { 243 | resolveCalled++; 244 | return new Promise(function(resolve) { resolve(x); }); 245 | }; 246 | 247 | AlternativeConstructor.prototype.then = function(onFulfillment, onRejection) { 248 | this.followers.push(onFulfillment); 249 | }; 250 | 251 | return Promise.race.call(AlternativeConstructor, [two]).then(function(value) { 252 | assert.equal(thenCalled, 1); 253 | assert.equal(resolveCalled, 1); 254 | assert.deepEqual(value, 2); 255 | }); 256 | }); 257 | }); 258 | 259 | }); 260 | }); 261 | 262 | describe("extensions", function() { 263 | describe("Promise constructor", function() { 264 | it('should exist and have length 1', function() { 265 | assert(Promise); 266 | assert.equal(Promise.length, 1); 267 | }); 268 | 269 | it('should fulfill if `resolve` is called with a value', function(done) { 270 | var promise = new Promise(function(resolve) { resolve('value'); }); 271 | 272 | promise.then(function(value) { 273 | assert.equal(value, 'value'); 274 | done(); 275 | }); 276 | }); 277 | 278 | it('should reject if `reject` is called with a reason', function(done) { 279 | var promise = new Promise(function(resolve, reject) { reject('reason'); }); 280 | 281 | promise.then(function() { 282 | assert(false); 283 | done(); 284 | }, function(reason) { 285 | assert.equal(reason, 'reason'); 286 | done(); 287 | }); 288 | }); 289 | 290 | it('should be a constructor', function() { 291 | var promise = new Promise(function() {}); 292 | 293 | assert.equal(Object.getPrototypeOf(promise), Promise.prototype, '[[Prototype]] equals Promise.prototype'); 294 | assert.equal(promise.constructor, Promise, 'constructor property of instances is set correctly'); 295 | assert.equal(Promise.prototype.constructor, Promise, 'constructor property of prototype is set correctly'); 296 | }); 297 | 298 | it('should NOT work without `new`', function() { 299 | assert.throws(function(){ 300 | Promise(function(resolve) { resolve('value'); }); 301 | }, TypeError) 302 | }); 303 | 304 | it('should throw a `TypeError` if not given a function', function() { 305 | assert.throws(function () { 306 | new Promise(); 307 | }, TypeError); 308 | 309 | assert.throws(function () { 310 | new Promise({}); 311 | }, TypeError); 312 | 313 | assert.throws(function () { 314 | new Promise('boo!'); 315 | }, TypeError); 316 | }); 317 | 318 | it('should reject on resolver exception', function(done) { 319 | new Promise(function() { 320 | throw 'error'; 321 | }).then(null, function(e) { 322 | assert.equal(e, 'error'); 323 | done(); 324 | }); 325 | }); 326 | 327 | it('should not resolve multiple times', function(done) { 328 | var resolver, rejector, fulfilled = 0, rejected = 0; 329 | var thenable = { 330 | then: function(resolve, reject) { 331 | resolver = resolve; 332 | rejector = reject; 333 | } 334 | }; 335 | 336 | var promise = new Promise(function(resolve) { 337 | resolve(1); 338 | }); 339 | 340 | promise.then(function(value){ 341 | return thenable; 342 | }).then(function(value){ 343 | fulfilled++; 344 | }, function(reason) { 345 | rejected++; 346 | }); 347 | 348 | setTimeout(function() { 349 | resolver(1); 350 | resolver(1); 351 | rejector(1); 352 | rejector(1); 353 | 354 | setTimeout(function() { 355 | assert.equal(fulfilled, 1); 356 | assert.equal(rejected, 0); 357 | done(); 358 | }, 20); 359 | }, 20); 360 | 361 | }); 362 | 363 | describe('assimilation', function() { 364 | it('should assimilate if `resolve` is called with a fulfilled promise', function(done) { 365 | var originalPromise = new Promise(function(resolve) { resolve('original value'); }); 366 | var promise = new Promise(function(resolve) { resolve(originalPromise); }); 367 | 368 | promise.then(function(value) { 369 | assert.equal(value, 'original value'); 370 | done(); 371 | }); 372 | }); 373 | 374 | it('should assimilate if `resolve` is called with a rejected promise', function(done) { 375 | var originalPromise = new Promise(function(resolve, reject) { reject('original reason'); }); 376 | var promise = new Promise(function(resolve) { resolve(originalPromise); }); 377 | 378 | promise.then(function() { 379 | assert(false); 380 | done(); 381 | }, function(reason) { 382 | assert.equal(reason, 'original reason'); 383 | done(); 384 | }); 385 | }); 386 | 387 | it('should assimilate if `resolve` is called with a fulfilled thenable', function(done) { 388 | var originalThenable = { 389 | then: function (onFulfilled) { 390 | setTimeout(function() { onFulfilled('original value'); }, 0); 391 | } 392 | }; 393 | var promise = new Promise(function(resolve) { resolve(originalThenable); }); 394 | 395 | promise.then(function(value) { 396 | assert.equal(value, 'original value'); 397 | done(); 398 | }); 399 | }); 400 | 401 | it('should assimilate if `resolve` is called with a rejected thenable', function(done) { 402 | var originalThenable = { 403 | then: function (onFulfilled, onRejected) { 404 | setTimeout(function() { onRejected('original reason'); }, 0); 405 | } 406 | }; 407 | var promise = new Promise(function(resolve) { resolve(originalThenable); }); 408 | 409 | promise.then(function() { 410 | assert(false); 411 | done(); 412 | }, function(reason) { 413 | assert.equal(reason, 'original reason'); 414 | done(); 415 | }); 416 | }); 417 | 418 | 419 | it('should assimilate two levels deep, for fulfillment of self fulfilling promises', function(done) { 420 | var originalPromise, promise; 421 | originalPromise = new Promise(function(resolve) { 422 | setTimeout(function() { 423 | resolve(originalPromise); 424 | }, 0) 425 | }); 426 | 427 | promise = new Promise(function(resolve) { 428 | setTimeout(function() { 429 | resolve(originalPromise); 430 | }, 0); 431 | }); 432 | 433 | promise.then(function(value) { 434 | assert(false); 435 | done(); 436 | }).catch(function(reason) { 437 | assert.equal(reason.message, "You cannot resolve a promise with itself"); 438 | assert(reason instanceof TypeError); 439 | done(); 440 | }); 441 | }); 442 | 443 | it('should assimilate two levels deep, for fulfillment', function(done) { 444 | var originalPromise = new Promise(function(resolve) { resolve('original value'); }); 445 | var nextPromise = new Promise(function(resolve) { resolve(originalPromise); }); 446 | var promise = new Promise(function(resolve) { resolve(nextPromise); }); 447 | 448 | promise.then(function(value) { 449 | assert.equal(value, 'original value'); 450 | done(); 451 | }); 452 | }); 453 | 454 | it('should assimilate two levels deep, for rejection', function(done) { 455 | var originalPromise = new Promise(function(resolve, reject) { reject('original reason'); }); 456 | var nextPromise = new Promise(function(resolve) { resolve(originalPromise); }); 457 | var promise = new Promise(function(resolve) { resolve(nextPromise); }); 458 | 459 | promise.then(function() { 460 | assert(false); 461 | done(); 462 | }, function(reason) { 463 | assert.equal(reason, 'original reason'); 464 | done(); 465 | }); 466 | }); 467 | 468 | it('should assimilate three levels deep, mixing thenables and promises (fulfilled case)', function(done) { 469 | var originalPromise = new Promise(function(resolve) { resolve('original value'); }); 470 | var intermediateThenable = { 471 | then: function (onFulfilled) { 472 | setTimeout(function() { onFulfilled(originalPromise); }, 0); 473 | } 474 | }; 475 | var promise = new Promise(function(resolve) { resolve(intermediateThenable); }); 476 | 477 | promise.then(function(value) { 478 | assert.equal(value, 'original value'); 479 | done(); 480 | }); 481 | }); 482 | 483 | it('should assimilate three levels deep, mixing thenables and promises (rejected case)', function(done) { 484 | var originalPromise = new Promise(function(resolve, reject) { reject('original reason'); }); 485 | var intermediateThenable = { 486 | then: function (onFulfilled) { 487 | setTimeout(function() { onFulfilled(originalPromise); }, 0); 488 | } 489 | }; 490 | var promise = new Promise(function(resolve) { resolve(intermediateThenable); }); 491 | 492 | promise.then(function() { 493 | assert(false); 494 | done(); 495 | }, function(reason) { 496 | assert.equal(reason, 'original reason'); 497 | done(); 498 | }); 499 | }); 500 | }); 501 | }); 502 | 503 | describe('Promise.all', function() { 504 | testAll(function(){ 505 | return Promise.all.apply(Promise, arguments); 506 | }); 507 | }); 508 | 509 | function testAll(all) { 510 | it('should exist', function() { 511 | assert(all); 512 | }); 513 | 514 | it('works with plan pojo input', function(done) { 515 | all([ 516 | {} 517 | ]).then(function(result) { 518 | assert.deepEqual(result, [{}]); 519 | done(); 520 | }); 521 | }); 522 | 523 | it('throws when not passed an array', function(done) { 524 | var nothing = assertRejection(all()); 525 | var string = assertRejection(all('')); 526 | var object = assertRejection(all({})); 527 | 528 | Promise.all([ 529 | nothing, 530 | string, 531 | object 532 | ]).then(function(){ done(); }); 533 | }); 534 | 535 | specify('fulfilled only after all of the other promises are fulfilled', function(done) { 536 | var firstResolved, secondResolved, firstResolver, secondResolver; 537 | 538 | var first = new Promise(function(resolve) { 539 | firstResolver = resolve; 540 | }); 541 | first.then(function() { 542 | firstResolved = true; 543 | }); 544 | 545 | var second = new Promise(function(resolve) { 546 | secondResolver = resolve; 547 | }); 548 | second.then(function() { 549 | secondResolved = true; 550 | }); 551 | 552 | setTimeout(function() { 553 | firstResolver(true); 554 | }, 0); 555 | 556 | setTimeout(function() { 557 | secondResolver(true); 558 | }, 0); 559 | 560 | all([first, second]).then(function() { 561 | assert(firstResolved); 562 | assert(secondResolved); 563 | done(); 564 | }); 565 | }); 566 | 567 | specify('rejected as soon as a promise is rejected', function(done) { 568 | var firstResolver, secondResolver; 569 | 570 | var first = new Promise(function(resolve, reject) { 571 | firstResolver = { resolve: resolve, reject: reject }; 572 | }); 573 | 574 | var second = new Promise(function(resolve, reject) { 575 | secondResolver = { resolve: resolve, reject: reject }; 576 | }); 577 | 578 | setTimeout(function() { 579 | firstResolver.reject({}); 580 | }, 0); 581 | 582 | var firstWasRejected, secondCompleted; 583 | 584 | first.catch(function(){ 585 | firstWasRejected = true; 586 | }); 587 | 588 | second.then(function(){ 589 | secondCompleted = true; 590 | }, function() { 591 | secondCompleted = true; 592 | }); 593 | 594 | all([first, second]).then(function() { 595 | assert(false); 596 | }, function() { 597 | assert(firstWasRejected); 598 | assert(!secondCompleted); 599 | done(); 600 | }); 601 | }); 602 | 603 | specify('passes the resolved values of each promise to the callback in the correct order', function(done) { 604 | var firstResolver, secondResolver, thirdResolver; 605 | 606 | var first = new Promise(function(resolve, reject) { 607 | firstResolver = { resolve: resolve, reject: reject }; 608 | }); 609 | 610 | var second = new Promise(function(resolve, reject) { 611 | secondResolver = { resolve: resolve, reject: reject }; 612 | }); 613 | 614 | var third = new Promise(function(resolve, reject) { 615 | thirdResolver = { resolve: resolve, reject: reject }; 616 | }); 617 | 618 | thirdResolver.resolve(3); 619 | firstResolver.resolve(1); 620 | secondResolver.resolve(2); 621 | 622 | all([first, second, third]).then(function(results) { 623 | assert(results.length === 3); 624 | assert(results[0] === 1); 625 | assert(results[1] === 2); 626 | assert(results[2] === 3); 627 | done(); 628 | }); 629 | }); 630 | 631 | specify('resolves an empty array passed to all()', function(done) { 632 | all([]).then(function(results) { 633 | assert(results.length === 0); 634 | done(); 635 | }); 636 | }); 637 | 638 | specify('works with null', function(done) { 639 | all([null]).then(function(results) { 640 | assert.equal(results[0], null); 641 | done(); 642 | }); 643 | }); 644 | 645 | specify('works with a mix of promises and thenables and non-promises', function(done) { 646 | var promise = new Promise(function(resolve) { resolve(1); }); 647 | var syncThenable = { then: function (onFulfilled) { onFulfilled(2); } }; 648 | var asyncThenable = { then: function (onFulfilled) { setTimeout(function() { onFulfilled(3); }, 0); } }; 649 | var nonPromise = 4; 650 | 651 | all([promise, syncThenable, asyncThenable, nonPromise]).then(function(results) { 652 | assert.deepEqual(results, [1, 2, 3, 4]); 653 | done(); 654 | }).catch(done); 655 | }); 656 | } 657 | 658 | describe("reject", function(){ 659 | specify("it should exist", function(){ 660 | assert(Promise.reject); 661 | }); 662 | 663 | describe('it rejects', function(){ 664 | var reason = 'the reason', 665 | promise = Promise.reject(reason); 666 | 667 | promise.then(function(){ 668 | assert(false, 'should not fulfill'); 669 | }, function(actualReason){ 670 | assert.equal(reason, actualReason); 671 | }); 672 | }); 673 | }); 674 | 675 | function assertRejection(promise) { 676 | return promise.then(function(){ 677 | assert(false, 'expected rejection, but got fulfillment'); 678 | }, function(reason){ 679 | assert(reason instanceof Error); 680 | }); 681 | } 682 | 683 | describe('race', function() { 684 | it("should exist", function() { 685 | assert(Promise.race); 686 | }); 687 | 688 | it("throws when not passed an array", function(done) { 689 | var nothing = assertRejection(Promise.race()); 690 | var string = assertRejection(Promise.race('')); 691 | var object = assertRejection(Promise.race({})); 692 | 693 | Promise.all([ 694 | nothing, 695 | string, 696 | object 697 | ]).then(function(){ done(); }); 698 | }); 699 | 700 | specify('fulfilled after one of the other promises are fulfilled', function(done) { 701 | var firstResolved, secondResolved, firstResolver, secondResolver; 702 | 703 | var first = new Promise(function(resolve) { 704 | firstResolver = resolve; 705 | }); 706 | first.then(function() { 707 | firstResolved = true; 708 | }); 709 | 710 | var second = new Promise(function(resolve) { 711 | secondResolver = resolve; 712 | }); 713 | second.then(function() { 714 | secondResolved = true; 715 | }); 716 | 717 | setTimeout(function() { 718 | firstResolver(true); 719 | }, 100); 720 | 721 | setTimeout(function() { 722 | secondResolver(true); 723 | }, 0); 724 | 725 | Promise.race([first, second]).then(function() { 726 | assert(secondResolved); 727 | assert.equal(firstResolved, undefined); 728 | done(); 729 | }); 730 | }); 731 | 732 | specify('the race begins on nextTurn and prioritized by array entry', function(done) { 733 | var firstResolver, secondResolver, nonPromise = 5; 734 | 735 | var first = new Promise(function(resolve, reject) { 736 | resolve(true); 737 | }); 738 | 739 | var second = new Promise(function(resolve, reject) { 740 | resolve(false); 741 | }); 742 | 743 | Promise.race([first, second, nonPromise]).then(function(value) { 744 | assert.equal(value, true); 745 | done(); 746 | }); 747 | }); 748 | 749 | specify('rejected as soon as a promise is rejected', function(done) { 750 | var firstResolver, secondResolver; 751 | 752 | var first = new Promise(function(resolve, reject) { 753 | firstResolver = { resolve: resolve, reject: reject }; 754 | }); 755 | 756 | var second = new Promise(function(resolve, reject) { 757 | secondResolver = { resolve: resolve, reject: reject }; 758 | }); 759 | 760 | setTimeout(function() { 761 | firstResolver.reject({}); 762 | }, 0); 763 | 764 | var firstWasRejected, secondCompleted; 765 | 766 | first.catch(function(){ 767 | firstWasRejected = true; 768 | }); 769 | 770 | second.then(function(){ 771 | secondCompleted = true; 772 | }, function() { 773 | secondCompleted = true; 774 | }); 775 | 776 | Promise.race([first, second]).then(function() { 777 | assert(false); 778 | }, function() { 779 | assert(firstWasRejected); 780 | assert(!secondCompleted); 781 | done(); 782 | }); 783 | }); 784 | 785 | specify('resolves an empty array to forever pending Promise', function(done) { 786 | var foreverPendingPromise = Promise.race([]), 787 | wasSettled = false; 788 | 789 | foreverPendingPromise.then(function() { 790 | wasSettled = true; 791 | }, function() { 792 | wasSettled = true; 793 | }); 794 | 795 | setTimeout(function() { 796 | assert(!wasSettled); 797 | done(); 798 | }, 50); 799 | }); 800 | 801 | specify('works with a mix of promises and thenables', function(done) { 802 | var promise = new Promise(function(resolve) { setTimeout(function() { resolve(1); }, 10); }), 803 | syncThenable = { then: function (onFulfilled) { onFulfilled(2); } }; 804 | 805 | Promise.race([promise, syncThenable]).then(function(result) { 806 | assert(result, 2); 807 | done(); 808 | }); 809 | }); 810 | 811 | specify('works with a mix of thenables and non-promises', function (done) { 812 | var asyncThenable = { then: function (onFulfilled) { setTimeout(function() { onFulfilled(3); }, 0); } }, 813 | nonPromise = 4; 814 | 815 | Promise.race([asyncThenable, nonPromise]).then(function(result) { 816 | assert(result, 4); 817 | done(); 818 | }); 819 | }); 820 | }); 821 | 822 | describe("resolve", function(){ 823 | specify("it should exist", function(){ 824 | assert(Promise.resolve); 825 | }); 826 | 827 | describe("1. If x is a promise, adopt its state ", function(){ 828 | specify("1.1 If x is pending, promise must remain pending until x is fulfilled or rejected.", function(done){ 829 | var expectedValue, resolver, thenable, wrapped; 830 | 831 | expectedValue = 'the value'; 832 | thenable = { 833 | then: function(resolve, reject){ 834 | resolver = resolve; 835 | } 836 | }; 837 | 838 | wrapped = Promise.resolve(thenable); 839 | 840 | wrapped.then(function(value){ 841 | assert(value === expectedValue); 842 | done(); 843 | }); 844 | 845 | setTimeout(function(){ 846 | resolver(expectedValue); 847 | }, 10); 848 | }); 849 | 850 | specify("1.2 If/when x is fulfilled, fulfill promise with the same value.", function(done){ 851 | var expectedValue, thenable, wrapped; 852 | 853 | expectedValue = 'the value'; 854 | thenable = { 855 | then: function(resolve, reject){ 856 | resolve(expectedValue); 857 | } 858 | }; 859 | 860 | wrapped = Promise.resolve(thenable); 861 | 862 | wrapped.then(function(value){ 863 | assert(value === expectedValue); 864 | done(); 865 | }) 866 | }); 867 | 868 | specify("1.3 If/when x is rejected, reject promise with the same reason.", function(done){ 869 | var expectedError, thenable, wrapped; 870 | 871 | expectedError = new Error(); 872 | thenable = { 873 | then: function(resolve, reject){ 874 | reject(expectedError); 875 | } 876 | }; 877 | 878 | wrapped = Promise.resolve(thenable); 879 | 880 | wrapped.then(null, function(error){ 881 | assert(error === expectedError); 882 | done(); 883 | }); 884 | }); 885 | }); 886 | 887 | describe("2. Otherwise, if x is an object or function,", function(){ 888 | specify("2.1 Let then x.then", function(done){ 889 | var accessCount, resolver, wrapped, thenable; 890 | 891 | accessCount = 0; 892 | thenable = { }; 893 | 894 | // we likely don't need to test this, if the browser doesn't support it 895 | if (typeof Object.defineProperty !== "function") { done(); return; } 896 | 897 | Object.defineProperty(thenable, 'then', { 898 | get: function(){ 899 | accessCount++; 900 | 901 | if (accessCount > 1) { 902 | throw new Error(); 903 | } 904 | 905 | return function(){ }; 906 | } 907 | }); 908 | 909 | assert(accessCount === 0); 910 | 911 | wrapped = Promise.resolve(thenable); 912 | 913 | assert(accessCount === 1); 914 | 915 | done(); 916 | }); 917 | 918 | specify("2.2 If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.", function(done){ 919 | var wrapped, thenable, expectedError; 920 | 921 | expectedError = new Error(); 922 | thenable = { }; 923 | 924 | // we likely don't need to test this, if the browser doesn't support it 925 | if (typeof Object.defineProperty !== "function") { done(); return; } 926 | 927 | Object.defineProperty(thenable, 'then', { 928 | get: function(){ 929 | throw expectedError; 930 | } 931 | }); 932 | 933 | wrapped = Promise.resolve(thenable); 934 | 935 | wrapped.then(null, function(error){ 936 | assert(error === expectedError, 'incorrect exception was thrown'); 937 | done(); 938 | }); 939 | }); 940 | 941 | describe('2.3. If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where', function(){ 942 | specify('2.3.1 If/when resolvePromise is called with a value y, run Resolve(promise, y)', function(done){ 943 | var expectedSuccess, resolver, rejector, thenable, wrapped, calledThis; 944 | 945 | thenable = { 946 | then: function(resolve, reject){ 947 | calledThis = this; 948 | resolver = resolve; 949 | rejector = reject; 950 | } 951 | }; 952 | 953 | expectedSuccess = 'success'; 954 | wrapped = Promise.resolve(thenable); 955 | 956 | wrapped.then(function(success){ 957 | assert(calledThis === thenable, 'this must be the thenable'); 958 | assert(success === expectedSuccess, 'rejected promise with x'); 959 | done(); 960 | }); 961 | 962 | setTimeout(function() { 963 | resolver(expectedSuccess); 964 | }, 20); 965 | }); 966 | 967 | specify('2.3.2 If/when rejectPromise is called with a reason r, reject promise with r.', function(done){ 968 | var expectedError, resolver, rejector, thenable, wrapped, calledThis; 969 | 970 | thenable = { 971 | then: function(resolve, reject){ 972 | calledThis = this; 973 | resolver = resolve; 974 | rejector = reject; 975 | } 976 | }; 977 | 978 | expectedError = new Error(); 979 | 980 | wrapped = Promise.resolve(thenable); 981 | 982 | wrapped.then(null, function(error){ 983 | assert(error === expectedError, 'rejected promise with x'); 984 | done(); 985 | }); 986 | 987 | setTimeout(function() { 988 | rejector(expectedError); 989 | }, 20); 990 | }); 991 | 992 | specify("2.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored", function(done){ 993 | var expectedError, expectedSuccess, resolver, rejector, thenable, wrapped, calledThis, 994 | calledRejected, calledResolved; 995 | 996 | calledRejected = 0; 997 | calledResolved = 0; 998 | 999 | thenable = { 1000 | then: function(resolve, reject){ 1001 | calledThis = this; 1002 | resolver = resolve; 1003 | rejector = reject; 1004 | } 1005 | }; 1006 | 1007 | expectedError = new Error(); 1008 | 1009 | wrapped = Promise.resolve(thenable); 1010 | 1011 | wrapped.then(function(){ 1012 | calledResolved++; 1013 | }, function(error){ 1014 | calledRejected++; 1015 | assert(calledResolved === 0, 'never resolved'); 1016 | assert(calledRejected === 1, 'rejected only once'); 1017 | assert(error === expectedError, 'rejected promise with x'); 1018 | }); 1019 | 1020 | setTimeout(function() { 1021 | rejector(expectedError); 1022 | rejector(expectedError); 1023 | 1024 | rejector('foo'); 1025 | 1026 | resolver('bar'); 1027 | resolver('baz'); 1028 | }, 20); 1029 | 1030 | setTimeout(function(){ 1031 | assert(calledRejected === 1, 'only rejected once'); 1032 | assert(calledResolved === 0, 'never resolved'); 1033 | done(); 1034 | }, 50); 1035 | }); 1036 | 1037 | describe("2.3.4 If calling then throws an exception e", function(){ 1038 | specify("2.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.", function(done){ 1039 | var expectedSuccess, resolver, rejector, thenable, wrapped, calledThis, 1040 | calledRejected, calledResolved; 1041 | 1042 | expectedSuccess = 'success'; 1043 | 1044 | thenable = { 1045 | then: function(resolve, reject){ 1046 | resolve(expectedSuccess); 1047 | throw expectedError; 1048 | } 1049 | }; 1050 | 1051 | wrapped = Promise.resolve(thenable); 1052 | 1053 | wrapped.then(function(success){ 1054 | assert(success === expectedSuccess, 'resolved not errored'); 1055 | done(); 1056 | }); 1057 | }); 1058 | 1059 | specify("2.3.4.2 Otherwise, reject promise with e as the reason.", function(done) { 1060 | var expectedError, resolver, rejector, thenable, wrapped, calledThis, callCount; 1061 | 1062 | expectedError = new Error(); 1063 | callCount = 0; 1064 | 1065 | thenable = { then: function() { throw expectedError; } }; 1066 | 1067 | wrapped = Promise.resolve(thenable); 1068 | 1069 | wrapped.then(null, function(error){ 1070 | callCount++; 1071 | assert(expectedError === error, 'expected the correct error to be rejected'); 1072 | done(); 1073 | }); 1074 | 1075 | assert(callCount === 0, 'expected async, was sync'); 1076 | }); 1077 | }); 1078 | }); 1079 | 1080 | specify("2.4 If then is not a function, fulfill promise with x", function(done){ 1081 | var expectedError, resolver, rejector, thenable, wrapped, calledThis, callCount; 1082 | 1083 | thenable = { then: 3 }; 1084 | callCount = 0; 1085 | wrapped = Promise.resolve(thenable); 1086 | 1087 | wrapped.then(function(success){ 1088 | callCount++; 1089 | assert(thenable === success, 'fulfilled promise with x'); 1090 | done(); 1091 | }); 1092 | 1093 | assert(callCount === 0, 'expected async, was sync'); 1094 | }); 1095 | }); 1096 | 1097 | describe("3. If x is not an object or function, ", function(){ 1098 | specify("fulfill promise with x.", function(done){ 1099 | var thenable, callCount, wrapped; 1100 | 1101 | thenable = null; 1102 | callCount = 0; 1103 | wrapped = Promise.resolve(thenable); 1104 | 1105 | wrapped.then(function(success){ 1106 | callCount++; 1107 | assert(success === thenable, 'fulfilled promise with x'); 1108 | done(); 1109 | }, function(a){ 1110 | assert(false, 'should not also reject'); 1111 | }); 1112 | 1113 | assert(callCount === 0, 'expected async, was sync'); 1114 | }); 1115 | }); 1116 | }); 1117 | 1118 | if (typeof Worker !== 'undefined' && navigator.userAgent.indexOf('PhantomJS') < 1) { 1119 | describe('web worker', function () { 1120 | it('should work', function (done) { 1121 | this.timeout(2000); 1122 | var worker = new Worker('./worker.js'); 1123 | worker.addEventListener('error', function(reason) { 1124 | done(new Error("Test failed:" + reason)); 1125 | }); 1126 | worker.addEventListener('message', function (e) { 1127 | worker.terminate(); 1128 | assert.equal(e.data, 'pong'); 1129 | done(); 1130 | }); 1131 | worker.postMessage('ping'); 1132 | }); 1133 | }); 1134 | } 1135 | }); 1136 | 1137 | // thanks to @wizardwerdna for the test case -> https://github.com/tildeio/rsvp.js/issues/66 1138 | // Only run these tests in node (phantomjs cannot handle them) 1139 | if (typeof module !== 'undefined' && module.exports) { 1140 | 1141 | describe("using reduce to sum integers using promises", function(){ 1142 | it("should build the promise pipeline without error", function(){ 1143 | var array, iters, pZero, i; 1144 | 1145 | array = []; 1146 | iters = 1000; 1147 | 1148 | for (i=1; i<=iters; i++) { 1149 | array.push(i); 1150 | } 1151 | 1152 | pZero = Promise.resolve(0); 1153 | 1154 | array.reduce(function(promise, nextVal) { 1155 | return promise.then(function(currentVal) { 1156 | return Promise.resolve(currentVal + nextVal); 1157 | }); 1158 | }, pZero); 1159 | }); 1160 | 1161 | it("should get correct answer without blowing the nextTick stack", function(done){ 1162 | var pZero, array, iters, result, i; 1163 | 1164 | pZero = Promise.resolve(0); 1165 | 1166 | array = []; 1167 | iters = 1000; 1168 | 1169 | for (i=1; i<=iters; i++) { 1170 | array.push(i); 1171 | } 1172 | 1173 | result = array.reduce(function(promise, nextVal) { 1174 | return promise.then(function(currentVal) { 1175 | return Promise.resolve(currentVal + nextVal); 1176 | }); 1177 | }, pZero); 1178 | 1179 | result.then(function(value){ 1180 | assert.equal(value, (iters*(iters+1)/2)); 1181 | done(); 1182 | }); 1183 | }); 1184 | }); 1185 | } 1186 | 1187 | // Kudos to @Octane at https://github.com/getify/native-promise-only/issues/5 for this, and @getify for pinging me. 1188 | describe("Thenables should not be able to run code during assimilation", function () { 1189 | specify("resolving to a thenable", function () { 1190 | var thenCalled = false; 1191 | var thenable = { 1192 | then: function () { 1193 | thenCalled = true; 1194 | } 1195 | }; 1196 | 1197 | Promise.resolve(thenable); 1198 | assert.strictEqual(thenCalled, false); 1199 | }); 1200 | 1201 | specify("resolving to an evil promise", function () { 1202 | var thenCalled = false; 1203 | var evilPromise = Promise.resolve(); 1204 | evilPromise.then = function () { 1205 | thenCalled = true; 1206 | }; 1207 | 1208 | Promise.resolve(evilPromise); 1209 | assert.strictEqual(thenCalled, false); 1210 | }); 1211 | }); 1212 | 1213 | describe('Promise.prototype.finally', function() { 1214 | describe("native finally behaviour", function() { 1215 | describe("no value is passed in", function() { 1216 | it("does not provide a value to the finally code", function(done) { 1217 | var fulfillmentValue = 1; 1218 | var promise = Promise.resolve(fulfillmentValue); 1219 | 1220 | promise['finally'](function() { 1221 | assert.equal(arguments.length, 0); 1222 | done(); 1223 | }); 1224 | }); 1225 | 1226 | it("does not provide a reason to the finally code", function(done) { 1227 | var rejectionReason = new Error(); 1228 | var promise = Promise.reject(rejectionReason); 1229 | 1230 | promise['finally'](function(arg) { 1231 | assert.equal(arguments.length, 0); 1232 | done(); 1233 | }); 1234 | }); 1235 | }); 1236 | 1237 | describe("non-exceptional cases do not affect the result", function(){ 1238 | it("preserves the original fulfillment value even if the finally callback returns a value", function(done) { 1239 | var fulfillmentValue = 1; 1240 | var promise = Promise.resolve(fulfillmentValue); 1241 | 1242 | promise['finally'](function() { 1243 | return 2; 1244 | }).then(function(value) { 1245 | assert.equal(fulfillmentValue, value); 1246 | done(); 1247 | }); 1248 | }); 1249 | 1250 | it("preserves the original rejection reason even if the finally callback returns a value", function(done) { 1251 | var rejectionReason = new Error(); 1252 | var promise = Promise.reject(rejectionReason); 1253 | 1254 | promise['finally'](function() { 1255 | return 2; 1256 | }).then(undefined, function(reason) { 1257 | assert.equal(rejectionReason, reason); 1258 | done(); 1259 | }); 1260 | }); 1261 | 1262 | it("preserves the original fulfillment value even if a non-callable callback is given", function(done) { 1263 | var fulfillmentValue = 1; 1264 | var promise = Promise.resolve(fulfillmentValue); 1265 | 1266 | promise['finally']().then(function(value) { 1267 | assert.equal(fulfillmentValue, value); 1268 | done(); 1269 | }); 1270 | }); 1271 | 1272 | it("preserves the original rejection reason even if a non-callable callback is given", function(done) { 1273 | var rejectionReason = new Error(); 1274 | var promise = Promise.reject(rejectionReason); 1275 | 1276 | promise['finally']().then(undefined, function(reason) { 1277 | assert.equal(rejectionReason, reason); 1278 | done(); 1279 | }); 1280 | }); 1281 | }); 1282 | 1283 | describe("exception cases do propogate the failure", function(){ 1284 | describe("fulfilled promise", function(){ 1285 | it("propagates changes via throw", function(done) { 1286 | var promise = Promise.resolve(1); 1287 | var expectedReason = new Error(); 1288 | 1289 | promise['finally'](function() { 1290 | throw expectedReason; 1291 | }).then(undefined, function(reason) { 1292 | assert.deepEqual(expectedReason, reason); 1293 | done(); 1294 | }); 1295 | }); 1296 | 1297 | it("propagates changes via returned rejected promise", function(done){ 1298 | var promise = Promise.resolve(1); 1299 | var expectedReason = new Error(); 1300 | 1301 | promise['finally'](function() { 1302 | return Promise.reject(expectedReason); 1303 | }).then(undefined, function(reason) { 1304 | assert.deepEqual(expectedReason, reason); 1305 | done(); 1306 | }); 1307 | }); 1308 | }); 1309 | 1310 | describe("rejected promise", function(){ 1311 | it("propagates changes via throw", function(done) { 1312 | var promise = Promise.reject(1); 1313 | var expectedReason = new Error(); 1314 | 1315 | promise['finally'](function() { 1316 | throw expectedReason; 1317 | }).then(undefined, function(reason) { 1318 | assert.deepEqual(expectedReason, reason); 1319 | done(); 1320 | }); 1321 | }); 1322 | 1323 | it("propagates changes via returned rejected promise", function(done){ 1324 | var promise = Promise.reject(1); 1325 | var expectedReason = new Error(); 1326 | 1327 | promise['finally'](function() { 1328 | return Promise.reject(expectedReason); 1329 | }).then(undefined, function(reason) { 1330 | assert.deepEqual(expectedReason, reason); 1331 | done(); 1332 | }); 1333 | }); 1334 | }); 1335 | }); 1336 | }); 1337 | 1338 | describe("inheritance", function() { 1339 | function Subclass (resolver) { 1340 | this._promise$constructor(resolver); 1341 | } 1342 | 1343 | Subclass.prototype = Object.create(Promise.Promise.prototype); 1344 | Subclass.prototype.constructor = Subclass; 1345 | Subclass.prototype._promise$constructor = Promise.Promise; 1346 | 1347 | Subclass.resolve = Promise.Promise.resolve; 1348 | Subclass.reject = Promise.Promise.reject; 1349 | Subclass.all = Promise.Promise.all; 1350 | 1351 | it("preserves correct subclass when chained", function() { 1352 | var promise = Subclass.resolve().finally(); 1353 | assert.ok(promise instanceof Subclass); 1354 | assert.equal(promise.constructor, Subclass); 1355 | }); 1356 | 1357 | it("preserves correct subclass when rejected", function() { 1358 | var promise = Subclass.resolve().finally(function() { 1359 | throw new Error("OMG"); 1360 | }); 1361 | assert.ok(promise instanceof Subclass); 1362 | assert.equal(promise.constructor, Subclass); 1363 | }); 1364 | 1365 | it("preserves correct subclass when someone returns a thenable", function() { 1366 | var promise = Subclass.resolve().finally(function() { 1367 | return Promise.Promise.resolve(1); 1368 | }); 1369 | assert.ok(promise instanceof Subclass); 1370 | assert.equal(promise.constructor, Subclass); 1371 | }); 1372 | }); 1373 | }); 1374 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rsvp.js Tests 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 19 | 20 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | require('./test-adapter.js'); 2 | require('./scheduler-test.js'); 3 | require('./extension-test.js'); 4 | require('promises-aplus-tests-phantom/lib/testFiles'); 5 | -------------------------------------------------------------------------------- /test/scheduler-test.js: -------------------------------------------------------------------------------- 1 | /*global describe, it, assert */ 2 | 3 | var g = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this; 4 | var Promise = g.adapter.Promise; 5 | var assert = require('assert'); 6 | 7 | describe('scheduler', function() { 8 | afterEach(function() { 9 | // make sure the es6-promise scheduler is restored after each test 10 | Promise._setScheduler(void 0); 11 | }); 12 | 13 | describe('Promise._setScheduler', function() { 14 | it('should allow overriding the default scheduling mechanism', function(done) { 15 | // Wrapped in a setTimeout() to make sure that the microtask queue is empty 16 | // Otherwise we would have len > 2 and the overriden scheduling mechanism would not 17 | // be used. 18 | // This is required because the test library uses Promise. 19 | setTimeout(function() { 20 | var microtasks = []; 21 | var resolvedWith = null; 22 | 23 | Promise._setScheduler(function(fn) { 24 | microtasks.push(fn); 25 | }); 26 | 27 | Promise.resolve('value').then(function(v) { 28 | resolvedWith = v; 29 | }); 30 | 31 | assert.equal(resolvedWith, null); 32 | assert.equal(microtasks.length, 1); 33 | 34 | while (microtasks.length) { 35 | microtasks.shift()(); 36 | } 37 | 38 | assert.equal(resolvedWith, 'value'); 39 | 40 | // restore the original scheduler 41 | Promise._setScheduler(void 0); 42 | done(); 43 | }); 44 | }); 45 | }); 46 | 47 | describe('Promise._asap', function() { 48 | it('should allow enqueuing microtasks', function(done) { 49 | Promise._asap(function(arg) { 50 | assert.equal(arg, 'arg'); 51 | done(); 52 | }, 'arg'); 53 | }); 54 | }); 55 | 56 | describe('Promise._setAsap', function() { 57 | it('should allow overriding asap', function(done) { 58 | var called = false; 59 | 60 | Promise._setAsap(function(fn, arg) { 61 | called = true; 62 | // call the original implementation 63 | Promise._asap(fn, arg); 64 | // restore the original implementation 65 | Promise._setAsap(Promise._asap); 66 | }); 67 | 68 | Promise.resolve('value').then(function(v) { 69 | resolvedWith = v; 70 | assert.equal(v, 'value'); 71 | assert.equal(called, true); 72 | done(); 73 | }); 74 | }); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /test/test-adapter.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Promise = require('./es6-promise'); 3 | 4 | assert(typeof Promise.polyfill === 'function') 5 | assert(typeof Promise.Promise === 'function') 6 | assert(Promise.Promise === Promise) 7 | 8 | function defer() { 9 | var deferred = {}; 10 | 11 | deferred.promise = new Promise(function(resolve, reject) { 12 | deferred.resolve = resolve; 13 | deferred.reject = reject; 14 | }); 15 | 16 | return deferred; 17 | } 18 | 19 | new Function('return this;')().adapter = { 20 | resolved: function(a) { return Promise.resolve(a); }, 21 | rejected: function(a) { return Promise.reject(a); }, 22 | deferred: defer, 23 | Promise: Promise 24 | }; 25 | -------------------------------------------------------------------------------- /test/worker.js: -------------------------------------------------------------------------------- 1 | importScripts('../es6-promise.js'); 2 | new ES6Promise.Promise(function(resolve, reject) { 3 | self.onmessage = function (e) { 4 | if (e.data === 'ping') { 5 | resolve('pong'); 6 | } else { 7 | reject(new Error('wrong message')); 8 | } 9 | }; 10 | }).then(function (result) { 11 | self.postMessage(result); 12 | }, function (err){ 13 | setTimeout(function () { 14 | throw err; 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: "test/index.html", 5 | parallel: 5, 6 | frameworks: "mocha", 7 | launchers: { 8 | Mocha: { 9 | "command": `./node_modules/.bin/mocha ${process.env.EMBER_CLI_TEST_OUTPUT}/test/browserify.js -R tap`, 10 | "protocol": "tap" 11 | } 12 | }, 13 | launch_in_ci: [ 14 | "Phantomjs", 15 | "Mocha" 16 | ], 17 | launch_in_dev: [ 18 | "Phantomjs", 19 | "Mocha" 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /vendor/loader.js: -------------------------------------------------------------------------------- 1 | var define, requireModule, require, requirejs; 2 | 3 | (function() { 4 | var registry = {}, seen = {}; 5 | 6 | define = function(name, deps, callback) { 7 | registry[name] = { deps: deps, callback: callback }; 8 | }; 9 | 10 | requirejs = require = requireModule = function(name) { 11 | requirejs._eak_seen = registry; 12 | 13 | if (seen[name]) { return seen[name]; } 14 | seen[name] = {}; 15 | 16 | if (!registry[name]) { 17 | throw new Error("Could not find module " + name); 18 | } 19 | 20 | var mod = registry[name], 21 | deps = mod.deps, 22 | callback = mod.callback, 23 | reified = [], 24 | exports; 25 | 26 | for (var i=0, l=deps.length; i