pax_global_header 0000666 0000000 0000000 00000000064 12327251233 0014512 g ustar 00root root 0000000 0000000 52 comment=65f94012842a1e7957a42c5ce60d888f4280ce9e
promise-5.0.0/ 0000775 0000000 0000000 00000000000 12327251233 0013172 5 ustar 00root root 0000000 0000000 promise-5.0.0/.gitignore 0000664 0000000 0000000 00000000036 12327251233 0015161 0 ustar 00root root 0000000 0000000 components
build
node_modules
promise-5.0.0/.jshintrc 0000664 0000000 0000000 00000000064 12327251233 0015017 0 ustar 00root root 0000000 0000000 {
"asi": true,
"node": true,
"strict": true
}
promise-5.0.0/.npmignore 0000664 0000000 0000000 00000000103 12327251233 0015163 0 ustar 00root root 0000000 0000000 components
node_modules
test
.gitignore
.travis.yml
component.json
promise-5.0.0/.travis.yml 0000664 0000000 0000000 00000000104 12327251233 0015276 0 ustar 00root root 0000000 0000000 language: node_js
node_js:
- "0.7"
- "0.8"
- "0.9"
- "0.10"
promise-5.0.0/LICENSE 0000664 0000000 0000000 00000002043 12327251233 0014176 0 ustar 00root root 0000000 0000000 Copyright (c) 2014 Forbes Lindesay
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
promise-5.0.0/Readme.md 0000664 0000000 0000000 00000020025 12327251233 0014710 0 ustar 00root root 0000000 0000000
# promise
This is a simple implementation of Promises. It is a super set of ES6 Promises designed to have readable, performant code and to provide just the extensions that are absolutely necessary for using promises today.
For detailed tutorials on its use, see www.promisejs.org
[](https://travis-ci.org/then/promise)
[](https://gemnasium.com/then/promise)
[](http://badge.fury.io/js/promise)
## Installation
**Server:**
$ npm install promise
**Client:**
You can use browserify on the client, or use the pre-compiled script that acts as a pollyfill.
```html
```
## Usage
The example below shows how you can load the promise library (in a way that works on both client and server). It then demonstrates creating a promise from scratch. You simply call `new Promise(fn)`. There is a complete specification for what is returned by this method in [Promises/A+](http://promises-aplus.github.com/promises-spec/).
```javascript
var Promise = require('promise');
var promise = new Promise(function (resolve, reject) {
get('http://www.google.com', function (err, res) {
if (err) reject(err);
else resolve(res);
});
});
```
## API
Before all examples, you will need:
```js
var Promise = require('promise');
```
### new Promise(resolver)
This creates and returns a new promise. `resolver` must be a function. The `resolver` function is passed two arguments:
1. `resolve` should be called with a single argument. If it is called with a non-promise value then the promise is fulfilled with that value. If it is called with a promise (A) then the returned promise takes on the state of that new promise (A).
2. `reject` should be called with a single argument. The returned promise will be rejected with that argument.
### Static Functions
These methods are invoked by calling `Promise.methodName`.
#### Promise.resolve(value)
(deprecated aliases: `Promise.from(value)`, `Promise.cast(value)`)
Converts values and foreign promises into Promises/A+ promises. If you pass it a value then it returns a Promise for that value. If you pass it something that is close to a promise (such as a jQuery attempt at a promise) it returns a Promise that takes on the state of `value` (rejected or fulfilled).
#### Promise.all(array)
Returns a promise for an array. If it is called with a single argument that `Array.isArray` then this returns a promise for a copy of that array with any promises replaced by their fulfilled values. Otherwise it returns a promise for an array that conatins its arguments, except with promises replaced by their resolution values. e.g.
```js
Promise.all([Promise.from('a'), 'b', Promise.from('c')])
.then(function (res) {
assert(res[0] === 'a')
assert(res[1] === 'b')
assert(res[2] === 'c')
})
Promise.all(Promise.from('a'), 'b', Promise.from('c'))
.then(function (res) {
assert(res[0] === 'a')
assert(res[1] === 'b')
assert(res[2] === 'c')
})
```
#### Promise.denodeify(fn)
_Non Standard_
Takes a function which accepts a node style callback and returns a new function that returns a promise instead.
e.g.
```javascript
var fs = require('fs')
var read = Promise.denodeify(fs.readFile)
var write = Promise.denodeify(fs.writeFile)
var p = read('foo.json', 'utf8')
.then(function (str) {
return write('foo.json', JSON.stringify(JSON.parse(str), null, ' '), 'utf8')
})
```
#### Promise.nodeify(fn)
_Non Standard_
The twin to `denodeify` is useful when you want to export an API that can be used by people who haven't learnt about the brilliance of promises yet.
```javascript
module.exports = Promise.nodeify(awesomeAPI)
function awesomeAPI(a, b) {
return download(a, b)
}
```
If the last argument passed to `module.exports` is a function, then it will be treated like a node.js callback and not parsed on to the child function, otherwise the API will just return a promise.
### Prototype Methods
These methods are invoked on a promise instance by calling `myPromise.methodName`
### Promise#then(onFulfilled, onRejected)
This method follows the [Promises/A+ spec](http://promises-aplus.github.io/promises-spec/). It explains things very clearly so I recommend you read it.
Either `onFulfilled` or `onRejected` will be called and they will not be called more than once. They will be passed a single argument and will always be called asynchronously (in the next turn of the event loop).
If the promise is fulfilled then `onFulfilled` is called. If the promise is rejected then `onRejected` is called.
The call to `.then` also returns a promise. If the handler that is called returns a promise, the promise returned by `.then` takes on the state of that returned promise. If the handler that is called returns a value that is not a promise, the promise returned by `.then` will be fulfilled with that value. If the handler that is called throws an exception then the promise returned by `.then` is rejected with that exception.
#### Promise#done(onFulfilled, onRejected)
_Non Standard_
The same semantics as `.then` except that it does not return a promise and any exceptions are re-thrown so that they can be logged (crashing the application in non-browser environments)
#### Promise#nodeify(callback)
_Non Standard_
If `callback` is `null` or `undefined` it just returns `this`. If `callback` is a function it is called with rejection reason as the first argument and result as the second argument (as per the node.js convention).
This lets you write API functions that look like:
```javascript
function awesomeAPI(foo, bar, callback) {
return internalAPI(foo, bar)
.then(parseResult)
.then(null, retryErrors)
.nodeify(callback)
}
```
People who use typical node.js style callbacks will be able to just pass a callback and get the expected behavior. The enlightened people can not pass a callback and will get awesome promises.
## Extending Promises
There are three options for extending the promises created by this library.
### Inheritance
You can use inheritance if you want to create your own complete promise library with this as your basic starting point, perfect if you have lots of cool features you want to add. Here is an example of a promise library called `Awesome`, which is built on top of `Promise` correctly.
```javascript
var Promise = require('promise');
function Awesome(fn) {
if (!(this instanceof Awesome)) return new Awesome(fn);
Promise.call(this, fn);
}
Awesome.prototype = Object.create(Promise.prototype);
Awesome.prototype.constructor = Awesome;
//Awesome extension
Awesome.prototype.spread = function (cb) {
return this.then(function (arr) {
return cb.apply(this, arr);
})
};
```
N.B. if you fail to set the prototype and constructor properly or fail to do Promise.call, things can fail in really subtle ways.
### Wrap
This is the nuclear option, for when you want to start from scratch. It ensures you won't be impacted by anyone who is extending the prototype (see below).
```javascript
function Uber(fn) {
if (!(this instanceof Uber)) return new Uber(fn);
var _prom = new Promise(fn);
this.then = _prom.then;
}
Uber.prototype.spread = function (cb) {
return this.then(function (arr) {
return cb.apply(this, arr);
})
};
```
### Extending the Prototype
In general, you should never extend the prototype of this promise implimenation because your extensions could easily conflict with someone elses extensions. However, this organisation will host a library of extensions which do not conflict with each other, so you can safely enable any of those. If you think of an extension that we don't provide and you want to write it, submit an issue on this repository and (if I agree) I'll set you up with a repository and give you permission to commit to it.
## License
MIT
promise-5.0.0/component.json 0000664 0000000 0000000 00000000500 12327251233 0016062 0 ustar 00root root 0000000 0000000 {
"name": "promise",
"repo": "then/promise",
"description": "Bare bones Promises/A+ implementation",
"version": "5.0.0",
"keywords": [],
"dependencies": {
"johntron/asap": "*"
},
"development": {},
"license": "MIT",
"scripts": [
"index.js",
"core.js"
],
"twitter": "@ForbesLindesay"
} promise-5.0.0/core.js 0000664 0000000 0000000 00000005066 12327251233 0014467 0 ustar 00root root 0000000 0000000 'use strict';
var asap = require('asap')
module.exports = Promise
function Promise(fn) {
if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new')
if (typeof fn !== 'function') throw new TypeError('not a function')
var state = null
var value = null
var deferreds = []
var self = this
this.then = function(onFulfilled, onRejected) {
return new Promise(function(resolve, reject) {
handle(new Handler(onFulfilled, onRejected, resolve, reject))
})
}
function handle(deferred) {
if (state === null) {
deferreds.push(deferred)
return
}
asap(function() {
var cb = state ? deferred.onFulfilled : deferred.onRejected
if (cb === null) {
(state ? deferred.resolve : deferred.reject)(value)
return
}
var ret
try {
ret = cb(value)
}
catch (e) {
deferred.reject(e)
return
}
deferred.resolve(ret)
})
}
function resolve(newValue) {
try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.')
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then
if (typeof then === 'function') {
doResolve(then.bind(newValue), resolve, reject)
return
}
}
state = true
value = newValue
finale()
} catch (e) { reject(e) }
}
function reject(newValue) {
state = false
value = newValue
finale()
}
function finale() {
for (var i = 0, len = deferreds.length; i < len; i++)
handle(deferreds[i])
deferreds = null
}
doResolve(fn, resolve, reject)
}
function Handler(onFulfilled, onRejected, resolve, reject){
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
this.onRejected = typeof onRejected === 'function' ? onRejected : null
this.resolve = resolve
this.reject = reject
}
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, onFulfilled, onRejected) {
var done = false;
try {
fn(function (value) {
if (done) return
done = true
onFulfilled(value)
}, function (reason) {
if (done) return
done = true
onRejected(reason)
})
} catch (ex) {
if (done) return
done = true
onRejected(ex)
}
}
promise-5.0.0/index.js 0000664 0000000 0000000 00000011043 12327251233 0014636 0 ustar 00root root 0000000 0000000 'use strict';
//This file contains then/promise specific extensions to the core promise API
var Promise = require('./core.js')
var asap = require('asap')
module.exports = Promise
/* Static Functions */
function ValuePromise(value) {
this.then = function (onFulfilled) {
if (typeof onFulfilled !== 'function') return this
return new Promise(function (resolve, reject) {
asap(function () {
try {
resolve(onFulfilled(value))
} catch (ex) {
reject(ex);
}
})
})
}
}
ValuePromise.prototype = Object.create(Promise.prototype)
var TRUE = new ValuePromise(true)
var FALSE = new ValuePromise(false)
var NULL = new ValuePromise(null)
var UNDEFINED = new ValuePromise(undefined)
var ZERO = new ValuePromise(0)
var EMPTYSTRING = new ValuePromise('')
Promise.resolve = function (value) {
if (value instanceof Promise) return value
if (value === null) return NULL
if (value === undefined) return UNDEFINED
if (value === true) return TRUE
if (value === false) return FALSE
if (value === 0) return ZERO
if (value === '') return EMPTYSTRING
if (typeof value === 'object' || typeof value === 'function') {
try {
var then = value.then
if (typeof then === 'function') {
return new Promise(then.bind(value))
}
} catch (ex) {
return new Promise(function (resolve, reject) {
reject(ex)
})
}
}
return new ValuePromise(value)
}
Promise.from = Promise.cast = function (value) {
var err = new Error('Promise.from and Promise.cast are deprecated, use Promise.resolve instead')
err.name = 'Warning'
console.warn(err.stack)
return Promise.resolve(value)
}
Promise.denodeify = function (fn, argumentCount) {
argumentCount = argumentCount || Infinity
return function () {
var self = this
var args = Array.prototype.slice.call(arguments)
return new Promise(function (resolve, reject) {
while (args.length && args.length > argumentCount) {
args.pop()
}
args.push(function (err, res) {
if (err) reject(err)
else resolve(res)
})
fn.apply(self, args)
})
}
}
Promise.nodeify = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments)
var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null
try {
return fn.apply(this, arguments).nodeify(callback)
} catch (ex) {
if (callback === null || typeof callback == 'undefined') {
return new Promise(function (resolve, reject) { reject(ex) })
} else {
asap(function () {
callback(ex)
})
}
}
}
}
Promise.all = function () {
var calledWithArray = arguments.length === 1 && Array.isArray(arguments[0])
var args = Array.prototype.slice.call(calledWithArray ? arguments[0] : arguments)
if (!calledWithArray) {
var err = new Error('Promise.all should be called with a single array, calling it with multiple arguments is deprecated')
err.name = 'Warning'
console.warn(err.stack)
}
return new Promise(function (resolve, reject) {
if (args.length === 0) return resolve([])
var remaining = args.length
function res(i, val) {
try {
if (val && (typeof val === 'object' || typeof val === 'function')) {
var then = val.then
if (typeof then === 'function') {
then.call(val, function (val) { res(i, val) }, reject)
return
}
}
args[i] = val
if (--remaining === 0) {
resolve(args);
}
} catch (ex) {
reject(ex)
}
}
for (var i = 0; i < args.length; i++) {
res(i, args[i])
}
})
}
Promise.reject = function (value) {
return new Promise(function (resolve, reject) {
reject(value);
});
}
Promise.race = function (values) {
return new Promise(function (resolve, reject) {
values.forEach(function(value){
Promise.resolve(value).then(resolve, reject);
})
});
}
/* Prototype Methods */
Promise.prototype.done = function (onFulfilled, onRejected) {
var self = arguments.length ? this.then.apply(this, arguments) : this
self.then(null, function (err) {
asap(function () {
throw err
})
})
}
Promise.prototype.nodeify = function (callback) {
if (typeof callback != 'function') return this
this.then(function (value) {
asap(function () {
callback(null, value)
})
}, function (err) {
asap(function () {
callback(err)
})
})
}
Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected);
}
promise-5.0.0/package.json 0000664 0000000 0000000 00000001240 12327251233 0015455 0 ustar 00root root 0000000 0000000 {
"name": "promise",
"version": "5.0.0",
"description": "Bare bones Promises/A+ implementation",
"main": "index.js",
"scripts": {
"test": "mocha -R spec --timeout 200 --slow 99999",
"test-resolve": "mocha test/resolver-tests.js -R spec --timeout 200 --slow 999999",
"test-extensions": "mocha test/extensions-tests.js -R spec --timeout 200 --slow 999999"
},
"repository": {
"type": "git",
"url": "https://github.com/then/promise.git"
},
"author": "ForbesLindesay",
"license": "MIT",
"devDependencies": {
"promises-aplus-tests": "*",
"better-assert": "*",
"mocha": "*"
},
"dependencies": {
"asap": "~1.0.0"
}
} promise-5.0.0/test/ 0000775 0000000 0000000 00000000000 12327251233 0014151 5 ustar 00root root 0000000 0000000 promise-5.0.0/test/adapter-a.js 0000664 0000000 0000000 00000000537 12327251233 0016352 0 ustar 00root root 0000000 0000000 var Promise = require('../');
exports.deferred = function () {
var resolve, reject;
var promise = new Promise(function (_resolve, _reject) {
resolve = _resolve;
reject = _reject;
});
return {
promise: promise,
resolve: resolve,
reject: reject
};
};
exports.resolved = Promise.resolve;
exports.rejected = Promise.reject; promise-5.0.0/test/extensions-tests.js 0000664 0000000 0000000 00000015246 12327251233 0020056 0 ustar 00root root 0000000 0000000 var assert = require('better-assert')
var Promise = require('../')
var sentinel = {}
var promise = new Promise(function (resolve) {
resolve(sentinel)
})
var thenable = {then: function (fullfilled, rejected) { fullfilled(sentinel) }}
var thenableRejected = {then: function (fullfilled, rejected) { rejected(sentinel) }}
var a = {}
var b = {}
var c = {}
var A = Promise.resolve(a)
var B = Promise.resolve(b)
var C = Promise.resolve(c)
var rejection = {}
var rejected = new Promise(function (resolve, reject) { reject(rejection) })
describe('extensions', function () {
describe('Promise.denodeify(fn, [argumentCount])', function () {
it('returns a function that uses promises instead of callbacks', function (done) {
function wrap(val, key, callback) {
return callback(null, {val: val, key: key})
}
var pwrap = Promise.denodeify(wrap)
pwrap(sentinel, 'foo')
.then(function (wrapper) {
assert(wrapper.val === sentinel)
assert(wrapper.key === 'foo')
done()
})
})
it('converts callback error arguments into rejection', function (done) {
function fail(val, key, callback) {
return callback(sentinel)
}
var pfail = Promise.denodeify(fail)
pfail(promise, 'foo')
.then(null, function (err) {
assert(err === sentinel)
done()
})
})
it('with an argumentCount it ignores extra arguments', function (done) {
function wrap(val, key, callback) {
return callback(null, {val: val, key: key})
}
var pwrap = Promise.denodeify(wrap, 2)
pwrap(sentinel, 'foo', 'wtf')
.then(function (wrapper) {
assert(wrapper.val === sentinel)
assert(wrapper.key === 'foo')
done()
})
})
})
describe('Promise.nodeify(fn)', function () {
it('converts a promise returning function into a callback function', function (done) {
var add = Promise.nodeify(function (a, b) {
return Promise.resolve(a)
.then(function (a) {
return a + b
})
})
add(1, 2, function (err, res) {
if (err) return done(err)
assert(res === 3)
return done()
})
})
it('converts rejected promises into the first argument of the callback', function (done) {
var add = Promise.nodeify(function (a, b) {
return Promise.resolve(a)
.then(function (a) {
throw sentinel
})
})
var add2 = Promise.nodeify(function (a, b) {
throw sentinel
})
add(1, 2, function (err, res) {
assert(err === sentinel)
add2(1, 2, function (err, res){
assert(err === sentinel)
done()
})
})
})
it('passes through when no callback is provided', function (done) {
var add = Promise.nodeify(function (a, b) {
return Promise.resolve(a)
.then(function (a) {
return a + b
})
})
add(1, 2)
.then(function (res) {
assert(res === 3)
done()
})
})
})
describe('Promise.all(...)', function () {
describe('an array', function () {
describe('that is empty', function () {
it('returns a promise for an empty array', function (done) {
var res = Promise.all([])
assert(res instanceof Promise)
res.then(function (res) {
assert(Array.isArray(res))
assert(res.length === 0)
})
.nodeify(done)
})
})
describe('of objects', function () {
it('returns a promise for the array', function (done) {
var res = Promise.all([a, b, c])
assert(res instanceof Promise)
res.then(function (res) {
assert(Array.isArray(res))
assert(res[0] === a)
assert(res[1] === b)
assert(res[2] === c)
})
.nodeify(done)
})
})
describe('of promises', function () {
it('returns a promise for an array containing the fulfilled values', function (done) {
var res = Promise.all([A, B, C])
assert(res instanceof Promise)
res.then(function (res) {
assert(Array.isArray(res))
assert(res[0] === a)
assert(res[1] === b)
assert(res[2] === c)
})
.nodeify(done)
})
})
describe('of mixed values', function () {
it('returns a promise for an array containing the fulfilled values', function (done) {
var res = Promise.all([A, b, C])
assert(res instanceof Promise)
res.then(function (res) {
assert(Array.isArray(res))
assert(res[0] === a)
assert(res[1] === b)
assert(res[2] === c)
})
.nodeify(done)
})
})
describe('containing at least one rejected promise', function () {
it('rejects the resulting promise', function (done) {
var res = Promise.all([A, rejected, C])
assert(res instanceof Promise)
res.then(function (res) {
throw new Error('Should be rejected')
},
function (err) {
assert(err === rejection)
})
.nodeify(done)
})
})
})
})
describe('promise.done(onFulfilled, onRejected)', function () {
it.skip('behaves like then except for not returning anything', function () {
//todo
})
it.skip('rethrows unhandled rejections', function () {
//todo
})
})
describe('promise.nodeify(callback)', function () {
it('converts a promise returning function into a callback function', function (done) {
function add(a, b, callback) {
return Promise.resolve(a)
.then(function (a) {
return a + b
})
.nodeify(callback)
}
add(1, 2, function (err, res) {
if (err) return done(err)
assert(res === 3)
return done()
})
})
it('converts rejected promises into the first argument of the callback', function (done) {
function add(a, b, callback) {
return Promise.resolve(a)
.then(function (a) {
throw sentinel
})
.nodeify(callback)
}
add(1, 2, function (err, res) {
assert(err === sentinel)
done()
})
})
it('passes through when no callback is provided', function (done) {
function add(a, b, callback) {
return Promise.resolve(a)
.then(function (a) {
return a + b
})
.nodeify(callback)
}
add(1, 2)
.then(function (res) {
assert(res === 3)
done()
})
})
})
})
promise-5.0.0/test/promises-tests.js 0000664 0000000 0000000 00000000151 12327251233 0017505 0 ustar 00root root 0000000 0000000 var tests = require('promises-aplus-tests');
var adapter = require('./adapter-a');
tests.mocha(adapter); promise-5.0.0/test/resolver-tests.js 0000664 0000000 0000000 00000012557 12327251233 0017522 0 ustar 00root root 0000000 0000000 var assert = require('better-assert');
var Promise = require('../');
var sentinel = {};
var promise = new Promise(function (resolve) {
resolve(sentinel);
});
var _it = it;
describe('resolver-tests', function () {
describe('The Promise Constructor', function () {
it('has `Object.getPrototypeOf(promise) === Promise.prototype`', function () {
assert(Object.getPrototypeOf(promise) === Promise.prototype)
})
it('has `promise.constructor === Promise`', function () {
assert(promise.constructor === Promise)
})
it('has `promise.constructor === Promise.prototype.constructor`', function () {
assert(promise.constructor === Promise.prototype.constructor)
})
it('has `Promise.length === 1`', function () {
assert(Promise.length === 1)
})
describe('if resolver is not a function', function () {
it('must throw a `TypeError`', function () {
try {
new Promise({})
} catch (ex) {
assert(ex instanceof TypeError)
return
}
throw new Error('Should have thrown a TypeError')
})
})
describe('if resolver is a function', function () {
it('must be called with the promise\'s resolver arguments', function () {
new Promise(function (resolve, reject) {
assert(typeof resolve === 'function')
assert(typeof reject === 'function')
done();
})
})
it('must be called immediately, before `Promise` returns', function () {
var called = false;
new Promise(function (resolve, reject) {
called = true;
})
assert(called)
})
})
describe('Calling resolve(x)', function () {
describe('if promise is resolved', function () {
it('nothing happens', function (done) {
var thenable = {then: function (onComplete) {
setTimeout(function () {
onComplete(sentinel)
}, 50)
}};
new Promise(function (resolve) {
process.nextTick(function () {
resolve(thenable)
resolve(null)
});
})
.then(function (result) {
assert(result === sentinel)
})
.then(function () {
done()
}, function (err) {
done(err || new Error('Promise rejected'));
})
})
})
describe('otherwise', function () {
describe('if x is a thenable', function () {
it('assimilates the thenable', function () {
})
})
describe('otherwise', function () {
it('is fulfilled with x as the fulfillment value', function (done) {
new Promise(function (resolve, reject) {
resolve(sentinel)
})
.then(function (fulfillmentValue) {
assert(fulfillmentValue === sentinel)
})
.then(function () {
done()
}, function (err) {
done(err || new Error('Promise rejected'));
})
})
})
})
})
describe('Calling reject(x)', function () {
describe('if promise is resolved', function () {
it('nothing happens', function (done) {
var thenable = {then: function (onComplete) {
setTimeout(function () {
onComplete(sentinel)
}, 50)
}};
new Promise(function (resolve, reject) {
process.nextTick(function () {
resolve(thenable)
reject('foo')
});
})
.then(function (result) {
assert(result === sentinel)
})
.then(function () {
done()
}, function (err) {
done(err || new Error('Promise rejected'));
})
})
})
describe('otherwise', function () {
it('is rejected with x as the rejection reason', function (done) {
new Promise(function (resolve, reject) {
reject(sentinel)
})
.then(null, function (rejectionReason) {
assert(rejectionReason === sentinel)
})
.then(function () {
done()
}, function (err) {
done(err || new Error('Promise rejected'));
})
})
})
})
})
describe('if resolver throws', function () {
describe('if promise is resolved', function () {
it('nothing happens', function (done) {
var thenable = {then: function (onComplete) {
setTimeout(function () {
onComplete(sentinel)
}, 50)
}};
new Promise(function (resolve, reject) {
resolve(thenable)
throw new Error('foo');
})
.then(function (result) {
assert(result === sentinel)
})
.then(function () {
done()
}, function (err) {
done(err || new Error('Promise rejected'));
})
})
})
describe('otherwise', function () {
it('is rejected with e as the rejection reason', function () {
new Promise(function (resolve, reject) {
throw sentinel
})
.then(null, function (rejectionReason) {
assert(rejectionReason === sentinel)
})
.then(function () {
done()
}, function (err) {
done(err || new Error('Promise rejected'));
})
})
})
})
})