├── server ├── .jshintrc └── index.js ├── lib ├── es6-promise.auto.js ├── es6-promise.js └── es6-promise │ ├── utils.js │ ├── then.js │ ├── polyfill.js │ ├── promise │ ├── resolve.js │ ├── reject.js │ ├── all.js │ └── race.js │ ├── enumerator.js │ ├── asap.js │ ├── -internal.js │ └── promise.js ├── .gitignore ├── test ├── index.js ├── worker.js ├── index.html ├── test-adapter.js ├── scheduler-test.js └── extension-test.js ├── .spmignore ├── auto.js ├── testem.json ├── config ├── versionTemplate.txt └── s3ProjectConfig.js ├── .release.json ├── bower.json ├── .travis.yml ├── bin └── publish_to_s3.js ├── .eslintrc ├── .jshintrc ├── LICENSE ├── vendor └── loader.js ├── package.json ├── README.md ├── Brocfile.js ├── CHANGELOG.md ├── es6-promise.d.ts └── dist ├── es6-promise.min.js ├── es6-promise.auto.min.js ├── es6-promise.js └── es6-promise.auto.js /server/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true 3 | } 4 | -------------------------------------------------------------------------------- /lib/es6-promise.auto.js: -------------------------------------------------------------------------------- 1 | import Promise from './es6-promise'; 2 | Promise.polyfill(); 3 | export default Promise; 4 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.get('/', function(req, res) { 3 | res.redirect('/test/'); 4 | }) 5 | }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /promises_tests 3 | /main.js 4 | /tmp 5 | /docs 6 | /dist/test 7 | !/dist/es6-promise.js 8 | !/dist/es6-promise.min.js -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /testem.json: -------------------------------------------------------------------------------- 1 | { 2 | "test_page": "test/index.html", 3 | "parallel": 5, 4 | "frameworks": "mocha", 5 | "launchers": { 6 | "Mocha": { 7 | "command": "./node_modules/.bin/mocha dist/test/browserify.js" 8 | } 9 | }, 10 | "launch_in_ci": [ 11 | "PhantomJS", 12 | "Mocha" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /lib/es6-promise/utils.js: -------------------------------------------------------------------------------- 1 | export function objectOrFunction(x) { 2 | return typeof x === 'function' || (typeof x === 'object' && x !== null); 3 | } 4 | 5 | export function isFunction(x) { 6 | return typeof x === 'function'; 7 | } 8 | 9 | export function isMaybeThenable(x) { 10 | return typeof x === 'object' && x !== null; 11 | } 12 | 13 | let _isArray; 14 | if (!Array.isArray) { 15 | _isArray = x => Object.prototype.toString.call(x) === '[object Array]'; 16 | } else { 17 | _isArray = Array.isArray; 18 | } 19 | 20 | export const isArray = _isArray; 21 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rsvp.js Tests 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 19 | 20 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6-promise", 3 | "namespace": "Promise", 4 | "description": "A polyfill for ES6-style Promises, tracking rsvp", 5 | "authors": [ 6 | "Stefan Penner " 7 | ], 8 | "main": "dist/es6-promise.js", 9 | "keywords": [ 10 | "promise" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/stefanpenner/es6-promise.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/stefanpenner/es6-promise/issues" 18 | }, 19 | "license": "MIT", 20 | "ignore": [ 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "tests", 25 | "vendor", 26 | "tasks" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/s3ProjectConfig.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Using wildcard because es6-promise does not currently have a 3 | * channel system in place. 4 | */ 5 | module.exports = function(revision,tag,date){ 6 | return { 7 | 'es6-promise.js': 8 | { contentType: 'text/javascript', 9 | destinations: { 10 | wildcard: [ 11 | 'es6-promise-latest.js', 12 | 'es6-promise-' + revision + '.js' 13 | ] 14 | } 15 | }, 16 | 'es6-promise.min.js': 17 | { contentType: 'text/javascript', 18 | destinations: { 19 | wildcard: [ 20 | 'es6-promise-latest.min.js', 21 | 'es6-promise-' + revision + '.min.js' 22 | ] 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | - "6" 5 | - "4" 6 | 7 | after_success: 8 | - "./bin/publish_to_s3.js" 9 | before_install: 10 | - nvm install 6 11 | - nvm use 6 12 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH 13 | before_script: 14 | - nvm use $TRAVIS_NODE_VERSION 15 | env: 16 | global: 17 | - EMBER_ENV=production 18 | - secure: MiR5zbpHpblG1HcQyxzhYqxcb+3gGsjKWOjLYi1AOU4BG90+Ckim4mCJbneIHaBfeIZKZCm+X1wRRwUE0TlaMFEiXqReHt8k/1ChjfFdTicZtXYytbnGdRlHjvN/LVCaVhBiORSXWPzjIWhwYInMhCtekWTOqChtblRGdbrCLCY= 19 | - secure: LF1yJIzr4z5e4RxFt+ZIwjazDyItPoPtSUgcoSQfTUhRtkxFsIRpOpf4VnzPH2FSpw4wbkNq+hm+K1xrKHNq+OE0PLYtD+H7i03ac993jReSJN8rwb0gpAlwAGWC++uF7j7aMKJAlZA1u4tnPD5cxuk6BKnNSONtD0BwKQ6oXkk= 20 | - secure: MB2RZPsHVuikayckVYCVE9TWiooaFV+jyxEkpSoNJ5pRIVTwy9xRHK56S1sxkXmPIm7sYyaLWpngp13/CN80pT796HS8mEuxJ011XwjY6YutXHnR2gOrCssah6kQ6gaXO1sFVkerBa/GwdkKu/k8LVAiORiz1JlPZvOKDRsJaNw= 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /bin/publish_to_s3.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // To invoke this from the commandline you need the following to env vars to exist: 4 | // 5 | // S3_BUCKET_NAME 6 | // TRAVIS_BRANCH 7 | // TRAVIS_TAG 8 | // TRAVIS_COMMIT 9 | // S3_SECRET_ACCESS_KEY 10 | // S3_ACCESS_KEY_ID 11 | // 12 | // Once you have those you execute with the following: 13 | // 14 | // ```sh 15 | // ./bin/publish_to_s3.js 16 | // ``` 17 | var S3Publisher = require('ember-publisher'); 18 | var configPath = require('path').join(__dirname, '../config/s3ProjectConfig.js'); 19 | publisher = new S3Publisher({ projectConfigPath: configPath }); 20 | 21 | // Always use wildcard section of project config. 22 | // This is useful when the including library does not 23 | // require channels (like in ember.js / ember-data). 24 | publisher.currentBranch = function() { 25 | return (process.env.TRAVIS_BRANCH === 'master') ? 'wildcard' : 'no-op'; 26 | }; 27 | publisher.publish(); 28 | 29 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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://people.mozilla.org/~jorendorff/es6-draft.html#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://raw.githubusercontent.com/stefanpenner/es6-promise/master/dist/es6-promise.js) 10 | * [es6-promise-auto 27.78 KB (7.3 KB gzipped)](https://raw.githubusercontent.com/stefanpenner/es6-promise/master/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://raw.githubusercontent.com/stefanpenner/es6-promise/master/dist/es6-promise.min.js) 12 | * [es6-promise-auto-min 6.19 KB (2.4 KB gzipped)](https://raw.githubusercontent.com/stefanpenner/es6-promise/master/dist/es6-promise.auto.min.js) - Minified version of `es6-promise-auto` above. 13 | 14 | ## Node.js 15 | 16 | To install: 17 | 18 | ```sh 19 | npm install es6-promise 20 | ``` 21 | 22 | To use: 23 | 24 | ```js 25 | var Promise = require('es6-promise').Promise; 26 | ``` 27 | 28 | ## Bower 29 | 30 | To install: 31 | 32 | ```sh 33 | bower install es6-promise --save 34 | ``` 35 | 36 | 37 | ## Usage in IE<9 38 | 39 | `catch` is a reserved word in IE<9, meaning `promise.catch(func)` throws a syntax error. To work around this, you can use a string to access the property as shown in the following example. 40 | 41 | However, please remember that such technique is already provided by most common minifiers, making the resulting code safe for old browsers and production: 42 | 43 | ```js 44 | promise['catch'](function(err) { 45 | // ... 46 | }); 47 | ``` 48 | 49 | Or use `.then` instead: 50 | 51 | ```js 52 | promise.then(undefined, function(err) { 53 | // ... 54 | }); 55 | ``` 56 | 57 | ## Auto-polyfill 58 | 59 | To polyfill the global environment (either in Node or in the browser via CommonJS) use the following code snippet: 60 | 61 | ```js 62 | require('es6-promise').polyfill(); 63 | ``` 64 | 65 | Alternatively 66 | 67 | ```js 68 | require('es6-promise/auto'); 69 | ``` 70 | 71 | 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. 72 | 73 | ## Building & Testing 74 | 75 | You will need to have PhantomJS installed globally in order to run the tests. 76 | 77 | `npm install -g phantomjs` 78 | 79 | * `npm run build` to build 80 | * `npm test` to run tests 81 | * `npm start` to run a build watcher, and webserver to test 82 | * `npm run test:server` for a testem test runner and watching builder 83 | -------------------------------------------------------------------------------- /Brocfile.js: -------------------------------------------------------------------------------- 1 | /* jshint node:true, undef:true, unused:true */ 2 | var Rollup = require('broccoli-rollup'); 3 | var Babel = require('broccoli-babel-transpiler'); 4 | var merge = require('broccoli-merge-trees'); 5 | var uglify = require('broccoli-uglify-js'); 6 | var version = require('git-repo-version'); 7 | var watchify = require('broccoli-watchify'); 8 | var concat = require('broccoli-concat'); 9 | var fs = require('fs'); 10 | 11 | var stew = require('broccoli-stew'); 12 | 13 | var find = stew.find; 14 | var mv = stew.mv; 15 | var rename = stew.rename; 16 | var env = stew.env; 17 | var map = stew.map; 18 | 19 | var lib = find('lib'); 20 | 21 | // test stuff 22 | var testDir = find('test'); 23 | var testFiles = find('test/{index.html,worker.js}'); 24 | 25 | var json3 = mv(find('node_modules/json3/lib/{json3.js}'), 'node_modules/json3/lib/', 'test/'); 26 | // mocha doesn't browserify correctly 27 | var mocha = mv(find('node_modules/mocha/mocha.{js,css}'), 'node_modules/mocha/', 'test/'); 28 | 29 | var testVendor = merge([ json3, mocha ]); 30 | 31 | 32 | var es5 = new Babel(lib, { 33 | blacklist: ['es6.modules'] 34 | }); 35 | 36 | function rollupConfig(entry) { 37 | return new Rollup(es5, { 38 | rollup: { 39 | entry: 'lib/' + entry, 40 | targets: [ 41 | { 42 | format: 'umd', 43 | moduleName: 'ES6Promise', 44 | dest: entry, 45 | sourceMap: 'inline' 46 | } 47 | ] 48 | } 49 | }); 50 | } 51 | 52 | // build RSVP itself 53 | var es6Promise = rollupConfig('es6-promise.js') 54 | var es6PromiseAuto = rollupConfig('es6-promise.auto.js') 55 | 56 | var testBundle = watchify(merge([ 57 | mv(es6Promise, 'test'), 58 | testDir 59 | ]), { 60 | browserify: { debug: true, entries: ['./test/index.js'] } 61 | }); 62 | 63 | var header = stew.map(find('config/versionTemplate.txt'), function(content) { 64 | return content.replace(/VERSION_PLACEHOLDER_STRING/, version()); 65 | }); 66 | 67 | function concatAs(outputFile) { 68 | return merge([ 69 | concat(merge([es6Promise, header]), { 70 | headerFiles: ['config/versionTemplate.txt'], 71 | inputFiles: ['es6-promise.js'], 72 | outputFile: outputFile 73 | }), 74 | 75 | concat(merge([es6PromiseAuto, header]), { 76 | headerFiles: ['config/versionTemplate.txt'], 77 | inputFiles: ['es6-promise.auto.js'], 78 | outputFile: outputFile.replace('es6-promise', 'es6-promise.auto'), 79 | }), 80 | 81 | ]); 82 | } 83 | 84 | function production() { 85 | var result; 86 | env('production', function(){ 87 | result = uglify(concatAs('es6-promise.min.js'), { 88 | compress: true, 89 | mangle: true, 90 | }); 91 | }) 92 | return result; 93 | } 94 | 95 | function development() { 96 | return concatAs('es6-promise.js'); 97 | } 98 | 99 | module.exports = merge([ 100 | merge([ 101 | production(), 102 | development(), 103 | ].filter(Boolean)), 104 | // test stuff 105 | testFiles, 106 | testVendor, 107 | mv(testBundle, 'test') 108 | ]); 109 | -------------------------------------------------------------------------------- /lib/es6-promise/enumerator.js: -------------------------------------------------------------------------------- 1 | import { 2 | isArray, 3 | isMaybeThenable 4 | } from './utils'; 5 | 6 | import { 7 | noop, 8 | reject, 9 | fulfill, 10 | subscribe, 11 | FULFILLED, 12 | REJECTED, 13 | PENDING, 14 | getThen, 15 | handleMaybeThenable 16 | } from './-internal'; 17 | 18 | import then from './then'; 19 | import Promise from './promise'; 20 | import originalResolve from './promise/resolve'; 21 | import originalThen from './then'; 22 | import { makePromise, PROMISE_ID } from './-internal'; 23 | 24 | export default Enumerator; 25 | function Enumerator(Constructor, input) { 26 | this._instanceConstructor = Constructor; 27 | this.promise = new Constructor(noop); 28 | 29 | if (!this.promise[PROMISE_ID]) { 30 | makePromise(this.promise); 31 | } 32 | 33 | if (isArray(input)) { 34 | this._input = input; 35 | this.length = input.length; 36 | this._remaining = input.length; 37 | 38 | this._result = new Array(this.length); 39 | 40 | if (this.length === 0) { 41 | fulfill(this.promise, this._result); 42 | } else { 43 | this.length = this.length || 0; 44 | this._enumerate(); 45 | if (this._remaining === 0) { 46 | fulfill(this.promise, this._result); 47 | } 48 | } 49 | } else { 50 | reject(this.promise, validationError()); 51 | } 52 | } 53 | 54 | function validationError() { 55 | return new Error('Array Methods must be provided an Array'); 56 | }; 57 | 58 | Enumerator.prototype._enumerate = function() { 59 | let { length, _input } = this; 60 | 61 | for (let i = 0; this._state === PENDING && i < length; i++) { 62 | this._eachEntry(_input[i], i); 63 | } 64 | }; 65 | 66 | Enumerator.prototype._eachEntry = function(entry, i) { 67 | let c = this._instanceConstructor; 68 | let { resolve } = c; 69 | 70 | if (resolve === originalResolve) { 71 | let then = getThen(entry); 72 | 73 | if (then === originalThen && 74 | entry._state !== PENDING) { 75 | this._settledAt(entry._state, i, entry._result); 76 | } else if (typeof then !== 'function') { 77 | this._remaining--; 78 | this._result[i] = entry; 79 | } else if (c === Promise) { 80 | let promise = new c(noop); 81 | handleMaybeThenable(promise, entry, then); 82 | this._willSettleAt(promise, i); 83 | } else { 84 | this._willSettleAt(new c(resolve => resolve(entry)), i); 85 | } 86 | } else { 87 | this._willSettleAt(resolve(entry), i); 88 | } 89 | }; 90 | 91 | Enumerator.prototype._settledAt = function(state, i, value) { 92 | let { promise } = this; 93 | 94 | if (promise._state === PENDING) { 95 | this._remaining--; 96 | 97 | if (state === REJECTED) { 98 | reject(promise, value); 99 | } else { 100 | this._result[i] = value; 101 | } 102 | } 103 | 104 | if (this._remaining === 0) { 105 | fulfill(promise, this._result); 106 | } 107 | }; 108 | 109 | Enumerator.prototype._willSettleAt = function(promise, i) { 110 | let enumerator = this; 111 | 112 | subscribe(promise, undefined, value => enumerator._settledAt(FULFILLED, i, value), 113 | reason => enumerator._settledAt(REJECTED, i, reason)); 114 | }; 115 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Master 2 | 3 | # 4.1.0 4 | 5 | * [BUGFIX] Fix memory leak [#269] 6 | * [BUGFIX] Auto Bundles within an AMD Environment [#263] 7 | 8 | # 4.0.5 9 | 10 | * fix require('es6-promise/auto') for Node < 4 11 | 12 | # 4.0.4 13 | 14 | * fix asap when using https://github.com/Kinvey/titanium-sdk 15 | 16 | # 4.0.3 17 | 18 | * fix Readme links 19 | 20 | # 4.0.2 21 | 22 | * fix require('es6-promise/auto'); 23 | 24 | # 4.0.0 25 | 26 | * no longer polyfill automatically, if needed one can still invoke 27 | `require('es6-promise/auto')` directly. 28 | 29 | # 3.3.1 30 | 31 | * fix links in readme 32 | 33 | # 3.3.0 34 | 35 | * support polyfil on WebMAF (playstation env) 36 | * fix tampering related bug global `constructor` was referenced by mistake. 37 | * provide TS Typings 38 | * increase compatibliity with sinon.useFakeTimers(); 39 | * update build tools (use rollup) 40 | * directly export promise; 41 | 42 | # 3.2.2 43 | 44 | * IE8: use isArray 45 | * update build dependencies 46 | 47 | # 3.2.1 48 | 49 | * fix race tampering issue 50 | * use eslint 51 | * fix Promise.all tampering 52 | * remove unused code 53 | * fix issues with NWJS/electron 54 | 55 | # 3.2.0 56 | 57 | * improve tamper resistence of Promise.all Promise.race and 58 | Promise.prototype.then (note, this isn't complete, but addresses an exception 59 | when used \w core-js, follow up work will address entirely) 60 | * remove spec incompatible then chaining fast-path 61 | * add eslint 62 | * update build deps 63 | 64 | # 3.1.2 65 | 66 | * fix node detection issues with NWJS/electron 67 | 68 | # 3.1.0 69 | 70 | * improve performance of Promise.all when it encounters a non-promise input object input 71 | * then/resolve tamper protection 72 | * reduce AST size of promise constructor, to facilitate more inlining 73 | * Update README.md with details about PhantomJS requirement for running tests 74 | * Mangle and compress the minified version 75 | 76 | # 3.0.2 77 | 78 | * correctly bump both bower and package.json versions 79 | 80 | # 3.0.1 81 | 82 | * no longer include dist/test in npm releases 83 | 84 | # 3.0.0 85 | 86 | * use nextTick() instead of setImmediate() to schedule microtasks with node 0.10. Later versions of 87 | nodes are not affected as they were already using nextTick(). Note that using nextTick() might 88 | trigger a depreciation warning on 0.10 as described at https://github.com/cujojs/when/issues/410. 89 | The reason why nextTick() is preferred is that is setImmediate() would schedule a macrotask 90 | instead of a microtask and might result in a different scheduling. 91 | If needed you can revert to the former behavior as follow: 92 | 93 | var Promise = require('es6-promise').Promise; 94 | Promise._setScheduler(setImmediate); 95 | 96 | # 2.3.0 97 | 98 | * #121: Ability to override the internal asap implementation 99 | * #120: Use an ascii character for an apostrophe, for source maps 100 | 101 | # 2.2.0 102 | 103 | * #116: Expose asap() and a way to override the scheduling mechanism on Promise 104 | * Lock to v0.2.3 of ember-cli 105 | 106 | # 2.1.1 107 | 108 | * Fix #100 via #105: tell browserify to ignore vertx require 109 | * Fix #101 via #102: "follow thenable state, not own state" 110 | 111 | # 2.1.0 112 | 113 | * #59: Automatic polyfill. No need to invoke `ES6Promise.polyfill()` anymore. 114 | * ... (see the commit log) 115 | 116 | # 2.0.0 117 | 118 | * re-sync with RSVP. Many large performance improvements and bugfixes. 119 | 120 | # 1.0.0 121 | 122 | * first subset of RSVP 123 | -------------------------------------------------------------------------------- /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, 1); 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 r = require; 100 | const vertx = r('vertx'); 101 | vertxNext = vertx.runOnLoop || vertx.runOnContext; 102 | return useVertxTimer(); 103 | } catch(e) { 104 | return useSetTimeout(); 105 | } 106 | } 107 | 108 | let scheduleFlush; 109 | // Decide what async method to use to triggering processing of queued callbacks: 110 | if (isNode) { 111 | scheduleFlush = useNextTick(); 112 | } else if (BrowserMutationObserver) { 113 | scheduleFlush = useMutationObserver(); 114 | } else if (isWorker) { 115 | scheduleFlush = useMessageChannel(); 116 | } else if (browserWindow === undefined && typeof require === 'function') { 117 | scheduleFlush = attemptVertx(); 118 | } else { 119 | scheduleFlush = useSetTimeout(); 120 | } 121 | -------------------------------------------------------------------------------- /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 | * Make a new promise from the thenable. 38 | * A thenable is promise-like in as far as it has a "then" method. 39 | */ 40 | static resolve (): Promise; 41 | static resolve (value: R | Thenable): Promise; 42 | 43 | /** 44 | * Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error 45 | */ 46 | static reject (error: any): Promise; 47 | 48 | /** 49 | * Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects. 50 | * the array passed to all can be a mixture of promise-like objects and other objects. 51 | * The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value. 52 | */ 53 | 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]>; 54 | 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]>; 55 | 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]>; 56 | 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]>; 57 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6]>; 58 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable]): Promise<[T1, T2, T3, T4, T5]>; 59 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable ]): Promise<[T1, T2, T3, T4]>; 60 | static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable]): Promise<[T1, T2, T3]>; 61 | static all(values: [T1 | Thenable, T2 | Thenable]): Promise<[T1, T2]>; 62 | static all(values: [T1 | Thenable]): Promise<[T1]>; 63 | static all(values: Array>): Promise; 64 | 65 | /** 66 | * Make a Promise that fulfills when any item fulfills, and rejects if any item rejects. 67 | */ 68 | static race (promises: (R | Thenable)[]): Promise; 69 | } 70 | 71 | /** 72 | * The polyfill method will patch the global environment (in this case to the Promise name) when called. 73 | */ 74 | export function polyfill (): void; 75 | -------------------------------------------------------------------------------- /dist/es6-promise.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.ES6Promise=e()}(this,function(){"use strict";function t(t){return"function"==typeof t||"object"==typeof t&&null!==t}function e(t){return"function"==typeof t}function n(t){I=t}function r(t){J=t}function o(){return function(){return process.nextTick(a)}}function i(){return"undefined"!=typeof H?function(){H(a)}:c()}function s(){var t=0,e=new V(a),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function u(){var t=new MessageChannel;return t.port1.onmessage=a,function(){return t.port2.postMessage(0)}}function c(){var t=setTimeout;return function(){return t(a,1)}}function a(){for(var t=0;t { 50 | var sealed = false; 51 | var error = tryThen(then, thenable, value => { 52 | if (sealed) { return; } 53 | sealed = true; 54 | if (thenable !== value) { 55 | resolve(promise, value); 56 | } else { 57 | fulfill(promise, value); 58 | } 59 | }, reason => { 60 | if (sealed) { return; } 61 | sealed = true; 62 | 63 | reject(promise, reason); 64 | }, 'Settle: ' + (promise._label || ' unknown promise')); 65 | 66 | if (!sealed && error) { 67 | sealed = true; 68 | reject(promise, error); 69 | } 70 | }, promise); 71 | } 72 | 73 | function handleOwnThenable(promise, thenable) { 74 | if (thenable._state === FULFILLED) { 75 | fulfill(promise, thenable._result); 76 | } else if (thenable._state === REJECTED) { 77 | reject(promise, thenable._result); 78 | } else { 79 | subscribe(thenable, undefined, value => resolve(promise, value), 80 | reason => reject(promise, reason)) 81 | } 82 | } 83 | 84 | function handleMaybeThenable(promise, maybeThenable, then) { 85 | if (maybeThenable.constructor === promise.constructor && 86 | then === originalThen && 87 | maybeThenable.constructor.resolve === originalResolve) { 88 | handleOwnThenable(promise, maybeThenable); 89 | } else { 90 | if (then === GET_THEN_ERROR) { 91 | reject(promise, GET_THEN_ERROR.error); 92 | GET_THEN_ERROR.error = null; 93 | } else if (then === undefined) { 94 | fulfill(promise, maybeThenable); 95 | } else if (isFunction(then)) { 96 | handleForeignThenable(promise, maybeThenable, then); 97 | } else { 98 | fulfill(promise, maybeThenable); 99 | } 100 | } 101 | } 102 | 103 | function resolve(promise, value) { 104 | if (promise === value) { 105 | reject(promise, selfFulfillment()); 106 | } else if (objectOrFunction(value)) { 107 | handleMaybeThenable(promise, value, getThen(value)); 108 | } else { 109 | fulfill(promise, value); 110 | } 111 | } 112 | 113 | function publishRejection(promise) { 114 | if (promise._onerror) { 115 | promise._onerror(promise._result); 116 | } 117 | 118 | publish(promise); 119 | } 120 | 121 | function fulfill(promise, value) { 122 | if (promise._state !== PENDING) { return; } 123 | 124 | promise._result = value; 125 | promise._state = FULFILLED; 126 | 127 | if (promise._subscribers.length !== 0) { 128 | asap(publish, promise); 129 | } 130 | } 131 | 132 | function reject(promise, reason) { 133 | if (promise._state !== PENDING) { return; } 134 | promise._state = REJECTED; 135 | promise._result = reason; 136 | 137 | asap(publishRejection, promise); 138 | } 139 | 140 | function subscribe(parent, child, onFulfillment, onRejection) { 141 | let { _subscribers } = parent; 142 | let { length } = _subscribers; 143 | 144 | parent._onerror = null; 145 | 146 | _subscribers[length] = child; 147 | _subscribers[length + FULFILLED] = onFulfillment; 148 | _subscribers[length + REJECTED] = onRejection; 149 | 150 | if (length === 0 && parent._state) { 151 | asap(publish, parent); 152 | } 153 | } 154 | 155 | function publish(promise) { 156 | let subscribers = promise._subscribers; 157 | let settled = promise._state; 158 | 159 | if (subscribers.length === 0) { return; } 160 | 161 | let child, callback, detail = promise._result; 162 | 163 | for (let i = 0; i < subscribers.length; i += 3) { 164 | child = subscribers[i]; 165 | callback = subscribers[i + settled]; 166 | 167 | if (child) { 168 | invokeCallback(settled, child, callback, detail); 169 | } else { 170 | callback(detail); 171 | } 172 | } 173 | 174 | promise._subscribers.length = 0; 175 | } 176 | 177 | function ErrorObject() { 178 | this.error = null; 179 | } 180 | 181 | const TRY_CATCH_ERROR = new ErrorObject(); 182 | 183 | function tryCatch(callback, detail) { 184 | try { 185 | return callback(detail); 186 | } catch(e) { 187 | TRY_CATCH_ERROR.error = e; 188 | return TRY_CATCH_ERROR; 189 | } 190 | } 191 | 192 | function invokeCallback(settled, promise, callback, detail) { 193 | let hasCallback = isFunction(callback), 194 | value, error, succeeded, failed; 195 | 196 | if (hasCallback) { 197 | value = tryCatch(callback, detail); 198 | 199 | if (value === TRY_CATCH_ERROR) { 200 | failed = true; 201 | error = value.error; 202 | value.error = null; 203 | } else { 204 | succeeded = true; 205 | } 206 | 207 | if (promise === value) { 208 | reject(promise, cannotReturnOwn()); 209 | return; 210 | } 211 | 212 | } else { 213 | value = detail; 214 | succeeded = true; 215 | } 216 | 217 | if (promise._state !== PENDING) { 218 | // noop 219 | } else if (hasCallback && succeeded) { 220 | resolve(promise, value); 221 | } else if (failed) { 222 | reject(promise, error); 223 | } else if (settled === FULFILLED) { 224 | fulfill(promise, value); 225 | } else if (settled === REJECTED) { 226 | reject(promise, value); 227 | } 228 | } 229 | 230 | function initializePromise(promise, resolver) { 231 | try { 232 | resolver(function resolvePromise(value){ 233 | resolve(promise, value); 234 | }, function rejectPromise(reason) { 235 | reject(promise, reason); 236 | }); 237 | } catch(e) { 238 | reject(promise, e); 239 | } 240 | } 241 | 242 | let id = 0; 243 | function nextId() { 244 | return id++; 245 | } 246 | 247 | function makePromise(promise) { 248 | promise[PROMISE_ID] = id++; 249 | promise._state = undefined; 250 | promise._result = undefined; 251 | promise._subscribers = []; 252 | } 253 | 254 | export { 255 | nextId, 256 | makePromise, 257 | getThen, 258 | noop, 259 | resolve, 260 | reject, 261 | fulfill, 262 | subscribe, 263 | publish, 264 | publishRejection, 265 | initializePromise, 266 | invokeCallback, 267 | FULFILLED, 268 | REJECTED, 269 | PENDING, 270 | handleMaybeThenable 271 | }; 272 | -------------------------------------------------------------------------------- /lib/es6-promise/promise.js: -------------------------------------------------------------------------------- 1 | import { 2 | isFunction 3 | } from './utils'; 4 | 5 | import { 6 | noop, 7 | nextId, 8 | PROMISE_ID, 9 | initializePromise 10 | } from './-internal'; 11 | 12 | import { 13 | asap, 14 | setAsap, 15 | setScheduler 16 | } from './asap'; 17 | 18 | import all from './promise/all'; 19 | import race from './promise/race'; 20 | import Resolve from './promise/resolve'; 21 | import Reject from './promise/reject'; 22 | import then from './then'; 23 | 24 | 25 | function needsResolver() { 26 | throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); 27 | } 28 | 29 | function needsNew() { 30 | throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); 31 | } 32 | 33 | /** 34 | Promise objects represent the eventual result of an asynchronous operation. The 35 | primary way of interacting with a promise is through its `then` method, which 36 | registers callbacks to receive either a promise's eventual value or the reason 37 | why the promise cannot be fulfilled. 38 | 39 | Terminology 40 | ----------- 41 | 42 | - `promise` is an object or function with a `then` method whose behavior conforms to this specification. 43 | - `thenable` is an object or function that defines a `then` method. 44 | - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). 45 | - `exception` is a value that is thrown using the throw statement. 46 | - `reason` is a value that indicates why a promise was rejected. 47 | - `settled` the final resting state of a promise, fulfilled or rejected. 48 | 49 | A promise can be in one of three states: pending, fulfilled, or rejected. 50 | 51 | Promises that are fulfilled have a fulfillment value and are in the fulfilled 52 | state. Promises that are rejected have a rejection reason and are in the 53 | rejected state. A fulfillment value is never a thenable. 54 | 55 | Promises can also be said to *resolve* a value. If this value is also a 56 | promise, then the original promise's settled state will match the value's 57 | settled state. So a promise that *resolves* a promise that rejects will 58 | itself reject, and a promise that *resolves* a promise that fulfills will 59 | itself fulfill. 60 | 61 | 62 | Basic Usage: 63 | ------------ 64 | 65 | ```js 66 | let promise = new Promise(function(resolve, reject) { 67 | // on success 68 | resolve(value); 69 | 70 | // on failure 71 | reject(reason); 72 | }); 73 | 74 | promise.then(function(value) { 75 | // on fulfillment 76 | }, function(reason) { 77 | // on rejection 78 | }); 79 | ``` 80 | 81 | Advanced Usage: 82 | --------------- 83 | 84 | Promises shine when abstracting away asynchronous interactions such as 85 | `XMLHttpRequest`s. 86 | 87 | ```js 88 | function getJSON(url) { 89 | return new Promise(function(resolve, reject){ 90 | let xhr = new XMLHttpRequest(); 91 | 92 | xhr.open('GET', url); 93 | xhr.onreadystatechange = handler; 94 | xhr.responseType = 'json'; 95 | xhr.setRequestHeader('Accept', 'application/json'); 96 | xhr.send(); 97 | 98 | function handler() { 99 | if (this.readyState === this.DONE) { 100 | if (this.status === 200) { 101 | resolve(this.response); 102 | } else { 103 | reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); 104 | } 105 | } 106 | }; 107 | }); 108 | } 109 | 110 | getJSON('/posts.json').then(function(json) { 111 | // on fulfillment 112 | }, function(reason) { 113 | // on rejection 114 | }); 115 | ``` 116 | 117 | Unlike callbacks, promises are great composable primitives. 118 | 119 | ```js 120 | Promise.all([ 121 | getJSON('/posts'), 122 | getJSON('/comments') 123 | ]).then(function(values){ 124 | values[0] // => postsJSON 125 | values[1] // => commentsJSON 126 | 127 | return values; 128 | }); 129 | ``` 130 | 131 | @class Promise 132 | @param {function} resolver 133 | Useful for tooling. 134 | @constructor 135 | */ 136 | export default function Promise(resolver) { 137 | this[PROMISE_ID] = nextId(); 138 | this._result = this._state = undefined; 139 | this._subscribers = []; 140 | 141 | if (noop !== resolver) { 142 | typeof resolver !== 'function' && needsResolver(); 143 | this instanceof Promise ? initializePromise(this, resolver) : needsNew(); 144 | } 145 | } 146 | 147 | Promise.all = all; 148 | Promise.race = race; 149 | Promise.resolve = Resolve; 150 | Promise.reject = Reject; 151 | Promise._setScheduler = setScheduler; 152 | Promise._setAsap = setAsap; 153 | Promise._asap = asap; 154 | 155 | Promise.prototype = { 156 | constructor: Promise, 157 | 158 | /** 159 | The primary way of interacting with a promise is through its `then` method, 160 | which registers callbacks to receive either a promise's eventual value or the 161 | reason why the promise cannot be fulfilled. 162 | 163 | ```js 164 | findUser().then(function(user){ 165 | // user is available 166 | }, function(reason){ 167 | // user is unavailable, and you are given the reason why 168 | }); 169 | ``` 170 | 171 | Chaining 172 | -------- 173 | 174 | The return value of `then` is itself a promise. This second, 'downstream' 175 | promise is resolved with the return value of the first promise's fulfillment 176 | or rejection handler, or rejected if the handler throws an exception. 177 | 178 | ```js 179 | findUser().then(function (user) { 180 | return user.name; 181 | }, function (reason) { 182 | return 'default name'; 183 | }).then(function (userName) { 184 | // If `findUser` fulfilled, `userName` will be the user's name, otherwise it 185 | // will be `'default name'` 186 | }); 187 | 188 | findUser().then(function (user) { 189 | throw new Error('Found user, but still unhappy'); 190 | }, function (reason) { 191 | throw new Error('`findUser` rejected and we're unhappy'); 192 | }).then(function (value) { 193 | // never reached 194 | }, function (reason) { 195 | // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. 196 | // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. 197 | }); 198 | ``` 199 | If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. 200 | 201 | ```js 202 | findUser().then(function (user) { 203 | throw new PedagogicalException('Upstream error'); 204 | }).then(function (value) { 205 | // never reached 206 | }).then(function (value) { 207 | // never reached 208 | }, function (reason) { 209 | // The `PedgagocialException` is propagated all the way down to here 210 | }); 211 | ``` 212 | 213 | Assimilation 214 | ------------ 215 | 216 | Sometimes the value you want to propagate to a downstream promise can only be 217 | retrieved asynchronously. This can be achieved by returning a promise in the 218 | fulfillment or rejection handler. The downstream promise will then be pending 219 | until the returned promise is settled. This is called *assimilation*. 220 | 221 | ```js 222 | findUser().then(function (user) { 223 | return findCommentsByAuthor(user); 224 | }).then(function (comments) { 225 | // The user's comments are now available 226 | }); 227 | ``` 228 | 229 | If the assimliated promise rejects, then the downstream promise will also reject. 230 | 231 | ```js 232 | findUser().then(function (user) { 233 | return findCommentsByAuthor(user); 234 | }).then(function (comments) { 235 | // If `findCommentsByAuthor` fulfills, we'll have the value here 236 | }, function (reason) { 237 | // If `findCommentsByAuthor` rejects, we'll have the reason here 238 | }); 239 | ``` 240 | 241 | Simple Example 242 | -------------- 243 | 244 | Synchronous Example 245 | 246 | ```javascript 247 | let result; 248 | 249 | try { 250 | result = findResult(); 251 | // success 252 | } catch(reason) { 253 | // failure 254 | } 255 | ``` 256 | 257 | Errback Example 258 | 259 | ```js 260 | findResult(function(result, err){ 261 | if (err) { 262 | // failure 263 | } else { 264 | // success 265 | } 266 | }); 267 | ``` 268 | 269 | Promise Example; 270 | 271 | ```javascript 272 | findResult().then(function(result){ 273 | // success 274 | }, function(reason){ 275 | // failure 276 | }); 277 | ``` 278 | 279 | Advanced Example 280 | -------------- 281 | 282 | Synchronous Example 283 | 284 | ```javascript 285 | let author, books; 286 | 287 | try { 288 | author = findAuthor(); 289 | books = findBooksByAuthor(author); 290 | // success 291 | } catch(reason) { 292 | // failure 293 | } 294 | ``` 295 | 296 | Errback Example 297 | 298 | ```js 299 | 300 | function foundBooks(books) { 301 | 302 | } 303 | 304 | function failure(reason) { 305 | 306 | } 307 | 308 | findAuthor(function(author, err){ 309 | if (err) { 310 | failure(err); 311 | // failure 312 | } else { 313 | try { 314 | findBoooksByAuthor(author, function(books, err) { 315 | if (err) { 316 | failure(err); 317 | } else { 318 | try { 319 | foundBooks(books); 320 | } catch(reason) { 321 | failure(reason); 322 | } 323 | } 324 | }); 325 | } catch(error) { 326 | failure(err); 327 | } 328 | // success 329 | } 330 | }); 331 | ``` 332 | 333 | Promise Example; 334 | 335 | ```javascript 336 | findAuthor(). 337 | then(findBooksByAuthor). 338 | then(function(books){ 339 | // found books 340 | }).catch(function(reason){ 341 | // something went wrong 342 | }); 343 | ``` 344 | 345 | @method then 346 | @param {Function} onFulfilled 347 | @param {Function} onRejected 348 | Useful for tooling. 349 | @return {Promise} 350 | */ 351 | then: then, 352 | 353 | /** 354 | `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same 355 | as the catch block of a try/catch statement. 356 | 357 | ```js 358 | function findAuthor(){ 359 | throw new Error('couldn't find that author'); 360 | } 361 | 362 | // synchronous 363 | try { 364 | findAuthor(); 365 | } catch(reason) { 366 | // something went wrong 367 | } 368 | 369 | // async with promises 370 | findAuthor().catch(function(reason){ 371 | // something went wrong 372 | }); 373 | ``` 374 | 375 | @method catch 376 | @param {Function} onRejection 377 | Useful for tooling. 378 | @return {Promise} 379 | */ 380 | catch(onRejection) { 381 | return this.then(null, onRejection); 382 | } 383 | }; 384 | -------------------------------------------------------------------------------- /dist/es6-promise.js: -------------------------------------------------------------------------------- 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 4.1.0+f9a5575b 7 | */ 8 | 9 | (function (global, factory) { 10 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 11 | typeof define === 'function' && define.amd ? define(factory) : 12 | (global.ES6Promise = factory()); 13 | }(this, (function () { 'use strict'; 14 | 15 | function objectOrFunction(x) { 16 | return typeof x === 'function' || typeof x === 'object' && x !== null; 17 | } 18 | 19 | function isFunction(x) { 20 | return typeof x === 'function'; 21 | } 22 | 23 | var _isArray = undefined; 24 | if (!Array.isArray) { 25 | _isArray = function (x) { 26 | return Object.prototype.toString.call(x) === '[object Array]'; 27 | }; 28 | } else { 29 | _isArray = Array.isArray; 30 | } 31 | 32 | var isArray = _isArray; 33 | 34 | var len = 0; 35 | var vertxNext = undefined; 36 | var customSchedulerFn = undefined; 37 | 38 | var asap = function asap(callback, arg) { 39 | queue[len] = callback; 40 | queue[len + 1] = arg; 41 | len += 2; 42 | if (len === 2) { 43 | // If len is 2, that means that we need to schedule an async flush. 44 | // If additional callbacks are queued before the queue is flushed, they 45 | // will be processed by this flush that we are scheduling. 46 | if (customSchedulerFn) { 47 | customSchedulerFn(flush); 48 | } else { 49 | scheduleFlush(); 50 | } 51 | } 52 | }; 53 | 54 | function setScheduler(scheduleFn) { 55 | customSchedulerFn = scheduleFn; 56 | } 57 | 58 | function setAsap(asapFn) { 59 | asap = asapFn; 60 | } 61 | 62 | var browserWindow = typeof window !== 'undefined' ? window : undefined; 63 | var browserGlobal = browserWindow || {}; 64 | var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; 65 | var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && ({}).toString.call(process) === '[object process]'; 66 | 67 | // test for web worker but not in IE10 68 | var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; 69 | 70 | // node 71 | function useNextTick() { 72 | // node version 0.10.x displays a deprecation warning when nextTick is used recursively 73 | // see https://github.com/cujojs/when/issues/410 for details 74 | return function () { 75 | return process.nextTick(flush); 76 | }; 77 | } 78 | 79 | // vertx 80 | function useVertxTimer() { 81 | if (typeof vertxNext !== 'undefined') { 82 | return function () { 83 | vertxNext(flush); 84 | }; 85 | } 86 | 87 | return useSetTimeout(); 88 | } 89 | 90 | function useMutationObserver() { 91 | var iterations = 0; 92 | var observer = new BrowserMutationObserver(flush); 93 | var node = document.createTextNode(''); 94 | observer.observe(node, { characterData: true }); 95 | 96 | return function () { 97 | node.data = iterations = ++iterations % 2; 98 | }; 99 | } 100 | 101 | // web worker 102 | function useMessageChannel() { 103 | var channel = new MessageChannel(); 104 | channel.port1.onmessage = flush; 105 | return function () { 106 | return channel.port2.postMessage(0); 107 | }; 108 | } 109 | 110 | function useSetTimeout() { 111 | // Store setTimeout reference so es6-promise will be unaffected by 112 | // other code modifying setTimeout (like sinon.useFakeTimers()) 113 | var globalSetTimeout = setTimeout; 114 | return function () { 115 | return globalSetTimeout(flush, 1); 116 | }; 117 | } 118 | 119 | var queue = new Array(1000); 120 | function flush() { 121 | for (var i = 0; i < len; i += 2) { 122 | var callback = queue[i]; 123 | var arg = queue[i + 1]; 124 | 125 | callback(arg); 126 | 127 | queue[i] = undefined; 128 | queue[i + 1] = undefined; 129 | } 130 | 131 | len = 0; 132 | } 133 | 134 | function attemptVertx() { 135 | try { 136 | var r = require; 137 | var vertx = r('vertx'); 138 | vertxNext = vertx.runOnLoop || vertx.runOnContext; 139 | return useVertxTimer(); 140 | } catch (e) { 141 | return useSetTimeout(); 142 | } 143 | } 144 | 145 | var scheduleFlush = undefined; 146 | // Decide what async method to use to triggering processing of queued callbacks: 147 | if (isNode) { 148 | scheduleFlush = useNextTick(); 149 | } else if (BrowserMutationObserver) { 150 | scheduleFlush = useMutationObserver(); 151 | } else if (isWorker) { 152 | scheduleFlush = useMessageChannel(); 153 | } else if (browserWindow === undefined && typeof require === 'function') { 154 | scheduleFlush = attemptVertx(); 155 | } else { 156 | scheduleFlush = useSetTimeout(); 157 | } 158 | 159 | function then(onFulfillment, onRejection) { 160 | var _arguments = arguments; 161 | 162 | var parent = this; 163 | 164 | var child = new this.constructor(noop); 165 | 166 | if (child[PROMISE_ID] === undefined) { 167 | makePromise(child); 168 | } 169 | 170 | var _state = parent._state; 171 | 172 | if (_state) { 173 | (function () { 174 | var callback = _arguments[_state - 1]; 175 | asap(function () { 176 | return invokeCallback(_state, child, callback, parent._result); 177 | }); 178 | })(); 179 | } else { 180 | subscribe(parent, child, onFulfillment, onRejection); 181 | } 182 | 183 | return child; 184 | } 185 | 186 | /** 187 | `Promise.resolve` returns a promise that will become resolved with the 188 | passed `value`. It is shorthand for the following: 189 | 190 | ```javascript 191 | let promise = new Promise(function(resolve, reject){ 192 | resolve(1); 193 | }); 194 | 195 | promise.then(function(value){ 196 | // value === 1 197 | }); 198 | ``` 199 | 200 | Instead of writing the above, your code now simply becomes the following: 201 | 202 | ```javascript 203 | let promise = Promise.resolve(1); 204 | 205 | promise.then(function(value){ 206 | // value === 1 207 | }); 208 | ``` 209 | 210 | @method resolve 211 | @static 212 | @param {Any} value value that the returned promise will be resolved with 213 | Useful for tooling. 214 | @return {Promise} a promise that will become fulfilled with the given 215 | `value` 216 | */ 217 | function resolve(object) { 218 | /*jshint validthis:true */ 219 | var Constructor = this; 220 | 221 | if (object && typeof object === 'object' && object.constructor === Constructor) { 222 | return object; 223 | } 224 | 225 | var promise = new Constructor(noop); 226 | _resolve(promise, object); 227 | return promise; 228 | } 229 | 230 | var PROMISE_ID = Math.random().toString(36).substring(16); 231 | 232 | function noop() {} 233 | 234 | var PENDING = void 0; 235 | var FULFILLED = 1; 236 | var REJECTED = 2; 237 | 238 | var GET_THEN_ERROR = new ErrorObject(); 239 | 240 | function selfFulfillment() { 241 | return new TypeError("You cannot resolve a promise with itself"); 242 | } 243 | 244 | function cannotReturnOwn() { 245 | return new TypeError('A promises callback cannot return that same promise.'); 246 | } 247 | 248 | function getThen(promise) { 249 | try { 250 | return promise.then; 251 | } catch (error) { 252 | GET_THEN_ERROR.error = error; 253 | return GET_THEN_ERROR; 254 | } 255 | } 256 | 257 | function tryThen(then, value, fulfillmentHandler, rejectionHandler) { 258 | try { 259 | then.call(value, fulfillmentHandler, rejectionHandler); 260 | } catch (e) { 261 | return e; 262 | } 263 | } 264 | 265 | function handleForeignThenable(promise, thenable, then) { 266 | asap(function (promise) { 267 | var sealed = false; 268 | var error = tryThen(then, thenable, function (value) { 269 | if (sealed) { 270 | return; 271 | } 272 | sealed = true; 273 | if (thenable !== value) { 274 | _resolve(promise, value); 275 | } else { 276 | fulfill(promise, value); 277 | } 278 | }, function (reason) { 279 | if (sealed) { 280 | return; 281 | } 282 | sealed = true; 283 | 284 | _reject(promise, reason); 285 | }, 'Settle: ' + (promise._label || ' unknown promise')); 286 | 287 | if (!sealed && error) { 288 | sealed = true; 289 | _reject(promise, error); 290 | } 291 | }, promise); 292 | } 293 | 294 | function handleOwnThenable(promise, thenable) { 295 | if (thenable._state === FULFILLED) { 296 | fulfill(promise, thenable._result); 297 | } else if (thenable._state === REJECTED) { 298 | _reject(promise, thenable._result); 299 | } else { 300 | subscribe(thenable, undefined, function (value) { 301 | return _resolve(promise, value); 302 | }, function (reason) { 303 | return _reject(promise, reason); 304 | }); 305 | } 306 | } 307 | 308 | function handleMaybeThenable(promise, maybeThenable, then$$) { 309 | if (maybeThenable.constructor === promise.constructor && then$$ === then && maybeThenable.constructor.resolve === resolve) { 310 | handleOwnThenable(promise, maybeThenable); 311 | } else { 312 | if (then$$ === GET_THEN_ERROR) { 313 | _reject(promise, GET_THEN_ERROR.error); 314 | GET_THEN_ERROR.error = null; 315 | } else if (then$$ === undefined) { 316 | fulfill(promise, maybeThenable); 317 | } else if (isFunction(then$$)) { 318 | handleForeignThenable(promise, maybeThenable, then$$); 319 | } else { 320 | fulfill(promise, maybeThenable); 321 | } 322 | } 323 | } 324 | 325 | function _resolve(promise, value) { 326 | if (promise === value) { 327 | _reject(promise, selfFulfillment()); 328 | } else if (objectOrFunction(value)) { 329 | handleMaybeThenable(promise, value, getThen(value)); 330 | } else { 331 | fulfill(promise, value); 332 | } 333 | } 334 | 335 | function publishRejection(promise) { 336 | if (promise._onerror) { 337 | promise._onerror(promise._result); 338 | } 339 | 340 | publish(promise); 341 | } 342 | 343 | function fulfill(promise, value) { 344 | if (promise._state !== PENDING) { 345 | return; 346 | } 347 | 348 | promise._result = value; 349 | promise._state = FULFILLED; 350 | 351 | if (promise._subscribers.length !== 0) { 352 | asap(publish, promise); 353 | } 354 | } 355 | 356 | function _reject(promise, reason) { 357 | if (promise._state !== PENDING) { 358 | return; 359 | } 360 | promise._state = REJECTED; 361 | promise._result = reason; 362 | 363 | asap(publishRejection, promise); 364 | } 365 | 366 | function subscribe(parent, child, onFulfillment, onRejection) { 367 | var _subscribers = parent._subscribers; 368 | var length = _subscribers.length; 369 | 370 | parent._onerror = null; 371 | 372 | _subscribers[length] = child; 373 | _subscribers[length + FULFILLED] = onFulfillment; 374 | _subscribers[length + REJECTED] = onRejection; 375 | 376 | if (length === 0 && parent._state) { 377 | asap(publish, parent); 378 | } 379 | } 380 | 381 | function publish(promise) { 382 | var subscribers = promise._subscribers; 383 | var settled = promise._state; 384 | 385 | if (subscribers.length === 0) { 386 | return; 387 | } 388 | 389 | var child = undefined, 390 | callback = undefined, 391 | detail = promise._result; 392 | 393 | for (var i = 0; i < subscribers.length; i += 3) { 394 | child = subscribers[i]; 395 | callback = subscribers[i + settled]; 396 | 397 | if (child) { 398 | invokeCallback(settled, child, callback, detail); 399 | } else { 400 | callback(detail); 401 | } 402 | } 403 | 404 | promise._subscribers.length = 0; 405 | } 406 | 407 | function ErrorObject() { 408 | this.error = null; 409 | } 410 | 411 | var TRY_CATCH_ERROR = new ErrorObject(); 412 | 413 | function tryCatch(callback, detail) { 414 | try { 415 | return callback(detail); 416 | } catch (e) { 417 | TRY_CATCH_ERROR.error = e; 418 | return TRY_CATCH_ERROR; 419 | } 420 | } 421 | 422 | function invokeCallback(settled, promise, callback, detail) { 423 | var hasCallback = isFunction(callback), 424 | value = undefined, 425 | error = undefined, 426 | succeeded = undefined, 427 | failed = undefined; 428 | 429 | if (hasCallback) { 430 | value = tryCatch(callback, detail); 431 | 432 | if (value === TRY_CATCH_ERROR) { 433 | failed = true; 434 | error = value.error; 435 | value.error = null; 436 | } else { 437 | succeeded = true; 438 | } 439 | 440 | if (promise === value) { 441 | _reject(promise, cannotReturnOwn()); 442 | return; 443 | } 444 | } else { 445 | value = detail; 446 | succeeded = true; 447 | } 448 | 449 | if (promise._state !== PENDING) { 450 | // noop 451 | } else if (hasCallback && succeeded) { 452 | _resolve(promise, value); 453 | } else if (failed) { 454 | _reject(promise, error); 455 | } else if (settled === FULFILLED) { 456 | fulfill(promise, value); 457 | } else if (settled === REJECTED) { 458 | _reject(promise, value); 459 | } 460 | } 461 | 462 | function initializePromise(promise, resolver) { 463 | try { 464 | resolver(function resolvePromise(value) { 465 | _resolve(promise, value); 466 | }, function rejectPromise(reason) { 467 | _reject(promise, reason); 468 | }); 469 | } catch (e) { 470 | _reject(promise, e); 471 | } 472 | } 473 | 474 | var id = 0; 475 | function nextId() { 476 | return id++; 477 | } 478 | 479 | function makePromise(promise) { 480 | promise[PROMISE_ID] = id++; 481 | promise._state = undefined; 482 | promise._result = undefined; 483 | promise._subscribers = []; 484 | } 485 | 486 | function Enumerator(Constructor, input) { 487 | this._instanceConstructor = Constructor; 488 | this.promise = new Constructor(noop); 489 | 490 | if (!this.promise[PROMISE_ID]) { 491 | makePromise(this.promise); 492 | } 493 | 494 | if (isArray(input)) { 495 | this._input = input; 496 | this.length = input.length; 497 | this._remaining = input.length; 498 | 499 | this._result = new Array(this.length); 500 | 501 | if (this.length === 0) { 502 | fulfill(this.promise, this._result); 503 | } else { 504 | this.length = this.length || 0; 505 | this._enumerate(); 506 | if (this._remaining === 0) { 507 | fulfill(this.promise, this._result); 508 | } 509 | } 510 | } else { 511 | _reject(this.promise, validationError()); 512 | } 513 | } 514 | 515 | function validationError() { 516 | return new Error('Array Methods must be provided an Array'); 517 | }; 518 | 519 | Enumerator.prototype._enumerate = function () { 520 | var length = this.length; 521 | var _input = this._input; 522 | 523 | for (var i = 0; this._state === PENDING && i < length; i++) { 524 | this._eachEntry(_input[i], i); 525 | } 526 | }; 527 | 528 | Enumerator.prototype._eachEntry = function (entry, i) { 529 | var c = this._instanceConstructor; 530 | var resolve$$ = c.resolve; 531 | 532 | if (resolve$$ === resolve) { 533 | var _then = getThen(entry); 534 | 535 | if (_then === then && entry._state !== PENDING) { 536 | this._settledAt(entry._state, i, entry._result); 537 | } else if (typeof _then !== 'function') { 538 | this._remaining--; 539 | this._result[i] = entry; 540 | } else if (c === Promise) { 541 | var promise = new c(noop); 542 | handleMaybeThenable(promise, entry, _then); 543 | this._willSettleAt(promise, i); 544 | } else { 545 | this._willSettleAt(new c(function (resolve$$) { 546 | return resolve$$(entry); 547 | }), i); 548 | } 549 | } else { 550 | this._willSettleAt(resolve$$(entry), i); 551 | } 552 | }; 553 | 554 | Enumerator.prototype._settledAt = function (state, i, value) { 555 | var promise = this.promise; 556 | 557 | if (promise._state === PENDING) { 558 | this._remaining--; 559 | 560 | if (state === REJECTED) { 561 | _reject(promise, value); 562 | } else { 563 | this._result[i] = value; 564 | } 565 | } 566 | 567 | if (this._remaining === 0) { 568 | fulfill(promise, this._result); 569 | } 570 | }; 571 | 572 | Enumerator.prototype._willSettleAt = function (promise, i) { 573 | var enumerator = this; 574 | 575 | subscribe(promise, undefined, function (value) { 576 | return enumerator._settledAt(FULFILLED, i, value); 577 | }, function (reason) { 578 | return enumerator._settledAt(REJECTED, i, reason); 579 | }); 580 | }; 581 | 582 | /** 583 | `Promise.all` accepts an array of promises, and returns a new promise which 584 | is fulfilled with an array of fulfillment values for the passed promises, or 585 | rejected with the reason of the first passed promise to be rejected. It casts all 586 | elements of the passed iterable to promises as it runs this algorithm. 587 | 588 | Example: 589 | 590 | ```javascript 591 | let promise1 = resolve(1); 592 | let promise2 = resolve(2); 593 | let promise3 = resolve(3); 594 | let promises = [ promise1, promise2, promise3 ]; 595 | 596 | Promise.all(promises).then(function(array){ 597 | // The array here would be [ 1, 2, 3 ]; 598 | }); 599 | ``` 600 | 601 | If any of the `promises` given to `all` are rejected, the first promise 602 | that is rejected will be given as an argument to the returned promises's 603 | rejection handler. For example: 604 | 605 | Example: 606 | 607 | ```javascript 608 | let promise1 = resolve(1); 609 | let promise2 = reject(new Error("2")); 610 | let promise3 = reject(new Error("3")); 611 | let promises = [ promise1, promise2, promise3 ]; 612 | 613 | Promise.all(promises).then(function(array){ 614 | // Code here never runs because there are rejected promises! 615 | }, function(error) { 616 | // error.message === "2" 617 | }); 618 | ``` 619 | 620 | @method all 621 | @static 622 | @param {Array} entries array of promises 623 | @param {String} label optional string for labeling the promise. 624 | Useful for tooling. 625 | @return {Promise} promise that is fulfilled when all `promises` have been 626 | fulfilled, or rejected if any of them become rejected. 627 | @static 628 | */ 629 | function all(entries) { 630 | return new Enumerator(this, entries).promise; 631 | } 632 | 633 | /** 634 | `Promise.race` returns a new promise which is settled in the same way as the 635 | first passed promise to settle. 636 | 637 | Example: 638 | 639 | ```javascript 640 | let promise1 = new Promise(function(resolve, reject){ 641 | setTimeout(function(){ 642 | resolve('promise 1'); 643 | }, 200); 644 | }); 645 | 646 | let promise2 = new Promise(function(resolve, reject){ 647 | setTimeout(function(){ 648 | resolve('promise 2'); 649 | }, 100); 650 | }); 651 | 652 | Promise.race([promise1, promise2]).then(function(result){ 653 | // result === 'promise 2' because it was resolved before promise1 654 | // was resolved. 655 | }); 656 | ``` 657 | 658 | `Promise.race` is deterministic in that only the state of the first 659 | settled promise matters. For example, even if other promises given to the 660 | `promises` array argument are resolved, but the first settled promise has 661 | become rejected before the other promises became fulfilled, the returned 662 | promise will become rejected: 663 | 664 | ```javascript 665 | let promise1 = new Promise(function(resolve, reject){ 666 | setTimeout(function(){ 667 | resolve('promise 1'); 668 | }, 200); 669 | }); 670 | 671 | let promise2 = new Promise(function(resolve, reject){ 672 | setTimeout(function(){ 673 | reject(new Error('promise 2')); 674 | }, 100); 675 | }); 676 | 677 | Promise.race([promise1, promise2]).then(function(result){ 678 | // Code here never runs 679 | }, function(reason){ 680 | // reason.message === 'promise 2' because promise 2 became rejected before 681 | // promise 1 became fulfilled 682 | }); 683 | ``` 684 | 685 | An example real-world use case is implementing timeouts: 686 | 687 | ```javascript 688 | Promise.race([ajax('foo.json'), timeout(5000)]) 689 | ``` 690 | 691 | @method race 692 | @static 693 | @param {Array} promises array of promises to observe 694 | Useful for tooling. 695 | @return {Promise} a promise which settles in the same way as the first passed 696 | promise to settle. 697 | */ 698 | function race(entries) { 699 | /*jshint validthis:true */ 700 | var Constructor = this; 701 | 702 | if (!isArray(entries)) { 703 | return new Constructor(function (_, reject) { 704 | return reject(new TypeError('You must pass an array to race.')); 705 | }); 706 | } else { 707 | return new Constructor(function (resolve, reject) { 708 | var length = entries.length; 709 | for (var i = 0; i < length; i++) { 710 | Constructor.resolve(entries[i]).then(resolve, reject); 711 | } 712 | }); 713 | } 714 | } 715 | 716 | /** 717 | `Promise.reject` returns a promise rejected with the passed `reason`. 718 | It is shorthand for the following: 719 | 720 | ```javascript 721 | let promise = new Promise(function(resolve, reject){ 722 | reject(new Error('WHOOPS')); 723 | }); 724 | 725 | promise.then(function(value){ 726 | // Code here doesn't run because the promise is rejected! 727 | }, function(reason){ 728 | // reason.message === 'WHOOPS' 729 | }); 730 | ``` 731 | 732 | Instead of writing the above, your code now simply becomes the following: 733 | 734 | ```javascript 735 | let promise = Promise.reject(new Error('WHOOPS')); 736 | 737 | promise.then(function(value){ 738 | // Code here doesn't run because the promise is rejected! 739 | }, function(reason){ 740 | // reason.message === 'WHOOPS' 741 | }); 742 | ``` 743 | 744 | @method reject 745 | @static 746 | @param {Any} reason value that the returned promise will be rejected with. 747 | Useful for tooling. 748 | @return {Promise} a promise rejected with the given `reason`. 749 | */ 750 | function reject(reason) { 751 | /*jshint validthis:true */ 752 | var Constructor = this; 753 | var promise = new Constructor(noop); 754 | _reject(promise, reason); 755 | return promise; 756 | } 757 | 758 | function needsResolver() { 759 | throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); 760 | } 761 | 762 | function needsNew() { 763 | throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); 764 | } 765 | 766 | /** 767 | Promise objects represent the eventual result of an asynchronous operation. The 768 | primary way of interacting with a promise is through its `then` method, which 769 | registers callbacks to receive either a promise's eventual value or the reason 770 | why the promise cannot be fulfilled. 771 | 772 | Terminology 773 | ----------- 774 | 775 | - `promise` is an object or function with a `then` method whose behavior conforms to this specification. 776 | - `thenable` is an object or function that defines a `then` method. 777 | - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). 778 | - `exception` is a value that is thrown using the throw statement. 779 | - `reason` is a value that indicates why a promise was rejected. 780 | - `settled` the final resting state of a promise, fulfilled or rejected. 781 | 782 | A promise can be in one of three states: pending, fulfilled, or rejected. 783 | 784 | Promises that are fulfilled have a fulfillment value and are in the fulfilled 785 | state. Promises that are rejected have a rejection reason and are in the 786 | rejected state. A fulfillment value is never a thenable. 787 | 788 | Promises can also be said to *resolve* a value. If this value is also a 789 | promise, then the original promise's settled state will match the value's 790 | settled state. So a promise that *resolves* a promise that rejects will 791 | itself reject, and a promise that *resolves* a promise that fulfills will 792 | itself fulfill. 793 | 794 | 795 | Basic Usage: 796 | ------------ 797 | 798 | ```js 799 | let promise = new Promise(function(resolve, reject) { 800 | // on success 801 | resolve(value); 802 | 803 | // on failure 804 | reject(reason); 805 | }); 806 | 807 | promise.then(function(value) { 808 | // on fulfillment 809 | }, function(reason) { 810 | // on rejection 811 | }); 812 | ``` 813 | 814 | Advanced Usage: 815 | --------------- 816 | 817 | Promises shine when abstracting away asynchronous interactions such as 818 | `XMLHttpRequest`s. 819 | 820 | ```js 821 | function getJSON(url) { 822 | return new Promise(function(resolve, reject){ 823 | let xhr = new XMLHttpRequest(); 824 | 825 | xhr.open('GET', url); 826 | xhr.onreadystatechange = handler; 827 | xhr.responseType = 'json'; 828 | xhr.setRequestHeader('Accept', 'application/json'); 829 | xhr.send(); 830 | 831 | function handler() { 832 | if (this.readyState === this.DONE) { 833 | if (this.status === 200) { 834 | resolve(this.response); 835 | } else { 836 | reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); 837 | } 838 | } 839 | }; 840 | }); 841 | } 842 | 843 | getJSON('/posts.json').then(function(json) { 844 | // on fulfillment 845 | }, function(reason) { 846 | // on rejection 847 | }); 848 | ``` 849 | 850 | Unlike callbacks, promises are great composable primitives. 851 | 852 | ```js 853 | Promise.all([ 854 | getJSON('/posts'), 855 | getJSON('/comments') 856 | ]).then(function(values){ 857 | values[0] // => postsJSON 858 | values[1] // => commentsJSON 859 | 860 | return values; 861 | }); 862 | ``` 863 | 864 | @class Promise 865 | @param {function} resolver 866 | Useful for tooling. 867 | @constructor 868 | */ 869 | function Promise(resolver) { 870 | this[PROMISE_ID] = nextId(); 871 | this._result = this._state = undefined; 872 | this._subscribers = []; 873 | 874 | if (noop !== resolver) { 875 | typeof resolver !== 'function' && needsResolver(); 876 | this instanceof Promise ? initializePromise(this, resolver) : needsNew(); 877 | } 878 | } 879 | 880 | Promise.all = all; 881 | Promise.race = race; 882 | Promise.resolve = resolve; 883 | Promise.reject = reject; 884 | Promise._setScheduler = setScheduler; 885 | Promise._setAsap = setAsap; 886 | Promise._asap = asap; 887 | 888 | Promise.prototype = { 889 | constructor: Promise, 890 | 891 | /** 892 | The primary way of interacting with a promise is through its `then` method, 893 | which registers callbacks to receive either a promise's eventual value or the 894 | reason why the promise cannot be fulfilled. 895 | 896 | ```js 897 | findUser().then(function(user){ 898 | // user is available 899 | }, function(reason){ 900 | // user is unavailable, and you are given the reason why 901 | }); 902 | ``` 903 | 904 | Chaining 905 | -------- 906 | 907 | The return value of `then` is itself a promise. This second, 'downstream' 908 | promise is resolved with the return value of the first promise's fulfillment 909 | or rejection handler, or rejected if the handler throws an exception. 910 | 911 | ```js 912 | findUser().then(function (user) { 913 | return user.name; 914 | }, function (reason) { 915 | return 'default name'; 916 | }).then(function (userName) { 917 | // If `findUser` fulfilled, `userName` will be the user's name, otherwise it 918 | // will be `'default name'` 919 | }); 920 | 921 | findUser().then(function (user) { 922 | throw new Error('Found user, but still unhappy'); 923 | }, function (reason) { 924 | throw new Error('`findUser` rejected and we're unhappy'); 925 | }).then(function (value) { 926 | // never reached 927 | }, function (reason) { 928 | // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. 929 | // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. 930 | }); 931 | ``` 932 | If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. 933 | 934 | ```js 935 | findUser().then(function (user) { 936 | throw new PedagogicalException('Upstream error'); 937 | }).then(function (value) { 938 | // never reached 939 | }).then(function (value) { 940 | // never reached 941 | }, function (reason) { 942 | // The `PedgagocialException` is propagated all the way down to here 943 | }); 944 | ``` 945 | 946 | Assimilation 947 | ------------ 948 | 949 | Sometimes the value you want to propagate to a downstream promise can only be 950 | retrieved asynchronously. This can be achieved by returning a promise in the 951 | fulfillment or rejection handler. The downstream promise will then be pending 952 | until the returned promise is settled. This is called *assimilation*. 953 | 954 | ```js 955 | findUser().then(function (user) { 956 | return findCommentsByAuthor(user); 957 | }).then(function (comments) { 958 | // The user's comments are now available 959 | }); 960 | ``` 961 | 962 | If the assimliated promise rejects, then the downstream promise will also reject. 963 | 964 | ```js 965 | findUser().then(function (user) { 966 | return findCommentsByAuthor(user); 967 | }).then(function (comments) { 968 | // If `findCommentsByAuthor` fulfills, we'll have the value here 969 | }, function (reason) { 970 | // If `findCommentsByAuthor` rejects, we'll have the reason here 971 | }); 972 | ``` 973 | 974 | Simple Example 975 | -------------- 976 | 977 | Synchronous Example 978 | 979 | ```javascript 980 | let result; 981 | 982 | try { 983 | result = findResult(); 984 | // success 985 | } catch(reason) { 986 | // failure 987 | } 988 | ``` 989 | 990 | Errback Example 991 | 992 | ```js 993 | findResult(function(result, err){ 994 | if (err) { 995 | // failure 996 | } else { 997 | // success 998 | } 999 | }); 1000 | ``` 1001 | 1002 | Promise Example; 1003 | 1004 | ```javascript 1005 | findResult().then(function(result){ 1006 | // success 1007 | }, function(reason){ 1008 | // failure 1009 | }); 1010 | ``` 1011 | 1012 | Advanced Example 1013 | -------------- 1014 | 1015 | Synchronous Example 1016 | 1017 | ```javascript 1018 | let author, books; 1019 | 1020 | try { 1021 | author = findAuthor(); 1022 | books = findBooksByAuthor(author); 1023 | // success 1024 | } catch(reason) { 1025 | // failure 1026 | } 1027 | ``` 1028 | 1029 | Errback Example 1030 | 1031 | ```js 1032 | 1033 | function foundBooks(books) { 1034 | 1035 | } 1036 | 1037 | function failure(reason) { 1038 | 1039 | } 1040 | 1041 | findAuthor(function(author, err){ 1042 | if (err) { 1043 | failure(err); 1044 | // failure 1045 | } else { 1046 | try { 1047 | findBoooksByAuthor(author, function(books, err) { 1048 | if (err) { 1049 | failure(err); 1050 | } else { 1051 | try { 1052 | foundBooks(books); 1053 | } catch(reason) { 1054 | failure(reason); 1055 | } 1056 | } 1057 | }); 1058 | } catch(error) { 1059 | failure(err); 1060 | } 1061 | // success 1062 | } 1063 | }); 1064 | ``` 1065 | 1066 | Promise Example; 1067 | 1068 | ```javascript 1069 | findAuthor(). 1070 | then(findBooksByAuthor). 1071 | then(function(books){ 1072 | // found books 1073 | }).catch(function(reason){ 1074 | // something went wrong 1075 | }); 1076 | ``` 1077 | 1078 | @method then 1079 | @param {Function} onFulfilled 1080 | @param {Function} onRejected 1081 | Useful for tooling. 1082 | @return {Promise} 1083 | */ 1084 | then: then, 1085 | 1086 | /** 1087 | `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same 1088 | as the catch block of a try/catch statement. 1089 | 1090 | ```js 1091 | function findAuthor(){ 1092 | throw new Error('couldn't find that author'); 1093 | } 1094 | 1095 | // synchronous 1096 | try { 1097 | findAuthor(); 1098 | } catch(reason) { 1099 | // something went wrong 1100 | } 1101 | 1102 | // async with promises 1103 | findAuthor().catch(function(reason){ 1104 | // something went wrong 1105 | }); 1106 | ``` 1107 | 1108 | @method catch 1109 | @param {Function} onRejection 1110 | Useful for tooling. 1111 | @return {Promise} 1112 | */ 1113 | 'catch': function _catch(onRejection) { 1114 | return this.then(null, onRejection); 1115 | } 1116 | }; 1117 | 1118 | function polyfill() { 1119 | var local = undefined; 1120 | 1121 | if (typeof global !== 'undefined') { 1122 | local = global; 1123 | } else if (typeof self !== 'undefined') { 1124 | local = self; 1125 | } else { 1126 | try { 1127 | local = Function('return this')(); 1128 | } catch (e) { 1129 | throw new Error('polyfill failed because global object is unavailable in this environment'); 1130 | } 1131 | } 1132 | 1133 | var P = local.Promise; 1134 | 1135 | if (P) { 1136 | var promiseToString = null; 1137 | try { 1138 | promiseToString = Object.prototype.toString.call(P.resolve()); 1139 | } catch (e) { 1140 | // silently ignored 1141 | } 1142 | 1143 | if (promiseToString === '[object Promise]' && !P.cast) { 1144 | return; 1145 | } 1146 | } 1147 | 1148 | local.Promise = Promise; 1149 | } 1150 | 1151 | // Strange compat.. 1152 | Promise.polyfill = polyfill; 1153 | Promise.Promise = Promise; 1154 | 1155 | return Promise; 1156 | 1157 | }))); 1158 | //# sourceMappingURL=es6-promise.map 1159 | -------------------------------------------------------------------------------- /dist/es6-promise.auto.js: -------------------------------------------------------------------------------- 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 4.1.0+f9a5575b 7 | */ 8 | 9 | (function (global, factory) { 10 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 11 | typeof define === 'function' && define.amd ? define(factory) : 12 | (global.ES6Promise = factory()); 13 | }(this, (function () { 'use strict'; 14 | 15 | function objectOrFunction(x) { 16 | return typeof x === 'function' || typeof x === 'object' && x !== null; 17 | } 18 | 19 | function isFunction(x) { 20 | return typeof x === 'function'; 21 | } 22 | 23 | var _isArray = undefined; 24 | if (!Array.isArray) { 25 | _isArray = function (x) { 26 | return Object.prototype.toString.call(x) === '[object Array]'; 27 | }; 28 | } else { 29 | _isArray = Array.isArray; 30 | } 31 | 32 | var isArray = _isArray; 33 | 34 | var len = 0; 35 | var vertxNext = undefined; 36 | var customSchedulerFn = undefined; 37 | 38 | var asap = function asap(callback, arg) { 39 | queue[len] = callback; 40 | queue[len + 1] = arg; 41 | len += 2; 42 | if (len === 2) { 43 | // If len is 2, that means that we need to schedule an async flush. 44 | // If additional callbacks are queued before the queue is flushed, they 45 | // will be processed by this flush that we are scheduling. 46 | if (customSchedulerFn) { 47 | customSchedulerFn(flush); 48 | } else { 49 | scheduleFlush(); 50 | } 51 | } 52 | }; 53 | 54 | function setScheduler(scheduleFn) { 55 | customSchedulerFn = scheduleFn; 56 | } 57 | 58 | function setAsap(asapFn) { 59 | asap = asapFn; 60 | } 61 | 62 | var browserWindow = typeof window !== 'undefined' ? window : undefined; 63 | var browserGlobal = browserWindow || {}; 64 | var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; 65 | var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && ({}).toString.call(process) === '[object process]'; 66 | 67 | // test for web worker but not in IE10 68 | var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; 69 | 70 | // node 71 | function useNextTick() { 72 | // node version 0.10.x displays a deprecation warning when nextTick is used recursively 73 | // see https://github.com/cujojs/when/issues/410 for details 74 | return function () { 75 | return process.nextTick(flush); 76 | }; 77 | } 78 | 79 | // vertx 80 | function useVertxTimer() { 81 | if (typeof vertxNext !== 'undefined') { 82 | return function () { 83 | vertxNext(flush); 84 | }; 85 | } 86 | 87 | return useSetTimeout(); 88 | } 89 | 90 | function useMutationObserver() { 91 | var iterations = 0; 92 | var observer = new BrowserMutationObserver(flush); 93 | var node = document.createTextNode(''); 94 | observer.observe(node, { characterData: true }); 95 | 96 | return function () { 97 | node.data = iterations = ++iterations % 2; 98 | }; 99 | } 100 | 101 | // web worker 102 | function useMessageChannel() { 103 | var channel = new MessageChannel(); 104 | channel.port1.onmessage = flush; 105 | return function () { 106 | return channel.port2.postMessage(0); 107 | }; 108 | } 109 | 110 | function useSetTimeout() { 111 | // Store setTimeout reference so es6-promise will be unaffected by 112 | // other code modifying setTimeout (like sinon.useFakeTimers()) 113 | var globalSetTimeout = setTimeout; 114 | return function () { 115 | return globalSetTimeout(flush, 1); 116 | }; 117 | } 118 | 119 | var queue = new Array(1000); 120 | function flush() { 121 | for (var i = 0; i < len; i += 2) { 122 | var callback = queue[i]; 123 | var arg = queue[i + 1]; 124 | 125 | callback(arg); 126 | 127 | queue[i] = undefined; 128 | queue[i + 1] = undefined; 129 | } 130 | 131 | len = 0; 132 | } 133 | 134 | function attemptVertx() { 135 | try { 136 | var r = require; 137 | var vertx = r('vertx'); 138 | vertxNext = vertx.runOnLoop || vertx.runOnContext; 139 | return useVertxTimer(); 140 | } catch (e) { 141 | return useSetTimeout(); 142 | } 143 | } 144 | 145 | var scheduleFlush = undefined; 146 | // Decide what async method to use to triggering processing of queued callbacks: 147 | if (isNode) { 148 | scheduleFlush = useNextTick(); 149 | } else if (BrowserMutationObserver) { 150 | scheduleFlush = useMutationObserver(); 151 | } else if (isWorker) { 152 | scheduleFlush = useMessageChannel(); 153 | } else if (browserWindow === undefined && typeof require === 'function') { 154 | scheduleFlush = attemptVertx(); 155 | } else { 156 | scheduleFlush = useSetTimeout(); 157 | } 158 | 159 | function then(onFulfillment, onRejection) { 160 | var _arguments = arguments; 161 | 162 | var parent = this; 163 | 164 | var child = new this.constructor(noop); 165 | 166 | if (child[PROMISE_ID] === undefined) { 167 | makePromise(child); 168 | } 169 | 170 | var _state = parent._state; 171 | 172 | if (_state) { 173 | (function () { 174 | var callback = _arguments[_state - 1]; 175 | asap(function () { 176 | return invokeCallback(_state, child, callback, parent._result); 177 | }); 178 | })(); 179 | } else { 180 | subscribe(parent, child, onFulfillment, onRejection); 181 | } 182 | 183 | return child; 184 | } 185 | 186 | /** 187 | `Promise.resolve` returns a promise that will become resolved with the 188 | passed `value`. It is shorthand for the following: 189 | 190 | ```javascript 191 | let promise = new Promise(function(resolve, reject){ 192 | resolve(1); 193 | }); 194 | 195 | promise.then(function(value){ 196 | // value === 1 197 | }); 198 | ``` 199 | 200 | Instead of writing the above, your code now simply becomes the following: 201 | 202 | ```javascript 203 | let promise = Promise.resolve(1); 204 | 205 | promise.then(function(value){ 206 | // value === 1 207 | }); 208 | ``` 209 | 210 | @method resolve 211 | @static 212 | @param {Any} value value that the returned promise will be resolved with 213 | Useful for tooling. 214 | @return {Promise} a promise that will become fulfilled with the given 215 | `value` 216 | */ 217 | function resolve(object) { 218 | /*jshint validthis:true */ 219 | var Constructor = this; 220 | 221 | if (object && typeof object === 'object' && object.constructor === Constructor) { 222 | return object; 223 | } 224 | 225 | var promise = new Constructor(noop); 226 | _resolve(promise, object); 227 | return promise; 228 | } 229 | 230 | var PROMISE_ID = Math.random().toString(36).substring(16); 231 | 232 | function noop() {} 233 | 234 | var PENDING = void 0; 235 | var FULFILLED = 1; 236 | var REJECTED = 2; 237 | 238 | var GET_THEN_ERROR = new ErrorObject(); 239 | 240 | function selfFulfillment() { 241 | return new TypeError("You cannot resolve a promise with itself"); 242 | } 243 | 244 | function cannotReturnOwn() { 245 | return new TypeError('A promises callback cannot return that same promise.'); 246 | } 247 | 248 | function getThen(promise) { 249 | try { 250 | return promise.then; 251 | } catch (error) { 252 | GET_THEN_ERROR.error = error; 253 | return GET_THEN_ERROR; 254 | } 255 | } 256 | 257 | function tryThen(then, value, fulfillmentHandler, rejectionHandler) { 258 | try { 259 | then.call(value, fulfillmentHandler, rejectionHandler); 260 | } catch (e) { 261 | return e; 262 | } 263 | } 264 | 265 | function handleForeignThenable(promise, thenable, then) { 266 | asap(function (promise) { 267 | var sealed = false; 268 | var error = tryThen(then, thenable, function (value) { 269 | if (sealed) { 270 | return; 271 | } 272 | sealed = true; 273 | if (thenable !== value) { 274 | _resolve(promise, value); 275 | } else { 276 | fulfill(promise, value); 277 | } 278 | }, function (reason) { 279 | if (sealed) { 280 | return; 281 | } 282 | sealed = true; 283 | 284 | _reject(promise, reason); 285 | }, 'Settle: ' + (promise._label || ' unknown promise')); 286 | 287 | if (!sealed && error) { 288 | sealed = true; 289 | _reject(promise, error); 290 | } 291 | }, promise); 292 | } 293 | 294 | function handleOwnThenable(promise, thenable) { 295 | if (thenable._state === FULFILLED) { 296 | fulfill(promise, thenable._result); 297 | } else if (thenable._state === REJECTED) { 298 | _reject(promise, thenable._result); 299 | } else { 300 | subscribe(thenable, undefined, function (value) { 301 | return _resolve(promise, value); 302 | }, function (reason) { 303 | return _reject(promise, reason); 304 | }); 305 | } 306 | } 307 | 308 | function handleMaybeThenable(promise, maybeThenable, then$$) { 309 | if (maybeThenable.constructor === promise.constructor && then$$ === then && maybeThenable.constructor.resolve === resolve) { 310 | handleOwnThenable(promise, maybeThenable); 311 | } else { 312 | if (then$$ === GET_THEN_ERROR) { 313 | _reject(promise, GET_THEN_ERROR.error); 314 | GET_THEN_ERROR.error = null; 315 | } else if (then$$ === undefined) { 316 | fulfill(promise, maybeThenable); 317 | } else if (isFunction(then$$)) { 318 | handleForeignThenable(promise, maybeThenable, then$$); 319 | } else { 320 | fulfill(promise, maybeThenable); 321 | } 322 | } 323 | } 324 | 325 | function _resolve(promise, value) { 326 | if (promise === value) { 327 | _reject(promise, selfFulfillment()); 328 | } else if (objectOrFunction(value)) { 329 | handleMaybeThenable(promise, value, getThen(value)); 330 | } else { 331 | fulfill(promise, value); 332 | } 333 | } 334 | 335 | function publishRejection(promise) { 336 | if (promise._onerror) { 337 | promise._onerror(promise._result); 338 | } 339 | 340 | publish(promise); 341 | } 342 | 343 | function fulfill(promise, value) { 344 | if (promise._state !== PENDING) { 345 | return; 346 | } 347 | 348 | promise._result = value; 349 | promise._state = FULFILLED; 350 | 351 | if (promise._subscribers.length !== 0) { 352 | asap(publish, promise); 353 | } 354 | } 355 | 356 | function _reject(promise, reason) { 357 | if (promise._state !== PENDING) { 358 | return; 359 | } 360 | promise._state = REJECTED; 361 | promise._result = reason; 362 | 363 | asap(publishRejection, promise); 364 | } 365 | 366 | function subscribe(parent, child, onFulfillment, onRejection) { 367 | var _subscribers = parent._subscribers; 368 | var length = _subscribers.length; 369 | 370 | parent._onerror = null; 371 | 372 | _subscribers[length] = child; 373 | _subscribers[length + FULFILLED] = onFulfillment; 374 | _subscribers[length + REJECTED] = onRejection; 375 | 376 | if (length === 0 && parent._state) { 377 | asap(publish, parent); 378 | } 379 | } 380 | 381 | function publish(promise) { 382 | var subscribers = promise._subscribers; 383 | var settled = promise._state; 384 | 385 | if (subscribers.length === 0) { 386 | return; 387 | } 388 | 389 | var child = undefined, 390 | callback = undefined, 391 | detail = promise._result; 392 | 393 | for (var i = 0; i < subscribers.length; i += 3) { 394 | child = subscribers[i]; 395 | callback = subscribers[i + settled]; 396 | 397 | if (child) { 398 | invokeCallback(settled, child, callback, detail); 399 | } else { 400 | callback(detail); 401 | } 402 | } 403 | 404 | promise._subscribers.length = 0; 405 | } 406 | 407 | function ErrorObject() { 408 | this.error = null; 409 | } 410 | 411 | var TRY_CATCH_ERROR = new ErrorObject(); 412 | 413 | function tryCatch(callback, detail) { 414 | try { 415 | return callback(detail); 416 | } catch (e) { 417 | TRY_CATCH_ERROR.error = e; 418 | return TRY_CATCH_ERROR; 419 | } 420 | } 421 | 422 | function invokeCallback(settled, promise, callback, detail) { 423 | var hasCallback = isFunction(callback), 424 | value = undefined, 425 | error = undefined, 426 | succeeded = undefined, 427 | failed = undefined; 428 | 429 | if (hasCallback) { 430 | value = tryCatch(callback, detail); 431 | 432 | if (value === TRY_CATCH_ERROR) { 433 | failed = true; 434 | error = value.error; 435 | value.error = null; 436 | } else { 437 | succeeded = true; 438 | } 439 | 440 | if (promise === value) { 441 | _reject(promise, cannotReturnOwn()); 442 | return; 443 | } 444 | } else { 445 | value = detail; 446 | succeeded = true; 447 | } 448 | 449 | if (promise._state !== PENDING) { 450 | // noop 451 | } else if (hasCallback && succeeded) { 452 | _resolve(promise, value); 453 | } else if (failed) { 454 | _reject(promise, error); 455 | } else if (settled === FULFILLED) { 456 | fulfill(promise, value); 457 | } else if (settled === REJECTED) { 458 | _reject(promise, value); 459 | } 460 | } 461 | 462 | function initializePromise(promise, resolver) { 463 | try { 464 | resolver(function resolvePromise(value) { 465 | _resolve(promise, value); 466 | }, function rejectPromise(reason) { 467 | _reject(promise, reason); 468 | }); 469 | } catch (e) { 470 | _reject(promise, e); 471 | } 472 | } 473 | 474 | var id = 0; 475 | function nextId() { 476 | return id++; 477 | } 478 | 479 | function makePromise(promise) { 480 | promise[PROMISE_ID] = id++; 481 | promise._state = undefined; 482 | promise._result = undefined; 483 | promise._subscribers = []; 484 | } 485 | 486 | function Enumerator(Constructor, input) { 487 | this._instanceConstructor = Constructor; 488 | this.promise = new Constructor(noop); 489 | 490 | if (!this.promise[PROMISE_ID]) { 491 | makePromise(this.promise); 492 | } 493 | 494 | if (isArray(input)) { 495 | this._input = input; 496 | this.length = input.length; 497 | this._remaining = input.length; 498 | 499 | this._result = new Array(this.length); 500 | 501 | if (this.length === 0) { 502 | fulfill(this.promise, this._result); 503 | } else { 504 | this.length = this.length || 0; 505 | this._enumerate(); 506 | if (this._remaining === 0) { 507 | fulfill(this.promise, this._result); 508 | } 509 | } 510 | } else { 511 | _reject(this.promise, validationError()); 512 | } 513 | } 514 | 515 | function validationError() { 516 | return new Error('Array Methods must be provided an Array'); 517 | }; 518 | 519 | Enumerator.prototype._enumerate = function () { 520 | var length = this.length; 521 | var _input = this._input; 522 | 523 | for (var i = 0; this._state === PENDING && i < length; i++) { 524 | this._eachEntry(_input[i], i); 525 | } 526 | }; 527 | 528 | Enumerator.prototype._eachEntry = function (entry, i) { 529 | var c = this._instanceConstructor; 530 | var resolve$$ = c.resolve; 531 | 532 | if (resolve$$ === resolve) { 533 | var _then = getThen(entry); 534 | 535 | if (_then === then && entry._state !== PENDING) { 536 | this._settledAt(entry._state, i, entry._result); 537 | } else if (typeof _then !== 'function') { 538 | this._remaining--; 539 | this._result[i] = entry; 540 | } else if (c === Promise) { 541 | var promise = new c(noop); 542 | handleMaybeThenable(promise, entry, _then); 543 | this._willSettleAt(promise, i); 544 | } else { 545 | this._willSettleAt(new c(function (resolve$$) { 546 | return resolve$$(entry); 547 | }), i); 548 | } 549 | } else { 550 | this._willSettleAt(resolve$$(entry), i); 551 | } 552 | }; 553 | 554 | Enumerator.prototype._settledAt = function (state, i, value) { 555 | var promise = this.promise; 556 | 557 | if (promise._state === PENDING) { 558 | this._remaining--; 559 | 560 | if (state === REJECTED) { 561 | _reject(promise, value); 562 | } else { 563 | this._result[i] = value; 564 | } 565 | } 566 | 567 | if (this._remaining === 0) { 568 | fulfill(promise, this._result); 569 | } 570 | }; 571 | 572 | Enumerator.prototype._willSettleAt = function (promise, i) { 573 | var enumerator = this; 574 | 575 | subscribe(promise, undefined, function (value) { 576 | return enumerator._settledAt(FULFILLED, i, value); 577 | }, function (reason) { 578 | return enumerator._settledAt(REJECTED, i, reason); 579 | }); 580 | }; 581 | 582 | /** 583 | `Promise.all` accepts an array of promises, and returns a new promise which 584 | is fulfilled with an array of fulfillment values for the passed promises, or 585 | rejected with the reason of the first passed promise to be rejected. It casts all 586 | elements of the passed iterable to promises as it runs this algorithm. 587 | 588 | Example: 589 | 590 | ```javascript 591 | let promise1 = resolve(1); 592 | let promise2 = resolve(2); 593 | let promise3 = resolve(3); 594 | let promises = [ promise1, promise2, promise3 ]; 595 | 596 | Promise.all(promises).then(function(array){ 597 | // The array here would be [ 1, 2, 3 ]; 598 | }); 599 | ``` 600 | 601 | If any of the `promises` given to `all` are rejected, the first promise 602 | that is rejected will be given as an argument to the returned promises's 603 | rejection handler. For example: 604 | 605 | Example: 606 | 607 | ```javascript 608 | let promise1 = resolve(1); 609 | let promise2 = reject(new Error("2")); 610 | let promise3 = reject(new Error("3")); 611 | let promises = [ promise1, promise2, promise3 ]; 612 | 613 | Promise.all(promises).then(function(array){ 614 | // Code here never runs because there are rejected promises! 615 | }, function(error) { 616 | // error.message === "2" 617 | }); 618 | ``` 619 | 620 | @method all 621 | @static 622 | @param {Array} entries array of promises 623 | @param {String} label optional string for labeling the promise. 624 | Useful for tooling. 625 | @return {Promise} promise that is fulfilled when all `promises` have been 626 | fulfilled, or rejected if any of them become rejected. 627 | @static 628 | */ 629 | function all(entries) { 630 | return new Enumerator(this, entries).promise; 631 | } 632 | 633 | /** 634 | `Promise.race` returns a new promise which is settled in the same way as the 635 | first passed promise to settle. 636 | 637 | Example: 638 | 639 | ```javascript 640 | let promise1 = new Promise(function(resolve, reject){ 641 | setTimeout(function(){ 642 | resolve('promise 1'); 643 | }, 200); 644 | }); 645 | 646 | let promise2 = new Promise(function(resolve, reject){ 647 | setTimeout(function(){ 648 | resolve('promise 2'); 649 | }, 100); 650 | }); 651 | 652 | Promise.race([promise1, promise2]).then(function(result){ 653 | // result === 'promise 2' because it was resolved before promise1 654 | // was resolved. 655 | }); 656 | ``` 657 | 658 | `Promise.race` is deterministic in that only the state of the first 659 | settled promise matters. For example, even if other promises given to the 660 | `promises` array argument are resolved, but the first settled promise has 661 | become rejected before the other promises became fulfilled, the returned 662 | promise will become rejected: 663 | 664 | ```javascript 665 | let promise1 = new Promise(function(resolve, reject){ 666 | setTimeout(function(){ 667 | resolve('promise 1'); 668 | }, 200); 669 | }); 670 | 671 | let promise2 = new Promise(function(resolve, reject){ 672 | setTimeout(function(){ 673 | reject(new Error('promise 2')); 674 | }, 100); 675 | }); 676 | 677 | Promise.race([promise1, promise2]).then(function(result){ 678 | // Code here never runs 679 | }, function(reason){ 680 | // reason.message === 'promise 2' because promise 2 became rejected before 681 | // promise 1 became fulfilled 682 | }); 683 | ``` 684 | 685 | An example real-world use case is implementing timeouts: 686 | 687 | ```javascript 688 | Promise.race([ajax('foo.json'), timeout(5000)]) 689 | ``` 690 | 691 | @method race 692 | @static 693 | @param {Array} promises array of promises to observe 694 | Useful for tooling. 695 | @return {Promise} a promise which settles in the same way as the first passed 696 | promise to settle. 697 | */ 698 | function race(entries) { 699 | /*jshint validthis:true */ 700 | var Constructor = this; 701 | 702 | if (!isArray(entries)) { 703 | return new Constructor(function (_, reject) { 704 | return reject(new TypeError('You must pass an array to race.')); 705 | }); 706 | } else { 707 | return new Constructor(function (resolve, reject) { 708 | var length = entries.length; 709 | for (var i = 0; i < length; i++) { 710 | Constructor.resolve(entries[i]).then(resolve, reject); 711 | } 712 | }); 713 | } 714 | } 715 | 716 | /** 717 | `Promise.reject` returns a promise rejected with the passed `reason`. 718 | It is shorthand for the following: 719 | 720 | ```javascript 721 | let promise = new Promise(function(resolve, reject){ 722 | reject(new Error('WHOOPS')); 723 | }); 724 | 725 | promise.then(function(value){ 726 | // Code here doesn't run because the promise is rejected! 727 | }, function(reason){ 728 | // reason.message === 'WHOOPS' 729 | }); 730 | ``` 731 | 732 | Instead of writing the above, your code now simply becomes the following: 733 | 734 | ```javascript 735 | let promise = Promise.reject(new Error('WHOOPS')); 736 | 737 | promise.then(function(value){ 738 | // Code here doesn't run because the promise is rejected! 739 | }, function(reason){ 740 | // reason.message === 'WHOOPS' 741 | }); 742 | ``` 743 | 744 | @method reject 745 | @static 746 | @param {Any} reason value that the returned promise will be rejected with. 747 | Useful for tooling. 748 | @return {Promise} a promise rejected with the given `reason`. 749 | */ 750 | function reject(reason) { 751 | /*jshint validthis:true */ 752 | var Constructor = this; 753 | var promise = new Constructor(noop); 754 | _reject(promise, reason); 755 | return promise; 756 | } 757 | 758 | function needsResolver() { 759 | throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); 760 | } 761 | 762 | function needsNew() { 763 | throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); 764 | } 765 | 766 | /** 767 | Promise objects represent the eventual result of an asynchronous operation. The 768 | primary way of interacting with a promise is through its `then` method, which 769 | registers callbacks to receive either a promise's eventual value or the reason 770 | why the promise cannot be fulfilled. 771 | 772 | Terminology 773 | ----------- 774 | 775 | - `promise` is an object or function with a `then` method whose behavior conforms to this specification. 776 | - `thenable` is an object or function that defines a `then` method. 777 | - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). 778 | - `exception` is a value that is thrown using the throw statement. 779 | - `reason` is a value that indicates why a promise was rejected. 780 | - `settled` the final resting state of a promise, fulfilled or rejected. 781 | 782 | A promise can be in one of three states: pending, fulfilled, or rejected. 783 | 784 | Promises that are fulfilled have a fulfillment value and are in the fulfilled 785 | state. Promises that are rejected have a rejection reason and are in the 786 | rejected state. A fulfillment value is never a thenable. 787 | 788 | Promises can also be said to *resolve* a value. If this value is also a 789 | promise, then the original promise's settled state will match the value's 790 | settled state. So a promise that *resolves* a promise that rejects will 791 | itself reject, and a promise that *resolves* a promise that fulfills will 792 | itself fulfill. 793 | 794 | 795 | Basic Usage: 796 | ------------ 797 | 798 | ```js 799 | let promise = new Promise(function(resolve, reject) { 800 | // on success 801 | resolve(value); 802 | 803 | // on failure 804 | reject(reason); 805 | }); 806 | 807 | promise.then(function(value) { 808 | // on fulfillment 809 | }, function(reason) { 810 | // on rejection 811 | }); 812 | ``` 813 | 814 | Advanced Usage: 815 | --------------- 816 | 817 | Promises shine when abstracting away asynchronous interactions such as 818 | `XMLHttpRequest`s. 819 | 820 | ```js 821 | function getJSON(url) { 822 | return new Promise(function(resolve, reject){ 823 | let xhr = new XMLHttpRequest(); 824 | 825 | xhr.open('GET', url); 826 | xhr.onreadystatechange = handler; 827 | xhr.responseType = 'json'; 828 | xhr.setRequestHeader('Accept', 'application/json'); 829 | xhr.send(); 830 | 831 | function handler() { 832 | if (this.readyState === this.DONE) { 833 | if (this.status === 200) { 834 | resolve(this.response); 835 | } else { 836 | reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); 837 | } 838 | } 839 | }; 840 | }); 841 | } 842 | 843 | getJSON('/posts.json').then(function(json) { 844 | // on fulfillment 845 | }, function(reason) { 846 | // on rejection 847 | }); 848 | ``` 849 | 850 | Unlike callbacks, promises are great composable primitives. 851 | 852 | ```js 853 | Promise.all([ 854 | getJSON('/posts'), 855 | getJSON('/comments') 856 | ]).then(function(values){ 857 | values[0] // => postsJSON 858 | values[1] // => commentsJSON 859 | 860 | return values; 861 | }); 862 | ``` 863 | 864 | @class Promise 865 | @param {function} resolver 866 | Useful for tooling. 867 | @constructor 868 | */ 869 | function Promise(resolver) { 870 | this[PROMISE_ID] = nextId(); 871 | this._result = this._state = undefined; 872 | this._subscribers = []; 873 | 874 | if (noop !== resolver) { 875 | typeof resolver !== 'function' && needsResolver(); 876 | this instanceof Promise ? initializePromise(this, resolver) : needsNew(); 877 | } 878 | } 879 | 880 | Promise.all = all; 881 | Promise.race = race; 882 | Promise.resolve = resolve; 883 | Promise.reject = reject; 884 | Promise._setScheduler = setScheduler; 885 | Promise._setAsap = setAsap; 886 | Promise._asap = asap; 887 | 888 | Promise.prototype = { 889 | constructor: Promise, 890 | 891 | /** 892 | The primary way of interacting with a promise is through its `then` method, 893 | which registers callbacks to receive either a promise's eventual value or the 894 | reason why the promise cannot be fulfilled. 895 | 896 | ```js 897 | findUser().then(function(user){ 898 | // user is available 899 | }, function(reason){ 900 | // user is unavailable, and you are given the reason why 901 | }); 902 | ``` 903 | 904 | Chaining 905 | -------- 906 | 907 | The return value of `then` is itself a promise. This second, 'downstream' 908 | promise is resolved with the return value of the first promise's fulfillment 909 | or rejection handler, or rejected if the handler throws an exception. 910 | 911 | ```js 912 | findUser().then(function (user) { 913 | return user.name; 914 | }, function (reason) { 915 | return 'default name'; 916 | }).then(function (userName) { 917 | // If `findUser` fulfilled, `userName` will be the user's name, otherwise it 918 | // will be `'default name'` 919 | }); 920 | 921 | findUser().then(function (user) { 922 | throw new Error('Found user, but still unhappy'); 923 | }, function (reason) { 924 | throw new Error('`findUser` rejected and we're unhappy'); 925 | }).then(function (value) { 926 | // never reached 927 | }, function (reason) { 928 | // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. 929 | // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. 930 | }); 931 | ``` 932 | If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. 933 | 934 | ```js 935 | findUser().then(function (user) { 936 | throw new PedagogicalException('Upstream error'); 937 | }).then(function (value) { 938 | // never reached 939 | }).then(function (value) { 940 | // never reached 941 | }, function (reason) { 942 | // The `PedgagocialException` is propagated all the way down to here 943 | }); 944 | ``` 945 | 946 | Assimilation 947 | ------------ 948 | 949 | Sometimes the value you want to propagate to a downstream promise can only be 950 | retrieved asynchronously. This can be achieved by returning a promise in the 951 | fulfillment or rejection handler. The downstream promise will then be pending 952 | until the returned promise is settled. This is called *assimilation*. 953 | 954 | ```js 955 | findUser().then(function (user) { 956 | return findCommentsByAuthor(user); 957 | }).then(function (comments) { 958 | // The user's comments are now available 959 | }); 960 | ``` 961 | 962 | If the assimliated promise rejects, then the downstream promise will also reject. 963 | 964 | ```js 965 | findUser().then(function (user) { 966 | return findCommentsByAuthor(user); 967 | }).then(function (comments) { 968 | // If `findCommentsByAuthor` fulfills, we'll have the value here 969 | }, function (reason) { 970 | // If `findCommentsByAuthor` rejects, we'll have the reason here 971 | }); 972 | ``` 973 | 974 | Simple Example 975 | -------------- 976 | 977 | Synchronous Example 978 | 979 | ```javascript 980 | let result; 981 | 982 | try { 983 | result = findResult(); 984 | // success 985 | } catch(reason) { 986 | // failure 987 | } 988 | ``` 989 | 990 | Errback Example 991 | 992 | ```js 993 | findResult(function(result, err){ 994 | if (err) { 995 | // failure 996 | } else { 997 | // success 998 | } 999 | }); 1000 | ``` 1001 | 1002 | Promise Example; 1003 | 1004 | ```javascript 1005 | findResult().then(function(result){ 1006 | // success 1007 | }, function(reason){ 1008 | // failure 1009 | }); 1010 | ``` 1011 | 1012 | Advanced Example 1013 | -------------- 1014 | 1015 | Synchronous Example 1016 | 1017 | ```javascript 1018 | let author, books; 1019 | 1020 | try { 1021 | author = findAuthor(); 1022 | books = findBooksByAuthor(author); 1023 | // success 1024 | } catch(reason) { 1025 | // failure 1026 | } 1027 | ``` 1028 | 1029 | Errback Example 1030 | 1031 | ```js 1032 | 1033 | function foundBooks(books) { 1034 | 1035 | } 1036 | 1037 | function failure(reason) { 1038 | 1039 | } 1040 | 1041 | findAuthor(function(author, err){ 1042 | if (err) { 1043 | failure(err); 1044 | // failure 1045 | } else { 1046 | try { 1047 | findBoooksByAuthor(author, function(books, err) { 1048 | if (err) { 1049 | failure(err); 1050 | } else { 1051 | try { 1052 | foundBooks(books); 1053 | } catch(reason) { 1054 | failure(reason); 1055 | } 1056 | } 1057 | }); 1058 | } catch(error) { 1059 | failure(err); 1060 | } 1061 | // success 1062 | } 1063 | }); 1064 | ``` 1065 | 1066 | Promise Example; 1067 | 1068 | ```javascript 1069 | findAuthor(). 1070 | then(findBooksByAuthor). 1071 | then(function(books){ 1072 | // found books 1073 | }).catch(function(reason){ 1074 | // something went wrong 1075 | }); 1076 | ``` 1077 | 1078 | @method then 1079 | @param {Function} onFulfilled 1080 | @param {Function} onRejected 1081 | Useful for tooling. 1082 | @return {Promise} 1083 | */ 1084 | then: then, 1085 | 1086 | /** 1087 | `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same 1088 | as the catch block of a try/catch statement. 1089 | 1090 | ```js 1091 | function findAuthor(){ 1092 | throw new Error('couldn't find that author'); 1093 | } 1094 | 1095 | // synchronous 1096 | try { 1097 | findAuthor(); 1098 | } catch(reason) { 1099 | // something went wrong 1100 | } 1101 | 1102 | // async with promises 1103 | findAuthor().catch(function(reason){ 1104 | // something went wrong 1105 | }); 1106 | ``` 1107 | 1108 | @method catch 1109 | @param {Function} onRejection 1110 | Useful for tooling. 1111 | @return {Promise} 1112 | */ 1113 | 'catch': function _catch(onRejection) { 1114 | return this.then(null, onRejection); 1115 | } 1116 | }; 1117 | 1118 | function polyfill() { 1119 | var local = undefined; 1120 | 1121 | if (typeof global !== 'undefined') { 1122 | local = global; 1123 | } else if (typeof self !== 'undefined') { 1124 | local = self; 1125 | } else { 1126 | try { 1127 | local = Function('return this')(); 1128 | } catch (e) { 1129 | throw new Error('polyfill failed because global object is unavailable in this environment'); 1130 | } 1131 | } 1132 | 1133 | var P = local.Promise; 1134 | 1135 | if (P) { 1136 | var promiseToString = null; 1137 | try { 1138 | promiseToString = Object.prototype.toString.call(P.resolve()); 1139 | } catch (e) { 1140 | // silently ignored 1141 | } 1142 | 1143 | if (promiseToString === '[object Promise]' && !P.cast) { 1144 | return; 1145 | } 1146 | } 1147 | 1148 | local.Promise = Promise; 1149 | } 1150 | 1151 | // Strange compat.. 1152 | Promise.polyfill = polyfill; 1153 | Promise.Promise = Promise; 1154 | 1155 | Promise.polyfill(); 1156 | 1157 | return Promise; 1158 | 1159 | }))); 1160 | //# sourceMappingURL=es6-promise.auto.map 1161 | -------------------------------------------------------------------------------- /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 | function keysOf(object) { 15 | var results = []; 16 | 17 | for (var key in object) { 18 | if (object.hasOwnProperty(key)) { 19 | results.push(key); 20 | } 21 | } 22 | 23 | return results; 24 | } 25 | 26 | var g = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this; 27 | var Promise = g.adapter.Promise; 28 | var assert = require('assert'); 29 | 30 | function objectEquals(obj1, obj2) { 31 | for (var i in obj1) { 32 | if (obj1.hasOwnProperty(i)) { 33 | if (!obj2.hasOwnProperty(i)) return false; 34 | if (obj1[i] != obj2[i]) return false; 35 | } 36 | } 37 | for (var i in obj2) { 38 | if (obj2.hasOwnProperty(i)) { 39 | if (!obj1.hasOwnProperty(i)) return false; 40 | if (obj1[i] != obj2[i]) return false; 41 | } 42 | } 43 | return true; 44 | } 45 | describe('tampering', function() { 46 | var resolve = Promise.resolve; 47 | 48 | afterEach(function() { 49 | Promise.resolve = resolve; 50 | }); 51 | 52 | describe('then assimilation', function() { 53 | it('tampered resolved and then', function() { 54 | var one = Promise.resolve(1); 55 | var two = Promise.resolve(2); 56 | var thenCalled = 0; 57 | var resolveCalled = 0; 58 | 59 | two.then = function() { 60 | thenCalled++; 61 | return Promise.prototype.then.apply(this, arguments); 62 | }; 63 | 64 | Promise.resolve = function(x) { 65 | resolveCalled++; 66 | return new Promise(function(resolve) { resolve(x); }); 67 | }; 68 | 69 | return one.then(function() { 70 | return two; 71 | }).then(function(value) { 72 | assert.equal(thenCalled, 1, 'expected then to be called once'); 73 | assert.equal(resolveCalled, 0, 'expected resolve to be called once'); 74 | assert.equal(value, 2, 'expected fulfillment value to be 2'); 75 | }); 76 | }); 77 | 78 | it('tampered resolved', function() { 79 | var one = Promise.resolve(1); 80 | var two = Promise.resolve(2); 81 | var thenCalled = 0; 82 | var resolveCalled = 0; 83 | 84 | Promise.resolve = function(x) { 85 | resolveCalled++; 86 | return new Promise(function(resolve) { resolve(x); }); 87 | }; 88 | 89 | return one.then(function() { 90 | return two; 91 | }).then(function(value) { 92 | assert.equal(resolveCalled, 0, 'expected resolve to be called once'); 93 | assert.equal(value, 2, 'expected fulfillment value to be 2'); 94 | }); 95 | }); 96 | 97 | describe('alternative constructor', function() { 98 | it('tampered resolved and then', function() { 99 | var one = Promise.resolve(1); 100 | var two = Promise.resolve(2); 101 | var thenCalled = 0; 102 | var resolveCalled = 0; 103 | var invokedAlternativeConstructor = 0; 104 | 105 | two.then = function() { 106 | thenCalled++; 107 | return Promise.prototype.then.apply(this, arguments); 108 | }; 109 | 110 | function AlternativeConstructor(executor) { 111 | invokedAlternativeConstructor++; 112 | var followers = this.followers = []; 113 | executor(function(value) { 114 | followers.forEach(function(onFulfillment) { 115 | onFulfillment(value) 116 | }); 117 | }, function() { 118 | throw TypeError('No Rejections supported'); 119 | }); 120 | } 121 | 122 | AlternativeConstructor.resolve = function(x) { 123 | resolveCalled++; 124 | return new Promise(function(resolve) { resolve(x); }); 125 | }; 126 | 127 | AlternativeConstructor.prototype.then = function(onFulfillment, onRejection) { 128 | this.followers.push(onFulfillment); 129 | }; 130 | 131 | AlternativeConstructor.resolve = function(x) { 132 | resolveCalled++; 133 | return new Promise(function(resolve) { resolve(x); }); 134 | }; 135 | 136 | one.constructor = AlternativeConstructor 137 | 138 | return one.then(function() { 139 | return two; 140 | }).then(function(value) { 141 | 142 | assert.equal(invokedAlternativeConstructor, 1, 'expected AlternativeConstructor to be invoked once'); 143 | assert.equal(thenCalled, 1, 'expected then to be called once'); 144 | assert.equal(resolveCalled, 0, 'expected resolve to be called once'); 145 | assert.equal(value, 2, 'expected fulfillment value to be 2'); 146 | }); 147 | }); 148 | }); 149 | 150 | describe('Promise.all', function() { 151 | it('tampered resolved and then', function() { 152 | var two = Promise.resolve(2); 153 | var thenCalled = 0; 154 | var resolveCalled = 0; 155 | 156 | two.then = function() { 157 | thenCalled++; 158 | return Promise.prototype.then.apply(this, arguments); 159 | }; 160 | 161 | Promise.resolve = function(x) { 162 | resolveCalled++; 163 | return new Promise(function(resolve) { resolve(x); }); 164 | }; 165 | 166 | return Promise.all([two]).then(function(value) { 167 | assert.equal(thenCalled, 1); 168 | assert.equal(resolveCalled, 1); 169 | assert.deepEqual(value, [2]); 170 | }); 171 | }); 172 | 173 | it('alternative constructor and tampered then', function() { 174 | var two = Promise.resolve(2); 175 | var thenCalled = 0; 176 | var resolveCalled = 0; 177 | 178 | two.then = function() { 179 | thenCalled++; 180 | return Promise.prototype.then.apply(this, arguments); 181 | }; 182 | 183 | function AlternativeConstructor(executor) { 184 | var followers = this.followers = []; 185 | executor(function(value) { 186 | followers.forEach(function(onFulfillment) { 187 | onFulfillment(value) 188 | }); 189 | }, function() { 190 | throw TypeError('No Rejections supported'); 191 | }); 192 | } 193 | 194 | AlternativeConstructor.resolve = function(x) { 195 | resolveCalled++; 196 | return new Promise(function(resolve) { resolve(x); }); 197 | }; 198 | 199 | AlternativeConstructor.prototype.then = function(onFulfillment, onRejection) { 200 | this.followers.push(onFulfillment); 201 | }; 202 | 203 | return Promise.all.call(AlternativeConstructor, [two]).then(function(value) { 204 | assert.equal(thenCalled, 1); 205 | assert.equal(resolveCalled, 1); 206 | assert.deepEqual(value, [2]); 207 | }); 208 | }); 209 | }); 210 | 211 | describe('core-js species test', function() { 212 | it('foreign thenable has correct internal slots', function() { 213 | var p = Promise.resolve(); 214 | 215 | function NewConstructor(it) { 216 | it(function(){}, function(){}) 217 | } 218 | 219 | p.constructor = NewConstructor; 220 | 221 | var f = p.then(function(){}); 222 | assert(f instanceof NewConstructor); 223 | }); 224 | }); 225 | describe('Promise.race', function() { 226 | it('tampered resolved and then', function() { 227 | var two = Promise.resolve(2); 228 | var thenCalled = 0; 229 | var resolveCalled = 0; 230 | 231 | two.then = function() { 232 | thenCalled++; 233 | return Promise.prototype.then.apply(this, arguments); 234 | }; 235 | 236 | Promise.resolve = function(x) { 237 | resolveCalled++; 238 | return new Promise(function(resolve) { resolve(x); }); 239 | }; 240 | 241 | return Promise.race([two]).then(function(value) { 242 | assert.equal(thenCalled, 1); 243 | assert.equal(resolveCalled, 1); 244 | assert.deepEqual(value, 2); 245 | }); 246 | }); 247 | 248 | it('alternative constructor and tampered then', function() { 249 | var two = Promise.resolve(2); 250 | var thenCalled = 0; 251 | var resolveCalled = 0; 252 | 253 | two.then = function() { 254 | thenCalled++; 255 | return Promise.prototype.then.apply(this, arguments); 256 | }; 257 | 258 | function AlternativeConstructor(executor) { 259 | var followers = this.followers = []; 260 | executor(function(value) { 261 | followers.forEach(function(onFulfillment) { 262 | onFulfillment(value) 263 | }); 264 | }, function() { 265 | throw TypeError('No Rejections supported'); 266 | }); 267 | } 268 | 269 | AlternativeConstructor.resolve = function(x) { 270 | resolveCalled++; 271 | return new Promise(function(resolve) { resolve(x); }); 272 | }; 273 | 274 | AlternativeConstructor.prototype.then = function(onFulfillment, onRejection) { 275 | this.followers.push(onFulfillment); 276 | }; 277 | 278 | return Promise.race.call(AlternativeConstructor, [two]).then(function(value) { 279 | assert.equal(thenCalled, 1); 280 | assert.equal(resolveCalled, 1); 281 | assert.deepEqual(value, 2); 282 | }); 283 | }); 284 | }); 285 | 286 | }); 287 | }); 288 | 289 | describe("extensions", function() { 290 | describe("Promise constructor", function() { 291 | it('should exist and have length 1', function() { 292 | assert(Promise); 293 | assert.equal(Promise.length, 1); 294 | }); 295 | 296 | it('should fulfill if `resolve` is called with a value', function(done) { 297 | var promise = new Promise(function(resolve) { resolve('value'); }); 298 | 299 | promise.then(function(value) { 300 | assert.equal(value, 'value'); 301 | done(); 302 | }); 303 | }); 304 | 305 | it('should reject if `reject` is called with a reason', function(done) { 306 | var promise = new Promise(function(resolve, reject) { reject('reason'); }); 307 | 308 | promise.then(function() { 309 | assert(false); 310 | done(); 311 | }, function(reason) { 312 | assert.equal(reason, 'reason'); 313 | done(); 314 | }); 315 | }); 316 | 317 | it('should be a constructor', function() { 318 | var promise = new Promise(function() {}); 319 | 320 | assert.equal(Object.getPrototypeOf(promise), Promise.prototype, '[[Prototype]] equals Promise.prototype'); 321 | assert.equal(promise.constructor, Promise, 'constructor property of instances is set correctly'); 322 | assert.equal(Promise.prototype.constructor, Promise, 'constructor property of prototype is set correctly'); 323 | }); 324 | 325 | it('should NOT work without `new`', function() { 326 | assert.throws(function(){ 327 | Promise(function(resolve) { resolve('value'); }); 328 | }, TypeError) 329 | }); 330 | 331 | it('should throw a `TypeError` if not given a function', function() { 332 | assert.throws(function () { 333 | new Promise(); 334 | }, TypeError); 335 | 336 | assert.throws(function () { 337 | new Promise({}); 338 | }, TypeError); 339 | 340 | assert.throws(function () { 341 | new Promise('boo!'); 342 | }, TypeError); 343 | }); 344 | 345 | it('should reject on resolver exception', function(done) { 346 | new Promise(function() { 347 | throw 'error'; 348 | }).then(null, function(e) { 349 | assert.equal(e, 'error'); 350 | done(); 351 | }); 352 | }); 353 | 354 | it('should not resolve multiple times', function(done) { 355 | var resolver, rejector, fulfilled = 0, rejected = 0; 356 | var thenable = { 357 | then: function(resolve, reject) { 358 | resolver = resolve; 359 | rejector = reject; 360 | } 361 | }; 362 | 363 | var promise = new Promise(function(resolve) { 364 | resolve(1); 365 | }); 366 | 367 | promise.then(function(value){ 368 | return thenable; 369 | }).then(function(value){ 370 | fulfilled++; 371 | }, function(reason) { 372 | rejected++; 373 | }); 374 | 375 | setTimeout(function() { 376 | resolver(1); 377 | resolver(1); 378 | rejector(1); 379 | rejector(1); 380 | 381 | setTimeout(function() { 382 | assert.equal(fulfilled, 1); 383 | assert.equal(rejected, 0); 384 | done(); 385 | }, 20); 386 | }, 20); 387 | 388 | }); 389 | 390 | describe('assimilation', function() { 391 | it('should assimilate if `resolve` is called with a fulfilled promise', function(done) { 392 | var originalPromise = new Promise(function(resolve) { resolve('original value'); }); 393 | var promise = new Promise(function(resolve) { resolve(originalPromise); }); 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 promise', function(done) { 402 | var originalPromise = new Promise(function(resolve, reject) { reject('original reason'); }); 403 | var promise = new Promise(function(resolve) { resolve(originalPromise); }); 404 | 405 | promise.then(function() { 406 | assert(false); 407 | done(); 408 | }, function(reason) { 409 | assert.equal(reason, 'original reason'); 410 | done(); 411 | }); 412 | }); 413 | 414 | it('should assimilate if `resolve` is called with a fulfilled thenable', function(done) { 415 | var originalThenable = { 416 | then: function (onFulfilled) { 417 | setTimeout(function() { onFulfilled('original value'); }, 0); 418 | } 419 | }; 420 | var promise = new Promise(function(resolve) { resolve(originalThenable); }); 421 | 422 | promise.then(function(value) { 423 | assert.equal(value, 'original value'); 424 | done(); 425 | }); 426 | }); 427 | 428 | it('should assimilate if `resolve` is called with a rejected thenable', function(done) { 429 | var originalThenable = { 430 | then: function (onFulfilled, onRejected) { 431 | setTimeout(function() { onRejected('original reason'); }, 0); 432 | } 433 | }; 434 | var promise = new Promise(function(resolve) { resolve(originalThenable); }); 435 | 436 | promise.then(function() { 437 | assert(false); 438 | done(); 439 | }, function(reason) { 440 | assert.equal(reason, 'original reason'); 441 | done(); 442 | }); 443 | }); 444 | 445 | 446 | it('should assimilate two levels deep, for fulfillment of self fulfilling promises', function(done) { 447 | var originalPromise, promise; 448 | originalPromise = new Promise(function(resolve) { 449 | setTimeout(function() { 450 | resolve(originalPromise); 451 | }, 0) 452 | }); 453 | 454 | promise = new Promise(function(resolve) { 455 | setTimeout(function() { 456 | resolve(originalPromise); 457 | }, 0); 458 | }); 459 | 460 | promise.then(function(value) { 461 | assert(false); 462 | done(); 463 | })['catch'](function(reason) { 464 | assert.equal(reason.message, "You cannot resolve a promise with itself"); 465 | assert(reason instanceof TypeError); 466 | done(); 467 | }); 468 | }); 469 | 470 | it('should assimilate two levels deep, for fulfillment', function(done) { 471 | var originalPromise = new Promise(function(resolve) { resolve('original value'); }); 472 | var nextPromise = new Promise(function(resolve) { resolve(originalPromise); }); 473 | var promise = new Promise(function(resolve) { resolve(nextPromise); }); 474 | 475 | promise.then(function(value) { 476 | assert.equal(value, 'original value'); 477 | done(); 478 | }); 479 | }); 480 | 481 | it('should assimilate two levels deep, for rejection', function(done) { 482 | var originalPromise = new Promise(function(resolve, reject) { reject('original reason'); }); 483 | var nextPromise = new Promise(function(resolve) { resolve(originalPromise); }); 484 | var promise = new Promise(function(resolve) { resolve(nextPromise); }); 485 | 486 | promise.then(function() { 487 | assert(false); 488 | done(); 489 | }, function(reason) { 490 | assert.equal(reason, 'original reason'); 491 | done(); 492 | }); 493 | }); 494 | 495 | it('should assimilate three levels deep, mixing thenables and promises (fulfilled case)', function(done) { 496 | var originalPromise = new Promise(function(resolve) { resolve('original value'); }); 497 | var intermediateThenable = { 498 | then: function (onFulfilled) { 499 | setTimeout(function() { onFulfilled(originalPromise); }, 0); 500 | } 501 | }; 502 | var promise = new Promise(function(resolve) { resolve(intermediateThenable); }); 503 | 504 | promise.then(function(value) { 505 | assert.equal(value, 'original value'); 506 | done(); 507 | }); 508 | }); 509 | 510 | it('should assimilate three levels deep, mixing thenables and promises (rejected case)', function(done) { 511 | var originalPromise = new Promise(function(resolve, reject) { reject('original reason'); }); 512 | var intermediateThenable = { 513 | then: function (onFulfilled) { 514 | setTimeout(function() { onFulfilled(originalPromise); }, 0); 515 | } 516 | }; 517 | var promise = new Promise(function(resolve) { resolve(intermediateThenable); }); 518 | 519 | promise.then(function() { 520 | assert(false); 521 | done(); 522 | }, function(reason) { 523 | assert.equal(reason, 'original reason'); 524 | done(); 525 | }); 526 | }); 527 | }); 528 | }); 529 | 530 | describe('Promise.all', function() { 531 | testAll(function(){ 532 | return Promise.all.apply(Promise, arguments); 533 | }); 534 | }); 535 | 536 | function testAll(all) { 537 | it('should exist', function() { 538 | assert(all); 539 | }); 540 | 541 | it('works with plan pojo input', function(done) { 542 | all([ 543 | {} 544 | ]).then(function(result) { 545 | assert.deepEqual(result, [{}]); 546 | done(); 547 | }); 548 | }); 549 | 550 | it('throws when not passed an array', function(done) { 551 | var nothing = assertRejection(all()); 552 | var string = assertRejection(all('')); 553 | var object = assertRejection(all({})); 554 | 555 | Promise.all([ 556 | nothing, 557 | string, 558 | object 559 | ]).then(function(){ done(); }); 560 | }); 561 | 562 | specify('fulfilled only after all of the other promises are fulfilled', function(done) { 563 | var firstResolved, secondResolved, firstResolver, secondResolver; 564 | 565 | var first = new Promise(function(resolve) { 566 | firstResolver = resolve; 567 | }); 568 | first.then(function() { 569 | firstResolved = true; 570 | }); 571 | 572 | var second = new Promise(function(resolve) { 573 | secondResolver = resolve; 574 | }); 575 | second.then(function() { 576 | secondResolved = true; 577 | }); 578 | 579 | setTimeout(function() { 580 | firstResolver(true); 581 | }, 0); 582 | 583 | setTimeout(function() { 584 | secondResolver(true); 585 | }, 0); 586 | 587 | all([first, second]).then(function() { 588 | assert(firstResolved); 589 | assert(secondResolved); 590 | done(); 591 | }); 592 | }); 593 | 594 | specify('rejected as soon as a promise is rejected', function(done) { 595 | var firstResolver, secondResolver; 596 | 597 | var first = new Promise(function(resolve, reject) { 598 | firstResolver = { resolve: resolve, reject: reject }; 599 | }); 600 | 601 | var second = new Promise(function(resolve, reject) { 602 | secondResolver = { resolve: resolve, reject: reject }; 603 | }); 604 | 605 | setTimeout(function() { 606 | firstResolver.reject({}); 607 | }, 0); 608 | 609 | var firstWasRejected, secondCompleted; 610 | 611 | first['catch'](function(){ 612 | firstWasRejected = true; 613 | }); 614 | 615 | second.then(function(){ 616 | secondCompleted = true; 617 | }, function() { 618 | secondCompleted = true; 619 | }); 620 | 621 | all([first, second]).then(function() { 622 | assert(false); 623 | }, function() { 624 | assert(firstWasRejected); 625 | assert(!secondCompleted); 626 | done(); 627 | }); 628 | }); 629 | 630 | specify('passes the resolved values of each promise to the callback in the correct order', function(done) { 631 | var firstResolver, secondResolver, thirdResolver; 632 | 633 | var first = new Promise(function(resolve, reject) { 634 | firstResolver = { resolve: resolve, reject: reject }; 635 | }); 636 | 637 | var second = new Promise(function(resolve, reject) { 638 | secondResolver = { resolve: resolve, reject: reject }; 639 | }); 640 | 641 | var third = new Promise(function(resolve, reject) { 642 | thirdResolver = { resolve: resolve, reject: reject }; 643 | }); 644 | 645 | thirdResolver.resolve(3); 646 | firstResolver.resolve(1); 647 | secondResolver.resolve(2); 648 | 649 | all([first, second, third]).then(function(results) { 650 | assert(results.length === 3); 651 | assert(results[0] === 1); 652 | assert(results[1] === 2); 653 | assert(results[2] === 3); 654 | done(); 655 | }); 656 | }); 657 | 658 | specify('resolves an empty array passed to all()', function(done) { 659 | all([]).then(function(results) { 660 | assert(results.length === 0); 661 | done(); 662 | }); 663 | }); 664 | 665 | specify('works with null', function(done) { 666 | all([null]).then(function(results) { 667 | assert.equal(results[0], null); 668 | done(); 669 | }); 670 | }); 671 | 672 | specify('works with a mix of promises and thenables and non-promises', function(done) { 673 | var promise = new Promise(function(resolve) { resolve(1); }); 674 | var syncThenable = { then: function (onFulfilled) { onFulfilled(2); } }; 675 | var asyncThenable = { then: function (onFulfilled) { setTimeout(function() { onFulfilled(3); }, 0); } }; 676 | var nonPromise = 4; 677 | 678 | all([promise, syncThenable, asyncThenable, nonPromise]).then(function(results) { 679 | assert(objectEquals(results, [1, 2, 3, 4])); 680 | done(); 681 | })['catch'](done); 682 | }); 683 | } 684 | 685 | describe("reject", function(){ 686 | specify("it should exist", function(){ 687 | assert(Promise.reject); 688 | }); 689 | 690 | describe('it rejects', function(){ 691 | var reason = 'the reason', 692 | promise = Promise.reject(reason); 693 | 694 | promise.then(function(){ 695 | assert(false, 'should not fulfill'); 696 | }, function(actualReason){ 697 | assert.equal(reason, actualReason); 698 | }); 699 | }); 700 | }); 701 | 702 | function assertRejection(promise) { 703 | return promise.then(function(){ 704 | assert(false, 'expected rejection, but got fulfillment'); 705 | }, function(reason){ 706 | assert(reason instanceof Error); 707 | }); 708 | } 709 | 710 | describe('race', function() { 711 | it("should exist", function() { 712 | assert(Promise.race); 713 | }); 714 | 715 | it("throws when not passed an array", function(done) { 716 | var nothing = assertRejection(Promise.race()); 717 | var string = assertRejection(Promise.race('')); 718 | var object = assertRejection(Promise.race({})); 719 | 720 | Promise.all([ 721 | nothing, 722 | string, 723 | object 724 | ]).then(function(){ done(); }); 725 | }); 726 | 727 | specify('fulfilled after one of the other promises are fulfilled', function(done) { 728 | var firstResolved, secondResolved, firstResolver, secondResolver; 729 | 730 | var first = new Promise(function(resolve) { 731 | firstResolver = resolve; 732 | }); 733 | first.then(function() { 734 | firstResolved = true; 735 | }); 736 | 737 | var second = new Promise(function(resolve) { 738 | secondResolver = resolve; 739 | }); 740 | second.then(function() { 741 | secondResolved = true; 742 | }); 743 | 744 | setTimeout(function() { 745 | firstResolver(true); 746 | }, 100); 747 | 748 | setTimeout(function() { 749 | secondResolver(true); 750 | }, 0); 751 | 752 | Promise.race([first, second]).then(function() { 753 | assert(secondResolved); 754 | assert.equal(firstResolved, undefined); 755 | done(); 756 | }); 757 | }); 758 | 759 | specify('the race begins on nextTurn and prioritized by array entry', function(done) { 760 | var firstResolver, secondResolver, nonPromise = 5; 761 | 762 | var first = new Promise(function(resolve, reject) { 763 | resolve(true); 764 | }); 765 | 766 | var second = new Promise(function(resolve, reject) { 767 | resolve(false); 768 | }); 769 | 770 | Promise.race([first, second, nonPromise]).then(function(value) { 771 | assert.equal(value, true); 772 | done(); 773 | }); 774 | }); 775 | 776 | specify('rejected as soon as a promise is rejected', function(done) { 777 | var firstResolver, secondResolver; 778 | 779 | var first = new Promise(function(resolve, reject) { 780 | firstResolver = { resolve: resolve, reject: reject }; 781 | }); 782 | 783 | var second = new Promise(function(resolve, reject) { 784 | secondResolver = { resolve: resolve, reject: reject }; 785 | }); 786 | 787 | setTimeout(function() { 788 | firstResolver.reject({}); 789 | }, 0); 790 | 791 | var firstWasRejected, secondCompleted; 792 | 793 | first['catch'](function(){ 794 | firstWasRejected = true; 795 | }); 796 | 797 | second.then(function(){ 798 | secondCompleted = true; 799 | }, function() { 800 | secondCompleted = true; 801 | }); 802 | 803 | Promise.race([first, second]).then(function() { 804 | assert(false); 805 | }, function() { 806 | assert(firstWasRejected); 807 | assert(!secondCompleted); 808 | done(); 809 | }); 810 | }); 811 | 812 | specify('resolves an empty array to forever pending Promise', function(done) { 813 | var foreverPendingPromise = Promise.race([]), 814 | wasSettled = false; 815 | 816 | foreverPendingPromise.then(function() { 817 | wasSettled = true; 818 | }, function() { 819 | wasSettled = true; 820 | }); 821 | 822 | setTimeout(function() { 823 | assert(!wasSettled); 824 | done(); 825 | }, 50); 826 | }); 827 | 828 | specify('works with a mix of promises and thenables', function(done) { 829 | var promise = new Promise(function(resolve) { setTimeout(function() { resolve(1); }, 10); }), 830 | syncThenable = { then: function (onFulfilled) { onFulfilled(2); } }; 831 | 832 | Promise.race([promise, syncThenable]).then(function(result) { 833 | assert(result, 2); 834 | done(); 835 | }); 836 | }); 837 | 838 | specify('works with a mix of thenables and non-promises', function (done) { 839 | var asyncThenable = { then: function (onFulfilled) { setTimeout(function() { onFulfilled(3); }, 0); } }, 840 | nonPromise = 4; 841 | 842 | Promise.race([asyncThenable, nonPromise]).then(function(result) { 843 | assert(result, 4); 844 | done(); 845 | }); 846 | }); 847 | }); 848 | 849 | describe("resolve", function(){ 850 | specify("it should exist", function(){ 851 | assert(Promise.resolve); 852 | }); 853 | 854 | describe("1. If x is a promise, adopt its state ", function(){ 855 | specify("1.1 If x is pending, promise must remain pending until x is fulfilled or rejected.", function(done){ 856 | var expectedValue, resolver, thenable, wrapped; 857 | 858 | expectedValue = 'the value'; 859 | thenable = { 860 | then: function(resolve, reject){ 861 | resolver = resolve; 862 | } 863 | }; 864 | 865 | wrapped = Promise.resolve(thenable); 866 | 867 | wrapped.then(function(value){ 868 | assert(value === expectedValue); 869 | done(); 870 | }); 871 | 872 | setTimeout(function(){ 873 | resolver(expectedValue); 874 | }, 10); 875 | }); 876 | 877 | specify("1.2 If/when x is fulfilled, fulfill promise with the same value.", function(done){ 878 | var expectedValue, thenable, wrapped; 879 | 880 | expectedValue = 'the value'; 881 | thenable = { 882 | then: function(resolve, reject){ 883 | resolve(expectedValue); 884 | } 885 | }; 886 | 887 | wrapped = Promise.resolve(thenable); 888 | 889 | wrapped.then(function(value){ 890 | assert(value === expectedValue); 891 | done(); 892 | }) 893 | }); 894 | 895 | specify("1.3 If/when x is rejected, reject promise with the same reason.", function(done){ 896 | var expectedError, thenable, wrapped; 897 | 898 | expectedError = new Error(); 899 | thenable = { 900 | then: function(resolve, reject){ 901 | reject(expectedError); 902 | } 903 | }; 904 | 905 | wrapped = Promise.resolve(thenable); 906 | 907 | wrapped.then(null, function(error){ 908 | assert(error === expectedError); 909 | done(); 910 | }); 911 | }); 912 | }); 913 | 914 | describe("2. Otherwise, if x is an object or function,", function(){ 915 | specify("2.1 Let then x.then", function(done){ 916 | var accessCount, resolver, wrapped, thenable; 917 | 918 | accessCount = 0; 919 | thenable = { }; 920 | 921 | // we likely don't need to test this, if the browser doesn't support it 922 | if (typeof Object.defineProperty !== "function") { done(); return; } 923 | 924 | Object.defineProperty(thenable, 'then', { 925 | get: function(){ 926 | accessCount++; 927 | 928 | if (accessCount > 1) { 929 | throw new Error(); 930 | } 931 | 932 | return function(){ }; 933 | } 934 | }); 935 | 936 | assert(accessCount === 0); 937 | 938 | wrapped = Promise.resolve(thenable); 939 | 940 | assert(accessCount === 1); 941 | 942 | done(); 943 | }); 944 | 945 | specify("2.2 If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.", function(done){ 946 | var wrapped, thenable, expectedError; 947 | 948 | expectedError = new Error(); 949 | thenable = { }; 950 | 951 | // we likely don't need to test this, if the browser doesn't support it 952 | if (typeof Object.defineProperty !== "function") { done(); return; } 953 | 954 | Object.defineProperty(thenable, 'then', { 955 | get: function(){ 956 | throw expectedError; 957 | } 958 | }); 959 | 960 | wrapped = Promise.resolve(thenable); 961 | 962 | wrapped.then(null, function(error){ 963 | assert(error === expectedError, 'incorrect exception was thrown'); 964 | done(); 965 | }); 966 | }); 967 | 968 | describe('2.3. If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where', function(){ 969 | specify('2.3.1 If/when resolvePromise is called with a value y, run Resolve(promise, y)', function(done){ 970 | var expectedSuccess, resolver, rejector, thenable, wrapped, calledThis; 971 | 972 | thenable = { 973 | then: function(resolve, reject){ 974 | calledThis = this; 975 | resolver = resolve; 976 | rejector = reject; 977 | } 978 | }; 979 | 980 | expectedSuccess = 'success'; 981 | wrapped = Promise.resolve(thenable); 982 | 983 | wrapped.then(function(success){ 984 | assert(calledThis === thenable, 'this must be the thenable'); 985 | assert(success === expectedSuccess, 'rejected promise with x'); 986 | done(); 987 | }); 988 | 989 | setTimeout(function() { 990 | resolver(expectedSuccess); 991 | }, 20); 992 | }); 993 | 994 | specify('2.3.2 If/when rejectPromise is called with a reason r, reject promise with r.', function(done){ 995 | var expectedError, resolver, rejector, thenable, wrapped, calledThis; 996 | 997 | thenable = { 998 | then: function(resolve, reject){ 999 | calledThis = this; 1000 | resolver = resolve; 1001 | rejector = reject; 1002 | } 1003 | }; 1004 | 1005 | expectedError = new Error(); 1006 | 1007 | wrapped = Promise.resolve(thenable); 1008 | 1009 | wrapped.then(null, function(error){ 1010 | assert(error === expectedError, 'rejected promise with x'); 1011 | done(); 1012 | }); 1013 | 1014 | setTimeout(function() { 1015 | rejector(expectedError); 1016 | }, 20); 1017 | }); 1018 | 1019 | 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){ 1020 | var expectedError, expectedSuccess, resolver, rejector, thenable, wrapped, calledThis, 1021 | calledRejected, calledResolved; 1022 | 1023 | calledRejected = 0; 1024 | calledResolved = 0; 1025 | 1026 | thenable = { 1027 | then: function(resolve, reject){ 1028 | calledThis = this; 1029 | resolver = resolve; 1030 | rejector = reject; 1031 | } 1032 | }; 1033 | 1034 | expectedError = new Error(); 1035 | 1036 | wrapped = Promise.resolve(thenable); 1037 | 1038 | wrapped.then(function(){ 1039 | calledResolved++; 1040 | }, function(error){ 1041 | calledRejected++; 1042 | assert(calledResolved === 0, 'never resolved'); 1043 | assert(calledRejected === 1, 'rejected only once'); 1044 | assert(error === expectedError, 'rejected promise with x'); 1045 | }); 1046 | 1047 | setTimeout(function() { 1048 | rejector(expectedError); 1049 | rejector(expectedError); 1050 | 1051 | rejector('foo'); 1052 | 1053 | resolver('bar'); 1054 | resolver('baz'); 1055 | }, 20); 1056 | 1057 | setTimeout(function(){ 1058 | assert(calledRejected === 1, 'only rejected once'); 1059 | assert(calledResolved === 0, 'never resolved'); 1060 | done(); 1061 | }, 50); 1062 | }); 1063 | 1064 | describe("2.3.4 If calling then throws an exception e", function(){ 1065 | specify("2.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.", function(done){ 1066 | var expectedSuccess, resolver, rejector, thenable, wrapped, calledThis, 1067 | calledRejected, calledResolved; 1068 | 1069 | expectedSuccess = 'success'; 1070 | 1071 | thenable = { 1072 | then: function(resolve, reject){ 1073 | resolve(expectedSuccess); 1074 | throw expectedError; 1075 | } 1076 | }; 1077 | 1078 | wrapped = Promise.resolve(thenable); 1079 | 1080 | wrapped.then(function(success){ 1081 | assert(success === expectedSuccess, 'resolved not errored'); 1082 | done(); 1083 | }); 1084 | }); 1085 | 1086 | specify("2.3.4.2 Otherwise, reject promise with e as the reason.", function(done) { 1087 | var expectedError, resolver, rejector, thenable, wrapped, calledThis, callCount; 1088 | 1089 | expectedError = new Error(); 1090 | callCount = 0; 1091 | 1092 | thenable = { then: function() { throw expectedError; } }; 1093 | 1094 | wrapped = Promise.resolve(thenable); 1095 | 1096 | wrapped.then(null, function(error){ 1097 | callCount++; 1098 | assert(expectedError === error, 'expected the correct error to be rejected'); 1099 | done(); 1100 | }); 1101 | 1102 | assert(callCount === 0, 'expected async, was sync'); 1103 | }); 1104 | }); 1105 | }); 1106 | 1107 | specify("2.4 If then is not a function, fulfill promise with x", function(done){ 1108 | var expectedError, resolver, rejector, thenable, wrapped, calledThis, callCount; 1109 | 1110 | thenable = { then: 3 }; 1111 | callCount = 0; 1112 | wrapped = Promise.resolve(thenable); 1113 | 1114 | wrapped.then(function(success){ 1115 | callCount++; 1116 | assert(thenable === success, 'fulfilled promise with x'); 1117 | done(); 1118 | }); 1119 | 1120 | assert(callCount === 0, 'expected async, was sync'); 1121 | }); 1122 | }); 1123 | 1124 | describe("3. If x is not an object or function, ", function(){ 1125 | specify("fulfill promise with x.", function(done){ 1126 | var thenable, callCount, wrapped; 1127 | 1128 | thenable = null; 1129 | callCount = 0; 1130 | wrapped = Promise.resolve(thenable); 1131 | 1132 | wrapped.then(function(success){ 1133 | callCount++; 1134 | assert(success === thenable, 'fulfilled promise with x'); 1135 | done(); 1136 | }, function(a){ 1137 | assert(false, 'should not also reject'); 1138 | }); 1139 | 1140 | assert(callCount === 0, 'expected async, was sync'); 1141 | }); 1142 | }); 1143 | }); 1144 | 1145 | if (typeof Worker !== 'undefined' && navigator.userAgent.indexOf('PhantomJS') < 1) { 1146 | describe('web worker', function () { 1147 | it('should work', function (done) { 1148 | this.timeout(2000); 1149 | var worker = new Worker('./worker.js'); 1150 | worker.addEventListener('error', function(reason) { 1151 | done(new Error("Test failed:" + reason)); 1152 | }); 1153 | worker.addEventListener('message', function (e) { 1154 | worker.terminate(); 1155 | assert.equal(e.data, 'pong'); 1156 | done(); 1157 | }); 1158 | worker.postMessage('ping'); 1159 | }); 1160 | }); 1161 | } 1162 | }); 1163 | 1164 | // thanks to @wizardwerdna for the test case -> https://github.com/tildeio/rsvp.js/issues/66 1165 | // Only run these tests in node (phantomjs cannot handle them) 1166 | if (typeof module !== 'undefined' && module.exports) { 1167 | 1168 | describe("using reduce to sum integers using promises", function(){ 1169 | it("should build the promise pipeline without error", function(){ 1170 | var array, iters, pZero, i; 1171 | 1172 | array = []; 1173 | iters = 1000; 1174 | 1175 | for (i=1; i<=iters; i++) { 1176 | array.push(i); 1177 | } 1178 | 1179 | pZero = Promise.resolve(0); 1180 | 1181 | array.reduce(function(promise, nextVal) { 1182 | return promise.then(function(currentVal) { 1183 | return Promise.resolve(currentVal + nextVal); 1184 | }); 1185 | }, pZero); 1186 | }); 1187 | 1188 | it("should get correct answer without blowing the nextTick stack", function(done){ 1189 | var pZero, array, iters, result, i; 1190 | 1191 | pZero = Promise.resolve(0); 1192 | 1193 | array = []; 1194 | iters = 1000; 1195 | 1196 | for (i=1; i<=iters; i++) { 1197 | array.push(i); 1198 | } 1199 | 1200 | result = array.reduce(function(promise, nextVal) { 1201 | return promise.then(function(currentVal) { 1202 | return Promise.resolve(currentVal + nextVal); 1203 | }); 1204 | }, pZero); 1205 | 1206 | result.then(function(value){ 1207 | assert.equal(value, (iters*(iters+1)/2)); 1208 | done(); 1209 | }); 1210 | }); 1211 | }); 1212 | } 1213 | 1214 | // Kudos to @Octane at https://github.com/getify/native-promise-only/issues/5 for this, and @getify for pinging me. 1215 | describe("Thenables should not be able to run code during assimilation", function () { 1216 | specify("resolving to a thenable", function () { 1217 | var thenCalled = false; 1218 | var thenable = { 1219 | then: function () { 1220 | thenCalled = true; 1221 | } 1222 | }; 1223 | 1224 | Promise.resolve(thenable); 1225 | assert.strictEqual(thenCalled, false); 1226 | }); 1227 | 1228 | specify("resolving to an evil promise", function () { 1229 | var thenCalled = false; 1230 | var evilPromise = Promise.resolve(); 1231 | evilPromise.then = function () { 1232 | thenCalled = true; 1233 | }; 1234 | 1235 | Promise.resolve(evilPromise); 1236 | assert.strictEqual(thenCalled, false); 1237 | }); 1238 | }); 1239 | --------------------------------------------------------------------------------