laissez-faire
Advanced tools
Comparing version 0.7.2 to 0.8.0
{ | ||
"name": "laissez-faire", | ||
"repo": "jkroso/Laissez-faire", | ||
"description": "A Promises/A implementation i.e a \"thenable\"", | ||
"version": "0.7.2", | ||
"description": "A promise class", | ||
"version": "0.8.0", | ||
"keywords": ["promise", "async", "future", "control", "flow"], | ||
@@ -7,0 +7,0 @@ "dependencies": {}, |
163
docs/head.md
@@ -5,3 +5,3 @@ # Laissez-faire | ||
It works in [node](./src/index.js) and the [browser](./browser/Promise.js). Anything in the name? Yea its a French word meaning "let it be". Seemed apt for a library designed to eliminate the need to worry about when computation occurs. | ||
It works in [node](./src/index.js) and the [browser](./browser/Promise.js). Anything in the name? Yea its a French word meaning "let it be". Seemed apt for a library designed to reduce the need to worry. | ||
@@ -12,42 +12,14 @@ ## Differences from other libraries | ||
1. Doesn't guarantee async calls to `then`; you use promises to abstract away the effects of time. Therefore, there shouldn't be any need to guarantee anything about when promises will be fulfilled. The reasoning behind this feature is usually that it makes promises easier to understand for beginners. I think its more like this feature makes it easier for users to misunderstand promises and treat them like a less ugly version of the callback pattern found in Node. | ||
1. Doesn't guarantee async calls to `then` | ||
You use promises to abstract away the effects of time. Therefore, there shouldn't be any need to guarantee anything about when promises will be fulfilled. The reasoning behind this feature is usually that it makes promises easier to understand for beginners. I think its more like this feature makes it easier for users to misunderstand promises and treat them like a less ugly version of the callback pattern found in Node. | ||
2. Paranoia is optional; a goblin proof proxy object is not created when you create a new instance of laissez. If you want to be able to pass out access to your promises value without allowing them to also assign a value to your promise you call `promise.proxy()` A new proxy is generated with each call so you can pass out as many as you like. These are robust proxies unlike some implementations which don't freeze there proxy objects and therefore are susceptible to being decorated by a goblin. A decorate-able proxy is just as insecure as no proxy at all (implementations which don't freeze their proxies do so for performance). | ||
2. Paranoia is optional | ||
A goblin proof proxy object is not created when you create a new instance of laissez. If you want to be able to pass out access to your promises value without allowing them to also assign a value to your promise you call `promise.proxy()` A new proxy is generated with each call so you can pass out as many as you like. These are robust proxies unlike some implementations which don't freeze there proxy objects and therefore are susceptible to being decorated by a goblin. A decorate-able proxy is just as insecure as no proxy at all (these implementations don't freeze their proxies for performance reasons). | ||
3. API: | ||
1. `new Laissez` instead of `lib.defer()` | ||
2. `deferred.proxy()` instead of `deferred.promise` | ||
The test suit is comprised mainly of the [promises test suite](https://github.com/domenic/promise-tests) written by Domenic Denicola. I leave out the always-async test since I disagree with the concept. | ||
The test suit is comprised mainly of the [promises test suite](https://github.com/domenic/promise-tests) written by Domenic Denicola. I leave out the always-async test since I disagree with the concept. I have added a few extra suites for Laissez-faire specific features. | ||
## Performance | ||
[Good](https://github.com/cujojs/promise-perf-tests) it seems. | ||
[Good](https://github.com/jkroso/promise-perf-tests) it seems. | ||
``` | ||
==================================================== | ||
Test: promise-sequence (10,000 iterations) | ||
---------------------------------------------------- | ||
Name Time ms ns/run Diff % | ||
laissez 14 1436 - | ||
when 18 1819 27 | ||
deferred 25 2524 76 | ||
avow 103 10278 616 | ||
rsvp 235 23455 1533 | ||
jquery 382 38244 2563 | ||
q 718 71794 4900 | ||
==================================================== | ||
Test: promise-create (10,000 iterations) | ||
---------------------------------------------------- | ||
Name Time ms ns/run Diff % | ||
laissez 0 47 - | ||
avow 3 329 601 | ||
when 30 3006 6303 | ||
deferred 30 3026 6347 | ||
rsvp 35 3503 7362 | ||
q 104 10393 22042 | ||
jquery 265 26545 56455 | ||
``` | ||
### What makes it better | ||
@@ -57,25 +29,16 @@ | ||
2. Faster | ||
3. Easier to debug (uncaught errors are logged) | ||
3. Easier to debug (uncaught errors can be logged) | ||
4. Flexible API | ||
### What makes it worse | ||
1. I haven't got around to doing much interope testing so will most likely have some bugs if you are passing it promises from other libraries. | ||
## Basic Usage | ||
```javascript | ||
var promise = new Promise(); | ||
var promise = new Promise | ||
promise.then(function(value) { | ||
// success | ||
}, function(value) { | ||
// failure | ||
}); | ||
promise.then(console.log, console.warn) | ||
// later... | ||
promise.resolve(value) // triggers first callback | ||
// at any point from now on calling... | ||
promise.resolve(value) // ...triggers console.log | ||
// or... | ||
promise.reject(error) // triggers second callback | ||
promise.reject(error) // ...triggers console.warn | ||
``` | ||
@@ -89,6 +52,6 @@ | ||
```javascript | ||
var getJSON = function(url) { | ||
var promise = new Promise() | ||
function getJSON (url) { | ||
var promise = new Promise | ||
var client = new XMLHttpRequest() | ||
var client = new XMLHttpRequest | ||
client.open("GET", url) | ||
@@ -101,18 +64,13 @@ client.onreadystatechange = handler | ||
function handler() { | ||
if (this.readyState === this.DONE) { | ||
if (this.status === 200) promise.resolve(this.response) | ||
else promise.reject(this) | ||
} | ||
}; | ||
if (this.readyState === this.DONE) | ||
if (this.status === 200) | ||
promise.resolve(this.response) | ||
else | ||
promise.reject(this) | ||
} | ||
return promise; | ||
// or if you you think there might be goblins in your system | ||
// return promise.proxy() | ||
}; | ||
return promise | ||
} | ||
getJSON("/posts.json").then(function(json) { | ||
// continue | ||
}, function(error) { | ||
// handle errors | ||
}); | ||
getJSON("/posts.json").then(console.log, console.warn) | ||
``` | ||
@@ -122,20 +80,26 @@ | ||
I struggled to understand promises and that is why I started playing with this project. It would be rude of me not to share my learnings. I hope my explanation prevents you from needing to take the same route in order to understand them. Every explanation I have read begins like this: "A promise represents the eventual value returned from the single completion of an operation". I have read a few and they all sounded suspiciously wrote. That one was from the Promises/A spec. Its a shame they all choose to use the word "eventual" since it is actually slightly misleading. Promises may be resolved now. Therefore, a simpler and more accurate way of introducing promises would be to say "A promise represents the value of a computation". | ||
This of course would raise the question: isn't that what variables are for? Yes. Except variables break if the computation occurs at any time other than right now. The problem promises are designed to solve is uncertainty due to the timing of processes. Promises take the worry away. They add some messy syntax but allow you to focus on the process which will ultimately makes things easier to reason about. In order to do this they need to do two things. | ||
I struggled to understand promises and that is why I started playing with this project. It would be rude of me not to share my learnings. I hope my explanation prevents you from needing to take the same route in order to understand them. Every explanation I have read begins like this: | ||
1. Store the result of the computation as soon as it happens | ||
2. Provide a mechanism to connect further computations which depend on this value | ||
> A promise represents the eventual value returned from the single completion of an operation | ||
By providing these capabilities they allow you to ignore the timing of computation completely. They could easily become a language feature complimenting the less powerful variable. | ||
You saw promises in use in the previous section but lets walk through how they are used since they look nothing like variables syntactically. First we create the promise: `var promise = new Promise`. So we need to use a variable in order to use a promise. You could say then that we are creating an enhanced variable or more simply a container. Then we do some computation and assign our value to the promise `return promise.assign('some value')`. Note you must always `return` the promise now though you don't have to assign to it until you are good and ready. So your functions must know how to manage a promise, its a shame since semantically they aren't much different from variables, though blame that one on the language designers. Maybe one day JavaScript will be inspired and adopt message passing like Io and Smalltalk, maybe. | ||
I have read a few and they all sounded suspiciously wrote. That one was from the Promises/A spec. Its a shame they all choose to use the word "eventual" since it is actually slightly misleading. Promises may be resolved now. Therefore, a simpler and more accurate way of introducing promises would be to say "A promise represents a value". | ||
This of course would raise the question: isn't that what variables are for? Yes. Except variables break if the computation occurs at any time other than right now. The problem promises are designed to solve is uncertainty due to the timing of processes. Promises take the worry away and allow you to focus on the process which ultimately makes things easier to reason about. In order to do this they need be able to do two things. | ||
In summary promises are a way of storing and accessing values devoid of when. That makes them very useful for situations where you can't be certain something is going to be sitting in memory ready to go but would like to take advantage of the speed if it is. Callbacks enable this and will always be faster, however promises provide the value and error propagation infrastructure to make these programs easier to reason about. Plus implementations like Laissez-faire are actually fairly light weight so offer good bang for your buck. The time they save you coding can be spent sleeping at night. | ||
1. Store a value | ||
2. Provide a mechanism to register interest in this value | ||
The complete explanation of promises I wish I had read then would look like this: | ||
> promises are variables which are useful even when they don't yet hold their value | ||
It also didn't help that every implementation I saw was very complicated. RSVP was the first implementation I saw that made it apparent what exactly promise implementations do. Thanks to those guys for keeping it simple. This implementation just has a little more mechanical sympathy features. Hopefully, its still a good learning resource too :) | ||
## What promises are not | ||
After writing this I realized promises could easily be mistaken for lazy evaluation. Lazy evaluation is a strategy to delay computation until results are actually requested. This is a good strategy to use when operating over collections since often you end up only using a small amount of the collection to get your result. It prevents you from concerning yourself with efficiency and allows you to program more declaratively. Meanwhile promises don't actively delay computation (at least they shouldn't) they simply provision for the scenario that its is delayed (due to io or network latency). Using promises still results in a proactive computation strategy. Meaning functions are run as soon as possible, but no sooner. In contrasting that statement with lazy evaluation you could say that lazy evaluation delays computation as long as possible, but no longer. | ||
<!-- After writing the above I realized promises could easily be mistaken for lazy evaluation. Lazy evaluation is a strategy to delay computation until results are actually requested. This is a good strategy to use when operating over collections since often you end up only using a small amount of the collection to get your result. It prevents you from concerning yourself with efficiency and allows you to program more declaratively. Meanwhile promises don't actively delay computation (at least they shouldn't) they simply provision for the scenario that it might be delayed (due to io or network latency etc..). Using promises still results in a proactive computation strategy. Meaning functions are run as soon as possible, but no sooner. In contrasting that statement with lazy evaluation you could say that lazy evaluation delays computation as long as possible, but no longer. --> | ||
<abbr title="Continuation passing style">CPS</abbr> plus the ability to handle several callbacks. They represent values properly, which makes them far more powerful. | ||
## The benefit of promises | ||
They allow you to compose the results of external computation with those of internal. By external I mean that which occurs anywhere from another process to another computer on the other side of the Internet. By internal computation I mean anything that can be done immediately. In JavaScript we consume a lot of external computations, therefore, promises should be and will become staple. They are the only tool I am aware off that properly abstracts the problems caused by distributed computation. | ||
They allow you to compose the results of external computation with those of internal. By external I mean that which occurs outside the current process. By internal computation I mean anything that can be done immediately. In JavaScript we consume a lot of external computations, therefore, promises should be staple. Unfortunatly though most API's are designed around callbacks. Which is fine, until you start building something interesting. i.e when you start composing programs together. With callbacks you have to contort your mind, with promises you just have to contort your eyes. | ||
@@ -218,4 +182,3 @@ <!-- Comparison: | ||
If you return a regular value, it will be passed, as is, to the next | ||
handler. | ||
Promises don't just provide a way to represent a value. They also provide a way to represent transformations on that value. They offer this mechanism through the `then` method. Which takes a callback (to do the transformation) and returns a promise for the result of that callbacks operation. | ||
@@ -231,5 +194,4 @@ ```javascript | ||
``` | ||
If the callback you give it results in another promise, no worries. It'll recognise it as a promise, even if its from another implementation, and wait for its value before resolving the new promise to it. | ||
Returned promises are recognized as promises and have their resolved value used to resolve the promise they were returned to | ||
```javascript | ||
@@ -247,20 +209,29 @@ getJSON("/post/1.json") | ||
## Error Handling | ||
Errors also propagate: | ||
Errors are represented by promises in the same way as values. But you have to provide seperate handlers for these so called "rejected" promises. To handle a rejected promise you pass a function as a second argument to then. The call to `then` will result in a new promise which resolve to the return value of the error handling function. | ||
```javascript | ||
```js | ||
getJSON("/badurl.json") | ||
.then(function(posts) { | ||
.then(function(posts) { | ||
}) | ||
.then(null, function(error) { | ||
// even though no error callback was passed to the | ||
// first `.then`, the error propagates | ||
}) | ||
``` | ||
Laissez-faire provides a nicer althernative if you prefer. | ||
```js | ||
getJSON("/badurl.json").catch(function(error){ | ||
// handle here | ||
}) | ||
.then(null, function(error) { | ||
// even though no error callback was passed to the | ||
// first `.then`, the error propagates | ||
}) | ||
``` | ||
### What happens if you don't handle the error? | ||
Technically a promise can never throw an error since at any time in the future a user could add a child promise and handle the exception. However in practice the promise user will bind their error handlers immediately, so in most cases unhandled rejections which sit around for more than one tick of the event loop are genuine errors. Laissez-faire provides a mechanism for you to hook into these errors. See the source for details. It actually uses this mechanism itself to provide a nice default behavior which is to log the errors to the console after a 1 second delay. If this doesn't suit you then it is easy to override the behavior. | ||
Well technically a promise can never throw an error since at any time in the future a user could decide to handle the exception. However, in practice if you didn't provide an error handler immediatly you probably weren't expecting to get an error. Laissez-faire provides a mechanism for you to hook into these errors. It actually uses this mechanism itself to provide a nice default behavior which is wait 1 second then throw the error. If this doesn't suit you then it is easy to override or void the behaviour. Alternatively Laissez-faire also provides a `throw` method designed to be called at the end of the promise chain. It will cause any uncaught errors to be immediatly thrown. | ||
## Finishing with a promise | ||
If you have a promise and you only need to access it value; that is to say you won't be returning a value from the next operation, then you have no need for the promise normally generated by the call to `then`. This is the situation where a callback as commonly seen in node.js would be sufficient for you needs. In fact this is a very common scenario. It must be since a lot of people get by with callbacks as their only tool dealing with async operations. Laissez-faire provides a method for this scenario. `end`. Using this method creates a new processing branch; as with `then`, except you are saying this is the last thing I am doing over here. Laissez-faire knows it is safe to throw errors then since you can't of intended for them. Furthermore, since you don't need a new promise `end` returns the old one. This is a nice API feature as you will see in the following example since it allows you to form more fluent chains. | ||
## Simpler value access | ||
If you have a promise and you only need to access it value; that is to say you won't be returning a value from the next operation, then you have no need for the promise normally generated by the call to `then`. This is the situation where a callback as commonly seen in node.js would be sufficient for you needs. In fact this is a very common scenario. It must be since a lot of people get by with callbacks as their only tool for dealing with async operations. In these situations you can call to `end` instead of then. Using this method creates a new branch from the promise its called on, as with `then`, except instead of returning a new promise it simply returns the promise it is called on. When you use `end` Laissez-faire knows it is safe to throw errors since you couldn't handle the error in the future even if you wanted to since their is no promise to represent it. Furthermore, this is a nice API feature as you will see in the following example since it allows you to form more fluent chains. | ||
```javascript | ||
@@ -296,9 +267,15 @@ new Promise().assign(1) | ||
Each box represents a process of some kind. The circles below some of the processes represent the values they expose. The first box creates the promise and exposes itself for binding. The three calls to `end` each create a process but don't create new promises. Therefore, any values they might generate are lost. The fourth call, to `then`, does the same thing as the others except it exposes a value to the outside world. This value is represented by a promise, therefore to access it we must use either `then` or `end`. In this case we used `then` which resulting in the final value being exposed. This final value is 2. The flow of data is represented by the arrows with the red boxes showing the value of the data. Hopefully this illustration makes it obvious what is happing when you access the value of a promise via `then` or `end`, you are creating a branch in the process. Values flow down these branches in the same way as errors. Though they are of course handled by separate functions. Because we chose to use `then` as the final call on the fourth branch we will be able to continue processing at any time where we left of. Be that with an error or a value, no matter, so long as the `p` variable doesn't go out of scope. | ||
In summary then we can say promises provide a mechanism to freeze process. Ready to defrost and continue via `then` and `end` as soon or as long as you like. | ||
Each box represents a process of some kind. The circles below some of the processes represent the values they expose. The first box creates the promise and exposes itself for binding. The three calls to `end` each create a process but don't create new promises. Therefore, any values they might generate are lost. And any errors thrown are thrown. The fourth call, to `then`, does the same thing as the others except it exposes a value to the outside world. This value is represented by a promise, therefore to access it we must use either `then` or `end`. In this case we used `then` which resulting in the final value being exposed. This final value is 2. The flow of data is represented by the arrows with the red boxes showing the value of the data. Hopefully this illustration makes it obvious what is happing when you access the value of a promise via `then` or `end`, you are creating a branch in the process. Values flow down these branches in the same way as errors. Though they are of course handled by separate functions. Because we chose to use `then` as the final call on the fourth branch we will be able to continue processing at any time where we left of. Be that with an error or a value. | ||
In summary then we can say promises provide a mechanism to declare dependencies on the values that must be calculated before it. | ||
## Mutability | ||
Its widely considered that promises should be immutable and I agree. I though its interesting to note what happens to promises when they are implemented in a mutable way. So when a promise has its value changed it propagates this value to any child promises. At which point you would of switched to the [Reactive programming](http://en.wikipedia.org/wiki/Reactive_programming) paradigm. The change in implementation to create this effect is trivial. I guess we can say then that promises borderline bring a new paradigm to JavaScript. Its amazing how flexible this language is. | ||
Its widely considered that promises should be immutable and I agree. I though its interesting to note what happens to promises when they are implemented in a mutable way. So when a promise has its value changed it propagates this value through the branches we just discussed. At which point you would of switched to the [Reactive programming](http://en.wikipedia.org/wiki/Reactive_programming) paradigm. The change in implementation to create this effect is trivial. I guess we can say then that promises borderline bring a new paradigm to JavaScript!. | ||
<!-- | ||
You saw promises in use in the previous section but lets walk through how they are used since they look nothing like variables syntactically. First we create the promise: `var promise = new Promise`. So we need to use a variable in order to use a promise. You could say then that we are creating an enhanced variable or more simply a container. Then we do some computation and assign our value to the promise `return promise.assign('some value')`. Note you must always `return` the promise now though you don't have to assign to it until you are good and ready. So your functions must know how to manage a promise, its a shame since semantically they aren't much different from variables, though blame that one on the language designers. Maybe one day JavaScript will be inspired and adopt message passing like Io and Smalltalk, maybe. | ||
In summary promises are a way of storing and accessing values devoid of when. That makes them very useful for situations where you can't be certain something is going to be sitting in memory ready to go but would like to take advantage of the speed if it is. Callbacks enable this and will always be faster, however promises provide the value and error propagation infrastructure to make these programs easier to reason about. Plus implementations like Laissez-faire are actually fairly light weight so offer good bang for your buck. The time they save you coding can be spent sleeping at night. | ||
--> | ||
## API | ||
{ | ||
"name": "laissez-faire", | ||
"version": "0.7.2", | ||
"version": "0.8.0", | ||
"description": "A promise implementation. Simple and fast", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
282
src/index.js
@@ -10,60 +10,11 @@ exports = module.exports = Promise | ||
/** | ||
* Process the value safely and ensure that what is returned is represented via a new promise | ||
* | ||
* @param {Function|null} fn A handler from the current promise | ||
* @param {Any} value The result from computing the previous promise | ||
* @api private | ||
*/ | ||
function run (fn, value) { | ||
try { fn = fn(value) } | ||
catch (e) {return newRejected(e) } | ||
var proto = Promise.prototype | ||
if (isPromise(fn)) { | ||
// No need to create a new promise if we already have one | ||
return fn === value ? fn.then() : fn | ||
} else { | ||
return newFulfilled(fn) | ||
} | ||
} | ||
/** | ||
* Handle the processing of a promise and resolve or reject it based on the results | ||
* | ||
* @param {Promise} promise The promise we are computing the value for | ||
* @param {Function} fn either the success or error handler | ||
* @param {Any} value The input | ||
* @api private | ||
/*! | ||
* Default values | ||
*/ | ||
function propagate (promise, fn, value) { | ||
try { | ||
value = fn(value) | ||
} catch (e) { | ||
return promise.reject(e) | ||
} | ||
if (isPromise(value)) { | ||
// prefer .end() | ||
value[typeof value.end === 'function' ? 'end': 'then']( | ||
function (val) {promise.resolve(val)}, | ||
function (err) {promise.reject(err) }) | ||
} | ||
else { | ||
promise.resolve(value) | ||
} | ||
} | ||
proto.value = proto.reason = undefined | ||
proto.state = 'pending' | ||
/** | ||
* Ducktype test if an object is a promise | ||
* @return {Boolean} | ||
*/ | ||
exports.is = isPromise | ||
function isPromise (item) { | ||
return item && typeof item.then === 'function' | ||
} | ||
var proto = Promise.prototype | ||
// Default states. | ||
proto.value = proto.reason = null | ||
/** | ||
* Create a follow up process | ||
@@ -82,23 +33,38 @@ * | ||
* @param {Function} fail | ||
* @return {Promise} This is a new promise which will be either fulfilled | ||
* unless the previous promise fails to handle an error | ||
* @return {Promise} | ||
*/ | ||
proto.then = function(done, fail) { | ||
var thenPromise = this[this._len++] = new Promise | ||
thenPromise._success = done | ||
thenPromise._fail = fail | ||
return thenPromise | ||
switch (this.state) { | ||
case 'pending': | ||
var thenPromise = this[this._len++] = new Promise | ||
thenPromise._success = done | ||
thenPromise._fail = fail | ||
return thenPromise | ||
case 'resolved': | ||
return done | ||
? run(done, this.value) | ||
: newFulfilled(this.value) | ||
default: | ||
exports.onCatch && exports.onCatch(this) | ||
return fail | ||
? run(fail, this.reason) | ||
: newRejected(this.reason) | ||
} | ||
} | ||
// Alternative then methods | ||
function resolvedThen (fn) { | ||
return fn | ||
? run(fn, this.value) | ||
: newFulfilled(this.value) | ||
/** | ||
* Process the value safely and ensure that what is returned is represented via a new promise | ||
* | ||
* @param {Function} fn the handler to apply | ||
* @param {Any} value | ||
* @api private | ||
*/ | ||
function run (fn, value) { | ||
try { fn = fn(value) } | ||
catch (e) {return newRejected(e) } | ||
// No need to create a new promise if we already have one | ||
if (isPromise(fn)) return fn === value ? fn.then() : fn | ||
return newFulfilled(fn) | ||
} | ||
function rejectedThen (_, fn) { | ||
exports.onCatch(this) | ||
return fn | ||
? run(fn, this.reason) | ||
: newRejected(this.reason) | ||
} | ||
@@ -126,32 +92,27 @@ /** | ||
* @param {Function} fail | ||
* @return {Self} p.end() === p; Allowing jquery style use of promises | ||
* @return {Self} | ||
*/ | ||
proto.stub = | ||
proto.throw = | ||
proto.end = function (done, fail) { | ||
// Queue a sudo-promise capable of throwing errors | ||
this[this._len++] = { | ||
// Handlers are bound to the assignment properties since these aren't run inside a try catch. Having no handlers is fine, the values will just pass straight through to the resolvers. | ||
resolve: done || noop, | ||
// Interestingly if we didn't provide an error thrower as a backup we would still be guaranteed to throw at the right time. However it would be "no such method" instead of the users error. | ||
reject: fail || function (e) {throw e} | ||
switch (this.state) { | ||
case 'pending': | ||
this[this._len++] = { | ||
// Handlers are bound to the assignment properties since these aren't run inside a try catch. Having no handlers is fine, the values will just pass straight through to the resolvers. | ||
resolve: done || noop, | ||
// Interestingly if we didn't provide an error thrower as a backup we would still be guaranteed to throw at the right time. However it would be "no such method" instead of the users error. | ||
reject: fail || function (e) {throw e} | ||
} | ||
break | ||
case 'resolved': | ||
done && done(this.value) | ||
break | ||
default: | ||
exports.onCatch && exports.onCatch(this) | ||
if (!fail) throw this.reason | ||
fail(this.reason) | ||
} | ||
return this | ||
} | ||
/** | ||
* @alias end | ||
*/ | ||
proto.stub = | ||
proto.throw = function (done, fail) {return this.end(done, fail)} | ||
// Alternative `end` methods for when the state of the promise changes | ||
function resolvedEnd (fn) { | ||
fn && fn(this.value) | ||
return this | ||
} | ||
function rejectedEnd (_, fn) { | ||
exports.onCatch(this) | ||
if (!fn) throw this.reason | ||
fn(this.reason) | ||
return this | ||
} | ||
/** | ||
@@ -169,16 +130,16 @@ * Give the promise it's value and propogate it to its pending children | ||
*/ | ||
proto.fulfill = | ||
proto.resolve = function (value) { | ||
// Change the state | ||
this.value = value | ||
// Change the behavior. In a parallel universe this is just a matter of swapping the prototype. | ||
this.resolve = this.reject = noop | ||
this.then = resolvedThen | ||
this.end = resolvedEnd | ||
// Propagate the value to any queued promises. | ||
var child, i = 0 | ||
while (child = this[i++]) { | ||
if (child._success) propagate(child, child._success, value) | ||
else child.resolve(value) | ||
if (this.state === 'pending') { | ||
// Change the state | ||
this.value = value | ||
this.state = 'resolved' | ||
// Propagate the value to any queued promises. | ||
var child, i = 0 | ||
while (child = this[i++]) { | ||
if (child._success) | ||
propagate(child, child._success, value) | ||
else | ||
child.resolve(value) | ||
} | ||
} | ||
@@ -196,21 +157,44 @@ return this | ||
*/ | ||
proto.break = | ||
proto.reject = function (e) { | ||
this.reason = e | ||
if (this.state === 'pending') { | ||
this.state = 'rejected' | ||
this.reason = e | ||
this.resolve = this.reject = noop | ||
this.then = rejectedThen | ||
this.end = rejectedEnd | ||
var i = 0 | ||
, child = this[i] | ||
if (child) { | ||
do { | ||
if (child._fail) propagate(child, child._fail, e) | ||
else child.reject(e) | ||
} while (child = this[++i]) | ||
var i = 0 | ||
, child = this[i] | ||
if (child) { | ||
do { | ||
if (child._fail) propagate(child, child._fail, e) | ||
else child.reject(e) | ||
} while (child = this[++i]) | ||
} | ||
else { | ||
exports.onError && exports.onError(this, e) | ||
} | ||
} | ||
return this | ||
} | ||
/** | ||
* Handle the processing of a promise and resolve or reject it based on the results | ||
* | ||
* @param {Promise} promise The promise we are computing the value for | ||
* @param {Function} fn either the success or error handler | ||
* @param {Any} value The input | ||
* @api private | ||
*/ | ||
function propagate (promise, fn, value) { | ||
try { value = fn(value) } | ||
catch (e) { return promise.reject(e) } | ||
if (isPromise(value)) { | ||
// prefer .end() for speed | ||
value[value instanceof Promise ? 'end': 'then']( | ||
function (val) {promise.resolve(val)}, | ||
function (err) {promise.reject(err) }) | ||
} | ||
else { | ||
exports.onError && exports.onError(this, e) | ||
promise.resolve(value) | ||
} | ||
return this | ||
} | ||
@@ -254,5 +238,5 @@ | ||
if (done || fail) return this.end(done, fail) | ||
switch (this.then) { | ||
case resolvedThen: return this.value | ||
case rejectedThen: return this.reason | ||
switch (this.state) { | ||
case 'resolved': return this.value | ||
case 'rejected': return this.reason | ||
default: return this | ||
@@ -267,3 +251,3 @@ } | ||
*/ | ||
proto.catch = | ||
proto.catch = | ||
proto.otherwise = function (errback) { | ||
@@ -327,5 +311,4 @@ return this.then(null, errback) | ||
proto.cancel = function () { | ||
while (this._len--) { | ||
while (this._len--) | ||
delete this[this._len] | ||
} | ||
return this | ||
@@ -356,3 +339,3 @@ } | ||
proto.isRejected = function () { | ||
return this.then === rejectedThen | ||
return this.state === 'rejected' | ||
} | ||
@@ -365,3 +348,3 @@ | ||
proto.isFulfilled = function () { | ||
return this.then === resolvedThen | ||
return this.state === 'resolved' | ||
} | ||
@@ -387,2 +370,11 @@ | ||
/** | ||
* Ducktype test if an object is a promise | ||
* @return {Boolean} | ||
*/ | ||
exports.is = isPromise | ||
function isPromise (item) { | ||
return item && typeof item.then === 'function' | ||
} | ||
/** | ||
* Helper to quickly create a rejected promise | ||
@@ -392,2 +384,3 @@ * @param {Error} e | ||
*/ | ||
exports.broken = | ||
exports.rejected = newRejected | ||
@@ -397,4 +390,3 @@ function newRejected (e) { | ||
p.reason = e | ||
p.then = rejectedThen | ||
p.end = rejectedEnd | ||
p.state = 'rejected' | ||
return p | ||
@@ -408,2 +400,3 @@ } | ||
*/ | ||
exports.resolved = | ||
exports.fulfilled = newFulfilled | ||
@@ -413,4 +406,3 @@ function newFulfilled (value) { | ||
p.value = value | ||
p.then = resolvedThen | ||
p.end = resolvedEnd | ||
p.state = 'resolved' | ||
return p | ||
@@ -421,18 +413,15 @@ } | ||
* Dafualt uncought error handler. It will wait 1 second for you to | ||
* handle the error before logging it to the console. Feel free to replace | ||
* handle the error before rethrowing it. Feel free to replace | ||
* this function with one you find more useful. Its purely a debugging | ||
* feature. Technically promises can't throw unhandled exceptions. They just sit | ||
* around waiting for you to handle them | ||
* feature. Technically promises shouldn't ever throw since at any time in the | ||
* future someone could decide to handle the error | ||
* | ||
* @param {Promise} promise The promise that was rejected without an error handler or child promise | ||
* @param {Error} e The Error instance emitted by the promise | ||
* @param {Promise} promise The promise that was rejected without an error handler | ||
* @param {Error} e The error emitted by the promise | ||
*/ | ||
exports.onError = function (promise, e) { | ||
promise._throw = setTimeout(function () { | ||
if (e instanceof Error) { | ||
if (e instanceof Error) | ||
e.message += ' (Rethrown from rejected promise)' | ||
throw e | ||
} else { | ||
console.warn('Not handled within 1 second: ', e) | ||
} | ||
throw e | ||
}, 1000) | ||
@@ -442,4 +431,5 @@ } | ||
/** | ||
* Sometimes error handlers may be added after a promise has been rejected. | ||
* In such a case you might want to "un-log" the error | ||
* If you set a timeout in the `onError` handler (the default) you can cancel it | ||
* in the onCatch handler. This will be called when an error handler is added to | ||
* a rejected promise. | ||
* | ||
@@ -446,0 +436,0 @@ * @param {Promise} promise The promise which previously failed without error handlers |
Sorry, the diff of this file is not supported yet
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
51466
0
403