bloodhound-promises
v1.1.5
Published
Promise implementation with built-in timing data.
Downloads
3
Maintainers
Readme
Bloodhound
Tracked Promises in JavaScript
Installation
To install for node:
npm install bloodhound-promises
Or the bower component:
bower install bloodhound-promises
Additional NPM Scripts
To run the Bloodhound-specific Jasmine unit tests:
npm test
To execute Bloodhound against the Promise/A+ specification test suite:
npm run-script verify
To generate JSDoc documentation in the build/dev/doc/bloodhound-promises folder:
npm run-script doc
Why Bloodhound?
Promises are fantastic. They encapsulate a long-running operation into a simple object that invokes callbacks based on the operation's success or failure.
But more than that, promises can be chained together into complex trees, where the root's success or failure will depend on the success or failure of all its children.
This tree-like structure accurately represents much of the real-world transactional logic of modern web applications.
Unfortunately, because these operations can overlap, current performance tracking libraries have no way to distinguish each operation -- they simply assume all transactions on a page should be grouped together. Or in a single-page application, they assume that everything in the current view should be timed together.
But modern single-page applications can have micro-content loading alongside primary content. The user can be in any part of an application -- like a slideout drawer showing user messages while the main area shows an edit-record view. Current tracking libraries have no way to handle this situation.
Enter Bloodhound.
How Does Bloodhound Work?
Bloodhound promises work just like regular promises (in fact, it fully implements the A+ spec), with a lot of the syntactic sugar that other promise implementations have popularized.
But unlike other promise libraries, Bloodhound also adds the following instance methods:
promise.trackAs(name [, passive]) : Promise
This marks the promise instance as a tracked promise, and then returns that instance for chaining. Optionally, you can say the promise is passively tracked. What that means will be explained below.
promise.done() : Promise
Not all promise libraries have a done method, but it's vital to using promises correctly. Basically, the golden rule of promises is:
If you don't return the promise, you must call done.
Calling done
is what tells Bloodhound that it should attempt to gather up any timing data
in the tree and persist it to any registered collectors. It also throws any unhandled
rejections / exceptions so you know if there is an error in your application. (Otherwise, your
app could end up in an inconsistent state.)
Let's look at an example that combines the two new methods:
function loadMessages() {
return new Promise(function(resolve, reject) {
// do real stuff here and
// call resolve(...) when done
}).trackAs('load messages', true);
}
function loadAppData() {
return new Promise(function(resolve, reject) {
// do real stuff here and
// call resolve(...) when done
}).trackAs('load app data', true);
}
Promise.all([
loadMessages(),
loadAppData()
]).trackAs('loading').done();
What does the code do? Both loadMessages
and loadAppData
return a promise that would
be resolved once their data calls completed. But before the promises are returned, they
are passively tracked with an appropriate name. Because the promise is being returned,
we do not call done()
-- after all, someone else will be consuming our promise, so we
can't throw any unhandled errors just yet.
WHAT DOES IT MEAN TO BE PASSIVELY TRACKED?
Basically, a passively tracked promise is just a promise that has been given the
specified name but will not be persisted to any registered collectors when `done`
is called...UNLESS it is part of a larger tree of promises that contains at
least one actively tracked promise and that tree has completely settled.
WHY USE PASSIVE TRACKING AT ALL?
Sometimes you want to control how your promise will appear in timing data but don't
actually want it persisted. For example, let's say you're routing all remote HTTP
calls through a custom data layer. Your data layer could return a promise that is
passively tracked using the remote URL as the name. That way, any reports you run
on the generated timing data show clearly what endpoint was taking the longest time
to run. But that timing data would not be logged EVERY time a remote call was made,
only when a call was made as part of a larger, actively tracked promise tree.
Finally, we wrap our example promises in a call to Promise.all
, a static method that
returns a promise which is only resolved if all child promises resolve. But whether that
promise resolves or reject, we actively track it as 'loading'. You can tell it's actively
tracked because we didn't specify true
for the optional passive
parameter of
trackAs
.
Then we call done()
, which waits until the promise is either resolved or rejected to
check for unhandled exceptions and also persist the timing data to any registered
collectors.
In this case, the timing data might look like the following:
{
"name": "loading",
"data": [
[
"sample",
"messages"
],
[
"sample",
"app",
"data"
]
],
"start": 1425943275662,
"stop": 1425943275786,
"duration": 124,
"children": [
{
"name": "load messages",
"data": [
"sample",
"messages"
],
"start": 1425943275661,
"stop": 1425943275716,
"duration": 55,
"children": []
},
{
"name": "load app data",
"data": [
"sample",
"app",
"data"
],
"start": 1425943275662,
"stop": 1425943275776,
"duration": 114,
"children": []
}
]
}
This is already excellent data -- we can quickly see that our application is taking a long time loading application data. Before, we would simply see that the initial load took 124 milliseconds; but figuring out why would be a lot more difficult.
If you look closely, you'll notice that the timing data for 'load messages'
and 'load app data' both start before the parent and end after. Why?
Because that's when they actually occurred. We kicked off those calls and
then passed those promises to Promise.all
.
Some people don't like seeing data like this; they prefer more consistent timing data, where parent promises always start on or before their children and always end on or after the last child settles. If this is needed for your particular situation, simply configure Bloodhound using the following command:
Promise.config.timing.useSaneTimings();
This adjusts timing data so parents start on or before their children and end on or after their last child settles. With sane timings enabled, the above timing data might look like the following:
{
"name": "loading",
"data": [
[
"sample",
"messages"
],
[
"sample",
"app",
"data"
]
],
"start": 1425943993069,
"stop": 1425943993193,
"duration": 124,
"children": [
{
"name": "load messages",
"data": [
"sample",
"messages"
],
"start": 1425943993069,
"stop": 1425943993129,
"duration": 60,
"children": []
},
{
"name": "load app data",
"data": [
"sample",
"app",
"data"
],
"start": 1425943993069,
"stop": 1425943993183,
"duration": 114,
"children": []
}
]
}
Configuration
Bloodhound provides a number of ways to configure timings, collectors, and how asynchronous operations are invoked.
Scheduling Asynchronous Operations
Promise.config.setScheduler(mySchedulerFunction)
Use the specified function to invoke an asynchronous operation. By default, Bloodhound
uses window.setTimeout
to execute an operation on the next tick of the clock.
In node environments, you may wish to use process.nextTick
.
In Angular environments, you may wish to use $rootScope.$digest.bind($rootScope)
to
ensure promise resolution occurs within the digest cycle.
Finally, if you're looking for the fastest possible async execution in modern browsers, you could set the scheduler to use the MutationObserver:
var i = 0,
scheduled,
node = document.createTextNode(''),
o = new MutationObserver(run);
o.observe(node, { characterData: true });
function run() {
var fn = scheduled;
scheduled = void 0;
fn();
}
Promise.config.setScheduler(function scheduler(fn) {
scheduled = fn;
node.data = (i ^= 1);
});
Pretty Stack Traces
In an effort to improve stack traces during errors, Bloodhound provides a few useful features and configuration options.
First, any named callback methods will be passively tracked automatically. Anonymous functions are not very helpful in stack traces, so by using named functions, you gain both better stack traces and better timing output.
Promise.resolve('abc').trackAs('parent')
.then(function myNamedCallback() { ... })
.done();
Here is the resulting promise tree:
parent (actively tracked)
└ myNamedCallback (passively tracked)
You can enable pretty stack traces using the following method:
Promise.config.prettyStacks.enable()
With pretty stacks enabled, code like this...
Promise.all([
Promise.delay(50, 'child-1').trackAs('child-1'),
Promise.delay(20, 'child-2').trackAs('child-2')
]).trackAs('parent').then(function callback(values) {
return new Promise(function(resolve) {
resolve('some value');
}).then(function resolved() {
throw new Error('inner');
});
}).done();
...will produce error output like this:
ERROR
at trackAs: parent
at function: callback
at constructor: Promise
at function: resolved
Error: inner
at inner (filename:<line>)
at parentSettled (Promise.js:<line>)
at push (Promise.js:<line>)
at ThenPromise (Promise.js:<line>)
at invoke (Promise.js:<line>)
at invoke (Promise.js:<line>)
at throwError (Promise.js:<line>)
at invoke (Promise.js:<line>)
at Promise.js:<line>
NOTE: For performance reasons, the default state of Bloodhound is to disable pretty stack traces. If you have enabled pretty stacks and wish to disable them again, simply call:
Promise.config.prettyStacks.disable()
Random Errors
Promise.config.setRandomErrorRate(Number)
Configures Bloodhound to randomly reject promises at the rate specified. You can pass a number between 0 and 100 or between 0 and 1. The number represents the percent of time a promise should be randomly rejected.
Promise.config.setRandomErrorRate(0); // never reject; the default
Promise.config.setRandomErrorRate(50); // reject half the time
Promise.config.setRandomErrorRate(100); // reject 100% of the time
These are equivalent to:
Promise.config.setRandomErrorRate(0); // never reject; the default
Promise.config.setRandomErrorRate(0.5); // reject half the time
Promise.config.setRandomErrorRate(1); // reject 100% of the time
Why Enable Random Errors? Unhandled promise rejections represent potential logical errors, unexpected network problems, and/or inconsistent application state. Identifying and fixing unhandled promise rejections ensures your application always recovers gracefully.
NOTE: The random error rate does not apply to Promise.resolve
or Promise.reject
.
These methods will always work as expected by resolving or rejecting with the specified
value or reason.
Timing Configuration
Promise.config.timing.enable()
Enables the persistence of timing data to any registered collectors. This is the default state of Bloodhound.
Promise.config.timing.disable()
Disables the persistence of timing data to registered collectors. You can re-enable
persistence by calling Promise.config.timing.enable()
.
Promise.config.timing.useSaneTimings()
Ensures parent promises are shown as starting on or before their child promises and ending on or after their last child promise settles.
Timing Collectors
Collectors are objects with a single method called collect
that accepts a timing data
object and decides what to do with it. For example, a collector for Google Analytics
may look like this:
var GACollector = {
collect: function(timingData) {
ga('send', 'timing', 'myApp', timingData.name, timingData.duration);
}
};
We could modify our GACollector to send a timing event for all our child timings, too, instead of just the root timing. Some collectors may also want us to persist start and stop times. All of this information is available through the timingData object, which has the following properties:
name {String} the tracked name of the promise, or 'anonymous'
data {*} either the resolved value or rejection reason
start {Number} when the promise was created, as the number of milliseconds since
midnight, January 1, 1970 UTC
stop {Number} when the promise was finally settled, as the number of milliseconds
since midnight, January 1, 1970 UTC
duration {Number} the difference between start and stop
children {Array} an array of child timings
To register or de-register collectors, use the following methods:
Promise.config.collectors.add(collector) : Function
Adds the specified collector to the list of registered collectors, and returns a function you can invoke to remove the collector.
var collector = {
collect: function(timingData) {
console.log(JSON.stringify(timingData, null, 2));
}
};
var remove = Promise.config.collectors.add(collector);
The above collector simply logs the timingData instance to the browser's console.
Promise.config.collectors.remove(collector)
Removes the specified collector from the list of registered collectors. This has the
same effect as invoking the function returned by Promise.config.collectors.add
.
Full API
Promise constructor
You create a new instance of a Bloodhound promise by calling the constructor and passing your 'resolver' function -- the method that will be run asynchronously and either resolve or reject the promise:
new Promise(function(resolve, reject, notify) {
// this method will be invoked asynchronously;
// when it completes, call resolve or reject;
// if it throws an exception, reject will be
// called automatically; if you want to notify
// any listeners of progress updates, call
// notify with any data you want your listeners
// to receive (such as a progress percentage)
});
Static Methods
Promise.all(Array) : Promise
Resolves if all child promises resolve; rejects if any child promise rejects.
Promise.all([
Promise.delay(50, 'hello'),
Promise.reject('world')
]).catch(function(reason) {
log(reason); // 'world'
}).done();
Promise.any(Array) : Promise
Resolves if any child promise resolves; rejects if all child promises reject.
Promise.any([
Promise.delay(50, 'hello'),
Promise.reject('world')
]).then(function(value) {
log(value); // 'hello'
}).done();
Promise.apply(Function*[, Array]*) : Promise
Similar to Promise.call
, but allows you to specify an optional array
of arguments.
function someMethod() {
var args = [].slice.call(arguments);
return args.reduce(function(result, arg) {
return result + arg;
}, 0);
}
Promise.apply(someMethod, [10, 20, 30, 40])
.then(function(result) {
log(result); // 100
}).done();
Promise.call(Function*[, arg1, arg2...]*) : Promise
Invokes the specified function with any optionally supplied arguments passed in as parameters, and returns a promise. The promise will be resolved with the return value of the function. If the function throws an exception, the promise will be rejected with the exception data.
function someMethod(a, b) {
return a + b;
}
Promise.call(someMethod, 10, 20)
.then(function(result) {
log(result); // 30
}).done();
Promise.cast(*) : Promise
Converts the specified value into a Bloodhound promise using the following rules:
- If the value is a Bloodhound promise, it will be returned unaltered.
- If the value is an Error, a promise will be returned that was immediately rejected with the Error data.
- If the value is a "thenable" -- like a promise from another library, the returned promise will be resolved or rejected when the value is.
- Otherwise, the returned promise will be immediately resolved with the given value.
Sample code:
Promise.cast(new Date()).then(function(now) {
log(now); // outputs the current date and time
}).done();
Promise.cast(new Error()).catch(function(err) {
log(err); // outputs the error instance
}).done();
Promise.cast($q.when(123)).then(function(value) {
log(value); // 123
}).done();
Promise.defer() : Object
DEPRECATED! This method is only provided for compatibility with any existing legacy promise code you may be using. You are encouraged instead to use the Promise constructor to return a promise.
Returns an object with properties and methods you can use to manage an asynchronous operation.
function doAsyncOperation() {
var defer = Promise.defer();
try {
window.setTimeout(function() {
// do some long-running
// operation, then resolve
// with the value you want
// to pass to handlers:
defer.resolve(...);
});
} catch (err) {
defer.reject(err);
}
return defer.promise;
}
doAsyncOperation().then(...).done();
The preferred approach is to use the Promise constructor:
function doAsyncOperation() {
return new Promise(function(resolve, reject) {
// do some long-running operation, then
// resolve with the value you want to
// pass to handlers; reject will be called
// automatically if your method throws an
// exception
resolve(...);
});
}
// how you use the result is the same:
doAsyncOperation().then(...).done();
Promise.delay(Number*[, ]) : Promise
Returns a promise that will be resolved with the specified value after the given number of milliseconds. If you provide an instance of an Error, the returned promise will be rejected when the given number of milliseconds have elapsed.
This is more of a utility method that you can use during development to simulate an asynchronous operation that results in a success or failure.
Promise.delay(100).then(function(value) {
log(value); // undefined
}).done();
Promise.delay(45, 'abc').then(function(value) {
log(value); // 'abc'
}).done()
Promise.delay(85, new Error()).catch(function(err) {
log(err); // Error
}).done();
Promise.hash(Object) : Promise
Returns a promise that will be resolved with an object. The object's keys will match the keys of the object passed in, and the object's values will represent the resolved values of the incoming object's promises, or the reasons it was rejected.
Promise.hash({
'key1': 'you can use normal values',
'key2': Promise.delay(30, 'and resolved values'),
'key3': Promise.reject('even rejections')
}).then(function(results) {
log(results.key1); // 'you can use normal values'
log(results.key2); // 'and resolved values'
log(results.key3); // 'even rejections'
}).done();
Promise.isPromise(*) : Boolean
Returns true
if the value is a Bloodhound promise or "thenable"
object; otherwise, returns false
.
log(Promise.isPromise(Q.when())); // true
log(Promise.isPromise(Promise.cast())); // true
log(Promise.isPromise(new Date())); // false
Promise.race(Array) : Promise
Resolves with the value of the first child to resolve. If no children resolve, the promise will be rejected.
Promise.race([
Promise.delay(50, 'hello'),
Promise.delay(20, 'world'),
Promise.reject('reason')
]).then(function(value) {
log(value); // 'world'
}).done();
Promise.settle(Array) : Promise
Resolves with an array of all child promises once they have been resolved or rejected. The resolved array will contain the values of any resolved child promises and the reasons for any rejected child promises.
Promise.settle([
Promise.delay(50, 'hello'),
Promise.delay(20, 'world'),
Promise.reject(new Error('reason'))
]).then(function(results) {
log(results); // ['hello', 'world', Error]
}).done();
Promise.some(Array, Number) : Promise
Resolves if the specified number of child promises resolve; rejects if enough promises reject that the specified count can't be reached.
Promise.some([
Promise.delay(50, 'hello'),
Promise.delay(100, 'world'),
Promise.reject('reason')
], 2).then(function(values) {
log(values); // ['hello', 'world']
}).done();
Instance Methods
promise.then([Function[, Function[, Function]]]) : Promise
Registers optional callbacks for success, failure, and notification, and returns a new promise. If your success or failure callback returns a value, it will become the new value of the returned promise. If either callback throws an exception the returned promise will be rejected with the error. If either callback returns a promise, the original promise will be resolved or rejected with the returned promise.
Promise.delay(20, (Math.random() * 10) % 2 === 0 ?
new Error() : 'hello').then(
function success(value) {
log(value); // 'hello'
},
function failure(err) {
log(err); // Error
}).done();
Promise.delay(10, 'a')
.then(function(value) {
log(value); // 'a'
return 'b';
}).then(function(value) {
log(value); // 'b'
return Promise.delay(30, 'c');
}).then(function(value) {
log(value); // 'c'
return Promise.reject('some reason');
}).catch(function(reason) {
log(reason); // 'some reason'
}).done();
promise.tap([Function]) : Promise
Registers a callback that will be invoked when the promise resolves. The callback will be provided with the resolved value, but anything you return from the callback will be ignored. If your callback throws an error, it will be ignored.
Promise.delay(50, 'hello')
.tap(function(value) {
log(value); // 'hello'
return 'world'; // ignored
}).then(function(value) {
log(value); // still 'hello'
return value + ' world'; // not ignored
}).then(function(value) {
log(value); // 'hello world'
return value;
}).tap(function(value) {
throw new Error('this is ignored');
}).then(function(value) {
log(value); // still 'hello world'
}).done();
promise.catch([Function]) : Promise
Registers a callback that will be invoked only if the promise is rejected, and returns a new promise that will be resolved or rejected based on the callback's behavior.
If the callback does not return anything, the returned promise will be resolved. If the callback returns a value, the returned promise will be resolved with that value. If the callback throws an exception or returns a rejected promise, the child promise will be rejected.
Promise.delay(50, new Error())
.catch(function(err) {
// by handling the error, the original
// promise will switch from rejected
// to resolve UNLESS we re-throw the
// error or return a rejected promise
throw err;
// or: return Promise.reject(err);
}).done();
Promise.delay(40, new Error())
.catch(function(err) {
// let's handle the error; if we
// do not return anything, then we
// effectively "swallowed" the
// rejection and converted this
// to a resolved promise; if we
// explicitly return a value, it
// will become the new resolved
// value for any chained handlers:
return 'new value';
}).then(function(value) {
log(value); // 'new value'
}).done();
The ability to swallow exceptions is just one reason why calling
done()
is so important at the end of a promise chain. It ensures
that any *un-*handled exceptions are rethrown so your application
won't end up in an inconsistent state.
Finally, there is a subtle but important difference between using
catch
and then
to register failure callbacks. Look at the
following 2 code samples:
var doStuff = function(resolve) {
// pretend stuff happens here
resolve(values);
},
success = function(results) {
// process results
},
failure = function() {
// log failure
}
return new Promise(doStuff).then(success, failure);
return new Promise(doStuff).then(success).catch(failure);
What is the difference in the last 2 lines? In the first line, we
attached the failure callback to the doStuff
method -- it will only
be invoked if that method throws an error or rejects.
But in the second line, we've attached the failure callback to
both doStuff
and success
-- now, if an error occurs in either
of those methods, failure
will be called. This may be the behavior
you want. Or you may want to handle errors in success
with special
logic:
return new Promise(doStuff)
.then(success, failure)
.catch(function() {
// success must have failed
});
promise.notified([Function]) : Promise
You can schedule a notification callback to be invoked whenever an update is announced. Long-running operations can take advantage of notification callbacks to present status data to the user (e.g. in the form of a progress bar).
new Promise(function(resolve, reject, update) {
$('#myBar).css({width: 0}).show();
// do long-running operation #1
update(20); // 20% done
// do long-running operation #2
update(45);
// do long-running operation #3
update(70);
// do long-running operation #4
update(100);
}).notified(function(percent) {
$('#myBar').css({width: percent});
}).finally(function() {
$('#myBar').hide();
});
The static methods hash
and settle
will call any registered
notification handlers automatically with the percent of promises
that have been settled at any point in time.
promise.finally([Function]) : Promise
Allows you to register a callback that will be invoked when the promise is settled, regardless of whether it was resolved or rejected.
Promise.delay(50, 'resolved')
.finally(function(value) {
log(value); // resolved
}).done();
Promise.delay(50, new Error())
.finally(function(value) {
log(value); // Error
}).done();
promise.done() : Promise
This is a very important method. The golden rule of promises is:
If you do not return the promise, you must call done.
Calling done()
is what throws any unhandled rejections up to
the browser, ensuring any errors in your application can be found
and handled correctly. Look at the following example:
Promise.resolve(new Date()).then(myHandler);
// because we don't call done here, what would
// happen if an exception occurred in the myHandler
// method? we would never know the error occurred
// because it would have been converted into a
// rejected promise!
Promise.resolve(new Date()).then(myHandler).done();
// now any unhandled rejections will be propagated
// up to the UI so we know a problem occurred
Calling done()
also persists timing data to your collectors.
Because promises can be chained together into complex trees,
there is no other way for Bloodhound to know that you are done
constructing the promise tree and that it is safe to persist.
function myLongRunningOperation() {
// because we are returning the promise,
// we DO NOT call done(); this ensures
// callers can incorporate this promise
// into their trees -- we have to rely
// on them calling done() at the correct
// time and place
return Promise.delay(2000, 'sample data');
}
Promise.all([
myLongRunningOperation(),
someOtherLongRunningOperation(),
anotherLongRunningOperation(),
...
]).trackAs('lots of operations').done();
// we call done() when it's finally safe to
// look for unhandled exceptions and to persist
// the timing data to collectors
promise.timeout(Number*[, String]*) : Promise
If the promise is not settled in the amount of time specified, automatically reject it. You can provide a custom rejection string, or use the default of 'timed out'.
Promise.delay(50).timeout(20); // rejects after 20ms with 'timed out'
Promise.delay(50).timeout(100); // does not time out
Promise.delay(50).timeout(20, 'too slow'); // rejects with 'too slow'
The original promise is returned from this method, so you can continue chaining handlers:
Promise.delay(50)
.timeout(Math.random() * 100)
.then(success, failure);
promise.spread([Function]) : Promise
If the promise is resolved with an array, this method will invoke the specified callback with each array value passed in as arguments.
Promise.all([
Promise.delay(40, 'a'),
Promise.delay(10, 'b'),
Promise.delay(20, 'c')
]).spread(function(a, b, c) {
log(a, b, c); // 'a', 'b', 'c'
}).done();
This can be a useful alternative to nesting promises. Without all
,
you would've had to write code like this:
Promise.delay(40, 'a').then(function(a) {
Promise.delay(10, 'b').then(function(b) {
Promise.delay(20, 'c').then(function(c) {
log(a, b, c); // 'a', 'b', 'c'
}).done();
}).done();
}).done();
promise.trackAs(String*[, Boolean]*) : Promise
Registers a tracking name with the promise. If the promise timing data is persisted to collectors, it will include the specified name.
The second, optional parameter, determines if the promise should be passively tracked. Passively tracked promises will not be persisted to collectors unless they are part of a larger promise tree that contains at least one actively tracked promise.
Actively tracked promises are only persisted if done()
is called.
Promise.all([
Promise.delay(100, 'a').trackAs('child-1'),
Promise.delay(200, 'b').trackAs('child-2'),
Promise.delay(150, 'c').trackAs('child-3')
]).trackAs('parent').done();
The above tree might produce timing data like this:
{
"name": "parent",
"data": [
'a',
'b',
'c'
],
"start": 1425943275661,
"stop": 1425943275786,
"duration": 225,
"children": [
{
"name": "child-1",
"data": 'a',
"start": 1425943275661,
"stop": 1425943275766,
"duration": 105,
"children": []
},
{
"name": "child-2",
"data": 'b',
"start": 1425943275661,
"stop": 1425943275871,
"duration": 210,
"children": []
},
{
"name": "child-3",
"data": 'c',
"start": 1425943275661,
"stop": 1425943275816,
"duration": 155,
"children": []
}
]
}
promise.isRejected() : Boolean
Returns true
if the promise has been rejected. If the promise
is resolved or has not yet been settled, it will return false
.
Promise.reject('reason').isRejected(); // true
// but this returns false, because the promise has not
// yet been rejected:
Promise.delay(50, new Error()).isRejected();
promise.isResolved() : Boolean
Returns true
if the promise has been resolved. If the promise
is rejected or not yet settled, returns false
.
promise.isSettled() : Boolean
Returns true
if the promise has either been resolved or
rejected. If the promise is still pending, returns false
.
Copyright
Copyright (c) 2015 Dan Barnes
The MIT License
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.