├── .npmignore ├── .gitignore ├── .travis.yml ├── test └── adapter.js ├── .jshintrc ├── package.json ├── LICENSE.txt ├── README.md └── promiscuous.js /.npmignore: -------------------------------------------------------------------------------- 1 | !dist 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "6" 5 | - "node" 6 | script: 7 | - npm test 8 | cache: 9 | directories: 10 | - node_modules 11 | -------------------------------------------------------------------------------- /test/adapter.js: -------------------------------------------------------------------------------- 1 | var Promise = require('../promiscuous'); 2 | module.exports = { 3 | resolved: Promise.resolve, 4 | rejected: Promise.reject, 5 | deferred: function () { 6 | var deferred = {}; 7 | deferred.promise = new Promise(function (resolve, reject) { 8 | deferred.resolve = resolve; 9 | deferred.reject = reject; 10 | }); 11 | return deferred; 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, // Allow use of node.js globals. 3 | "expr": true, // Allow expressions where statements are allowed. 4 | "boss": true, // Allow assignments where expressions are allowed. 5 | "-W082": true, // Allow functions to be defined within blocks. 6 | "undef": true, // Don't use undefined variables. 7 | "unused": true, // Warn about unused variables. 8 | "white": true, // Enable strict whitespace checking. 9 | "indent": 2, // The code uses an indentation width of 2 spaces. 10 | "trailing": true // Do not leave trailing whitespace. 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "promiscuous", 3 | "version": "0.7.2", 4 | "description": "A minimal and fast promise implementation", 5 | "author": "Ruben Verborgh ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/RubenVerborgh/promiscuous.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/RubenVerborgh/promiscuous/issues" 13 | }, 14 | "main": "promiscuous.js", 15 | "scripts": { 16 | "test": "promises-aplus-tests test/adapter" 17 | }, 18 | "devDependencies": { 19 | "promises-aplus-tests": "2.0.x", 20 | "uglify-js": "2.2.x" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Licensed under the MIT License 2 | 3 | Copyright (c) 2013 Ruben Verborgh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Promiscuous was a 2013 experiment to make a tiny library with full Promise compatibility.** 2 | JavaScript has drastically changed since: we now have Promises (nearly) everywhere, and transpilation with full-fledged shims for cases where we don't. 3 | 4 | So please, **do not use this library anymore**. Look into Babel and webpack when developing for pre-ES6 targets. 5 | 6 | 7 | # promiscuous 8 | 9 | Promises/A+ logo 11 | 12 | 13 | promiscuous is a tiny implementation of the [Promises/A+ spec](http://promises-aplus.github.com/promises-spec/). 14 | 15 | It is promise library in JavaScript, **small** (< 1kb [minified](https://raw.github.com/RubenVerborgh/promiscuous/dist/promiscuous-node.js) / < 0.6kb gzipped) and **fast**. 16 | 17 | ## Installation and usage 18 | ### Node 19 | First, install promiscuous with npm. 20 | ```bash 21 | $ npm install promiscuous 22 | ``` 23 | 24 | Then, include promiscuous in your code file. 25 | ```javascript 26 | var Promise = require('promiscuous'); 27 | ``` 28 | 29 | ### Browsers 30 | Include [promiscuous](https://raw.github.com/RubenVerborgh/promiscuous/dist/promiscuous-browser.js) in your HTML file. 31 | ```html 32 | 33 | ``` 34 | 35 | This version (and a minified one) can be built with: 36 | ```bash 37 | $ build/build.js 38 | ``` 39 | 40 | ## API 41 | ### Create a resolved promise 42 | ```javascript 43 | var promise = Promise.resolve("one"); 44 | promise.then(function (value) { console.log(value); }); 45 | /* one */ 46 | ``` 47 | 48 | ### Create a rejected promise 49 | ```javascript 50 | var brokenPromise = Promise.reject(new Error("Could not keep promise.")); 51 | brokenPromise.then(null, function (error) { console.error(error.message); }); 52 | /* "Could not keep promise." */ 53 | ``` 54 | 55 | You can also use the `catch` method if there is no success callback: 56 | 57 | ```javascript 58 | brokenPromise.catch(function (error) { console.error(error.message); }); 59 | /* "Could not keep promise." */ 60 | ``` 61 | 62 | ### Write a function that returns a promise 63 | ```javascript 64 | function promiseLater(something) { 65 | return new Promise(function (resolve, reject) { 66 | setTimeout(function () { 67 | if (something) 68 | resolve(something); 69 | else 70 | reject(new Error("nothing")); 71 | }, 1000); 72 | }); 73 | } 74 | promiseLater("something").then( 75 | function (value) { console.log(value); }, 76 | function (error) { console.error(error.message); }); 77 | /* something */ 78 | 79 | promiseLater(null).then( 80 | function (value) { console.log(value); }, 81 | function (error) { console.error(error.message); }); 82 | /* nothing */ 83 | ``` 84 | 85 | ### Convert an array of promises into a promise for an array 86 | ```javascript 87 | var promises = [promiseLater(1), promiseLater(2), promiseLater(3)]; 88 | Promise.all(promises).then(function (values) { console.log(values); }); 89 | /* [1, 2, 3] */ 90 | ``` 91 | -------------------------------------------------------------------------------- /promiscuous.js: -------------------------------------------------------------------------------- 1 | /**@license MIT-promiscuous-©Ruben Verborgh*/ 2 | (function (func, obj) { 3 | // Type checking utility function 4 | function is(type, item) { return (typeof item)[0] == type; } 5 | 6 | // Creates a promise, calling callback(resolve, reject), ignoring other parameters. 7 | function Promise(callback, handler) { 8 | // The `handler` variable points to the function that will 9 | // 1) handle a .then(resolved, rejected) call 10 | // 2) handle a resolve or reject call (if the first argument === `is`) 11 | // Before 2), `handler` holds a queue of callbacks. 12 | // After 2), `handler` is a finalized .then handler. 13 | handler = function pendingHandler(resolved, rejected, value, queue, then, i) { 14 | queue = pendingHandler.q; 15 | 16 | // Case 1) handle a .then(resolved, rejected) call 17 | if (resolved != is) { 18 | return Promise(function (resolve, reject) { 19 | queue.push({ p: this, r: resolve, j: reject, 1: resolved, 0: rejected }); 20 | }); 21 | } 22 | 23 | // Case 2) handle a resolve or reject call 24 | // (`resolved` === `is` acts as a sentinel) 25 | // The actual function signature is 26 | // .re[ject|solve](, success, value) 27 | 28 | // Check if the value is a promise and try to obtain its `then` method 29 | if (value && (is(func, value) | is(obj, value))) { 30 | try { then = value.then; } 31 | catch (reason) { rejected = 0; value = reason; } 32 | } 33 | // If the value is a promise, take over its state 34 | if (is(func, then)) { 35 | try { then.call(value, transferState(1), rejected = transferState(0)); } 36 | catch (reason) { rejected(reason); } 37 | } 38 | // The value is not a promise; handle resolve/reject 39 | else { 40 | // Replace this handler with a finalized resolved/rejected handler 41 | handler = function (Resolved, Rejected) { 42 | // If the Resolved or Rejected parameter is not a function, 43 | // return the original promise (now stored in the `callback` variable) 44 | if (!is(func, (Resolved = rejected ? Resolved : Rejected))) 45 | return callback; 46 | // Otherwise, return a finalized promise, transforming the value with the function 47 | return Promise(function (resolve, reject) { finalize(this, resolve, reject, value, Resolved); }); 48 | }; 49 | // Resolve/reject pending callbacks 50 | i = 0; 51 | while (i < queue.length) { 52 | then = queue[i++]; 53 | // If no callback, just resolve/reject the promise 54 | if (!is(func, resolved = then[rejected])) 55 | (rejected ? then.r : then.j)(value); 56 | // Otherwise, resolve/reject the promise with the result of the callback 57 | else 58 | finalize(then.p, then.r, then.j, value, resolved); 59 | } 60 | } 61 | // Returns a function that transfers the state of the promise 62 | function transferState(resolved) { 63 | return function (value) { then && (then = 0, pendingHandler(is, resolved, value)); }; 64 | } 65 | }; 66 | // The queue of pending callbacks; garbage-collected when handler is resolved/rejected 67 | handler.q = []; 68 | 69 | // Create and return the promise (reusing the callback variable) 70 | callback.call(callback = { then: function (resolved, rejected) { return handler(resolved, rejected); }, 71 | "catch": function (rejected) { return handler(0, rejected); } }, 72 | function (value) { handler(is, 1, value); }, 73 | function (reason) { handler(is, 0, reason); }); 74 | return callback; 75 | } 76 | 77 | // Finalizes the promise by resolving/rejecting it with the transformed value 78 | function finalize(promise, resolve, reject, value, transform) { 79 | setImmediate(function () { 80 | try { 81 | // Transform the value through and check whether it's a promise 82 | value = transform(value); 83 | transform = value && (is(obj, value) | is(func, value)) && value.then; 84 | // Return the result if it's not a promise 85 | if (!is(func, transform)) 86 | resolve(value); 87 | // If it's a promise, make sure it's not circular 88 | else if (value == promise) 89 | reject(TypeError()); 90 | // Take over the promise's state 91 | else 92 | transform.call(value, resolve, reject); 93 | } 94 | catch (error) { reject(error); } 95 | }); 96 | } 97 | 98 | // Export the main module 99 | module.exports = Promise; 100 | 101 | // Creates a resolved promise 102 | Promise.resolve = ResolvedPromise; 103 | function ResolvedPromise(value) { return Promise(function (resolve) { resolve(value); }); } 104 | 105 | // Creates a rejected promise 106 | Promise.reject = function (reason) { return Promise(function (resolve, reject) { reject(reason); }); }; 107 | 108 | // Transforms an array of promises into a promise for an array 109 | Promise.all = function (promises) { 110 | return Promise(function (resolve, reject, count, values) { 111 | // Array of collected values 112 | values = []; 113 | // Resolve immediately if there are no promises 114 | count = promises.length || resolve(values); 115 | // Transform all elements (`map` is shorter than `forEach`) 116 | promises.map(function (promise, index) { 117 | ResolvedPromise(promise).then( 118 | // Store the value and resolve if it was the last 119 | function (value) { 120 | values[index] = value; 121 | --count || resolve(values); 122 | }, 123 | // Reject if one element fails 124 | reject); 125 | }); 126 | }); 127 | }; 128 | 129 | // Returns a promise that resolves or rejects as soon as one promise in the array does 130 | Promise.race = function (promises) { 131 | return Promise(function (resolve, reject) { 132 | // Register to all promises in the array 133 | promises.map(function (promise) { 134 | ResolvedPromise(promise).then(resolve, reject); 135 | }); 136 | }); 137 | }; 138 | })('f', 'o'); 139 | --------------------------------------------------------------------------------