Giter VIP home page Giter VIP logo

when's People

Contributors

adamloving avatar anthonyvia avatar briancavalier avatar conradz avatar ded avatar gotdibbs avatar iamstolis avatar jamlen avatar jhnns avatar johan avatar kfranqueiro avatar kidkarolis avatar kkirsche avatar kriskowal avatar kyos avatar levibuzolic avatar mrienstra avatar nonplus avatar pdehaan avatar renato-zannon avatar rwaldron avatar schoonology avatar scothis avatar stevage avatar thanpolas avatar tikotzky avatar twisol avatar unscriptable avatar warpr avatar zhiyelee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

when's Issues

Should we freeze(when)?

For a library like when, that needs to be trusted, defend against other, potentially non-compliant or even broken implementations, and may be in use by other 3rd party libs you're using in your application, it seems like a good thing. For example, you may have a single copy of when.js in your codebase, and it is being used by several of your dependencies.

In that case, you need to be able to trust that when.js hasn't been altered.

The tradeoff, of course, is that you can't do crazy stuff like replace when.defer() to make it do something you think it should be doing, or hang new functionality off of when. In my personal opinion, you shouldn't be doing those things anyway--imho, wrapping rather than replacing is a better option.

So, at least right now, I think we should freeze(when), but I'm definitely open to hearing what other folks think.

Need support for pipe() and when()

jQuery 1.7.x provides futures support (Deferred, Promise) with two important methods: pipe() and when()

  • pipe( resolveFunc, rejectFunc, progressFunc ) - provides chaining features AND intercept features critical to code clarity.
  • when( promiseOrFunc1, promiseOrFunc2, ...) - provides feature to parallel process 1 or more functions/futures.

These are used frequently in jQuery solutions. Latest jQuery Deferred is also dependent upon Callback class for robust notifications. Why did you not just grab the jQuery Callback, Deferred, and Promise classes into a stand-alone when.js ?

I wonder if when.js would benefit from these also.

Thank you for this stand-alone version.

BTW, perhaps another test of any Promise implementaiton would be sucessful use with QUnit tests for Deferred/Promise:
@see QUnit Testing of Deferred/Promise

Always return undefined from deferred.resolve/reject

Currently, deferred.resolve/reject return a promise. This can be convenient somethings for things like:

return deferred.resolver.resolve(someValue);

since that would return a promise for someValue in a single line, rather than having to do:

deferred.resolver.resolve(someValue);
return deferred.promise;

Even if the deferred was previously resolved/rejected, the above is still safe because subsequent calls to deferred.resolve/reject also return a promise--but for their input rather than for the original resolution value. That way, the API is safe to use, but doesn't reveal anything about the state of the promise to parties that may have only been given a reference to the resolver.

It turns out that this causes some issues with other promise implementations, such as Q, who like when.js, assimilate foreign promises. In talking with the Q implementors we feel the best solution is simply to return undefined from deferred.resolve/reject in all cases. That prevents the interoperability issues, and doesn't reveal anything about the state of the promise.

This could be a breaking change for anyone relying on the returned promise. I don't think that's widely used, but want to get this on people's radar ASAP.

deferred.resolve/reject/progress should return silently when already resolved

The reason they current throw is practical: debuggability. However, I think it is more correct for them to simply become black holes. The reason is that it is currently possible for someone who has a reference to deferred.resolver to determine the status of deferred.promise by doing:

try {
  resolver.resolve();
} catch(e) {
  // If we get here, we know the promise is already resolved
}

The whole purpose of resolver separation is that you shouldn't be able to determine anything about the promise. It will also simplify (quite a bit) when.some and when.any if the resolver doesn't throw.

The potential downside is that you could accidentally resolve the same deferred more than once and not realize it. To mitigate that, when/debug could make deferred.resolve/reject/progress throw. So you would still be able to use when/debug to track down those kinds of problems.

Rejections propagate even when I don't re-throw them

This code:

when(42).then(function () { throw new Error(); })
        .then(function () { }, function () { return 5; })
        .then(function () { console.log("fulfilled"); },
              function () { console.log("rejected"); }
             );

results in "rejected", whereas I expect "fulfilled". Recall from the Promises/A spec,

This function should return a new promise that is fulfilled when the given fulfilledHandler or errorHandler callback is finished. This allows promise operations to be chained together. The value returned from the callback handler is the fulfillment value for the returned promise. If the callback throws an error, the returned promise will be moved to failed state.

Since I did not throw an error, and I returned 5, I expect the resulting promise to be fulfilled with 5, and not rejected.

Array-based methods (e.g. when.all/map/reduce) should also accept a promise for an array

For example:

var d = when.defer();

when.all(d.promise, function(resultsArray) { ... });

// d may resolve to a mixed array of values and promises
var d2 = when.defer();
d.resolve([1, 2, d2.promise]);

d2.resolve(3);

One practical example of where this could be very helpful is Dojo's data stores, which can return a promise for an array:

when.map(datastore.query({ ... }), mapperFunction).then(doSomethingWithMappedResults);

Implementing this is simple, but will probably make the solution to #15 insufficient, and we'll have to check parameters at the API boundary (e.g. directly in when.all), instead of relying on the checks in .then().

Should when.js Promises implement valueOf() and/or toString()?

Implementing valueOf() could be interesting in that it would allow the valueOf a resolved promise to return the resolution value, which would allow some operators, like ==, +, etc. to work on resolved promises. For example, doing a comparison directly on a promise if(resolvedPromise == 1) {...}.

The potential downside is that people might misuse this, or if they aren't familiar with using promises, could be confused into using it incorrectly. For example setInterval(function() { if(promise == 1) doSomething(promise.valueOf()); }, 100)', rather than when(promise, doSomething);

Not sure yet what the value would be in implementing toString() to return something like "[object Promise]" when unresolved, and String(resolvedValue) when it is resolved. But we should at least think through it.

Option for auto-wrapping exceptions

when.js auto-wraps exceptions thrown in deferred resolve handlers and forwards them to reject handlers to be handled there.

This makes sense when using exceptions as part of "normal" program behavior to control program flow (that is when the program is expected/designed to handle exceptions).

Its however inconvenient during development, when exceptions are considered fatal and not to be handled in program anyway.

For example, in AutobahnJS we use rejects to signal RPC error returns. An exception thrown in a resolve handler (after RPC success) would need to be logged in each and every reject handler. Further we need to distinguish between reject being called because of RPC error and reject being called because of exception from resolve handler.

With jQuery Deferreds, exceptions thrown will just pop up in the browser console and developers can fix them.

IMHO, it would be nice to have a global or per deferred instance option to control whether exceptions thrown in resolve handlers get wrapped and forwarded to reject, on just be (fatally) let through to pop up in browser console.

Btw: what about exceptions thrown in reject handlers?

Add a dependant promise to an existing deferred

I would like to to add a dependant promise to an existing deferred, sth similar to when.all(), but done after it (although prior to resolving the deferred, of cource).

Eg

var p1 = when.defer().promise
var p2 = when.defer().promise
d = when.all( p1, p2 )
var p3 = when.defer().promise
// proposal here
d.when( p3 )

Thanks!

IE8 calls the wrong function, causing infinite recursion

IE8 incorrectly calls the function expression named resolve within the defer() closure, instead of calling the outer function named resolve() like every other sane platform does. The fix is simple--rename the function expression, or remove the name entirely, but I prefer the former for debugging purposes.

fail early and loudly when attempting to register a handler that's not a function

Right now, if you accidentally pass something that's not a function as the callback, errback, or progback to .then(), when(), or any other method that accepts handlers, it may fail silently later when the promise is resolved, rejected, or progressed. Even if the failure does get logged, the accompanying stack trace might be useless if the resolution/rejection/progress occurs in a different event loop turn.

This could become an even bigger debugging headache if we start forcing future-turn resolutions, see #12.

This may be more likely to trip up folks who use when.all/some/any(), and who may have used jQuery Deferred and $.when. Since when.all()'s second parameter, for example must be a function, and $.when()'s second parameter is allowed to be another deferred, some folks may be inclined to write:

when.all(promise1, promise2).then(doSomething);

which may fail silently. It should be written as:

when.all([promise1, promise2]).then(doSomething);
// or
when.all([promise1, promise2], doSomething);

when( synchronous_function_throwing_an_error) will trigger success callback

This works:

 when(when.reject('error')).then(function() { console.log("ok"); }, function(err) {console.error(err); } ); // error

However when the synchronous error is throw in when(), it will trigger success callback:

when(function() { throw new Error("Error");}).then(function() { log("ok"); }, function(err) { log(err); } ); // ok

While in then() it will trigger error callback:

when(when.resolve()).then(function() { throw new Error("Error");}).then(function() { log("ok"); }, function(err) { log(err); } ); // Error

Accessing promise state for the sake of tests

I'm writing a client for a server where I provide promises as a public API to keep track of state. The API looks like this:

var myClient = createSessionClient();
myClient.started.then(function () { ... });

This allows the API to be very simple, the users of the API doesn't need to know if the session has already started or if it will start in the future. There's started, loaded, ended and unloaded. In my tests, I want to do something like this:

serverSideObject.started(); // Will cause the promises to be resolved based on state
assert(myClient.started.resolved);
refute(myClient.loaded.resolved);
refute(myClient.ended.resolved);
refute(myClient.unloaded.resolved);

As suggested on twitter (https://twitter.com/#!/cjno/status/195984710806147073) I could do something like this:

var startedSpy = sinon.spy();
myClient.started.then(startedSpy);
var loadedSpy = sinon.spy();
myClient.loaded.then(loadedSpy);
serverSideObject.loaded();
assert.calledOnce(startedSpy);
refute.called(loadedSpy);

This is a lot more code, but it should be possible to tone it down with some helper functions.

Just for the record, the code I'm writing is the client for a buster-capture-server session, where you create a client object and get notified when the session starts, loads, etc. The client gets this data via a Faye pubsub messaging server.

Need when.defer( function (dfd) {... } )

One nice syntactical feature of jQuery Deferred is the optional constructor function:

var defer = new $.Deferred( 
                function(dfd:Deferred) 
                {
                    // Do async action inline 
                    // prepare callbacks
                    // resolve/reject if needed here
                }            
            ).promise();

A brief study of when.js did not immediately reveal such to me...
Does when.js support such usage syntax?

Add apply/spread/variadic() callback helper

When a promise result is an array, it may be useful to call callbacks with with array elements as the callback's argument list, rather than simply passing the array as a single parameter. This can make for cleaner code in some cases, and may allow reuse of existing functions as promise callbacks.

Right now, when.js always calls callbacks with a single parameter, in this case the results array. That's nice because the promise consumer be sure what the callback signature is. I think there are use cases for both passing an array and spreading the array, so maybe there is a way to provide both easily.

To do it now, you have to wrap:

when.all(arrayOfPromises,
    function(resultsArray) {
        realCallback.apply(null, resultsArray);
    }
);

With an apply() helper:

when.all(arrayOfPromises,
    apply(realCallback)
);

Promise returned by then() doesn't chain progress

Currently the promise returned by then() doesn't propagate progress() notifications. In the following example, I would expect both progback1 and progback2 to be called with "update" but only the first one is called:

var dfd = when.defer();
var p1 = dfd.promise;
var p2 = p1.then(null, null, progback1);
p2.then(null, null, progback2);
dfd.progress("update");

I don't think the spec addresses this, but it looks like a bug to me. Is it the intended behavior? If so, that really limits the usefulness of progress reporting. If not, I can add some unit tests and create a pull request.

The behavior can be fixed in _then = function (callback, errback, progback) by replacing:

progback && progressHandlers.push(progback);

with:

progressHandlers.push(function (update) {
    progback && progback(update);
    deferred.progress(update);
});

FWIW, jQuery's promises propagate progress. So does node-promise (at least from looking at the code).

Listener functions fail silently

The try/catch wrapper around the code that notifies listener functions will swallow up any error occurring in the call chain of a listener function (./when.js#L281). This is not very desirable as any errors inside a listener function will be suppressed.

Consider the following example:

var deferred = when.defer();
deferred.then(function() {
    i.will.produce.ReferenceError
});
deferred.resolve();

This will fail silently, which is clearly not the expected behavior (I would expect a message to appear in the error log of the browser/environment).

Guarantee future-turn resolutions

Currently, when.js doesn't guarantee future-turn promise resolutions. Callbacks might run immediately, in the current stack frame. The Promises/A draft spec leaves it unspecified.

Here are some pros and cons I can think of:

Pros

  1. Removes all doubt as to whether a callback will run immediately or in a future turn. Currently, a callback registered with when() or .then() might execute immediately or in a future turn depending on the state of the promise (and potentially on the implementation if there are other promise implementations besides when.js in the app)
  2. Protects variables in the current stack frame from being modified by a callback that captures them and might execute immediately. Related to 1, but wanted to call it out, specifically.

Cons

  1. Performance. This is the big downside in my mind. Even in environments that provide a fast alternative to setTimeout(), there will be a hit. For environments that only provide setTimeout(), the hit will be even more significant, due to low timer resolution.
  2. Implementing this in an optimized, cross-environment way to take advantage of faster alternatives where available will obviously make the lib larger--not by a huge amount, but worth noting.

I've been using promises in JS heavily for over a year and honestly, I've never been bitten by, or even surprised by, a callback being called in the current turn when I didn't expect it to. I've also never run into a situation where I felt like I needed future-turn guarantees to implement something correctly.

All of my work has been in browsers, though, so I don't have a good feel for how important this issue is in a server environment, or how important the performance issue is there.

I'd love to hear what people think about this.

Remove freeze() until v8 freeze() is sane

The performance hit when accessing properties of frozen objects in v8 is unacceptable currently--it can be 10x or more. Right now, when.js freezes every promise and resolve object it creates when in paranoid mode (which is the default).

For now, I think it makes more sense just to remove calls to freeze() altogether, since it doesn't provide any real security (but rather just dissuades you from tampering with a promise). We can add it back once v8's freeze() is sane.

Add a promise rejection handler convenience method

See #13 for a discussion of various convenience methods. That thread led to the implementation of .always() in v1.1.0. We're still considering adding promise.otherwise(errback) as suggested by @jonnyreeves, or a method of some other name for easily registering rejection handlers.

when.any() fails when callback not provided

If you call when.any() like this, it will fail in v1.1.0:

when.any(promises).then(callback);

The workaround until a fix is released is to call it like this:

when.any(promises, callback);

any() and some() behavior is nondeterministic and inconsistent

As currently implemented, given the same theoretical set of Promises, a call to any() or some() will produce a different result depending on the timing of when those Promises are resolved or rejected.

For example:

If a Promise in the supplied set is resolved before any of the other Promises are rejected, any() will resolve the Promise it returned with the resolved value. If one of the Promises in the supplied set is subsequently rejected, it is ignored because the returned Promise has already been resolved.

If the timing is slightly different, and a Promise in the supplied set is rejected before any of the Promises are resolved, any() will reject the Promise it returned with the rejected value. If one of the Promsies in the supplied set is subsequently resolved, it is ignored because the returned Promise has already been rejected.

any() is documented as:

Return a promise that will resolve when any one of the supplied promisesOrValues has resolved.

and some() is documented as:

Return a promise that will resolve when howMany of the supplied promisesOrValues have resolved.

so this behavior would appear to contradict the documentation.

In their current form, this nondeterministic behavior sets any() and some() apart from their all(), map(), and reduce() brethren. Evaluating the same theoretical set of Promises, all(), map() and reduce() will always produce the same result, regardless of the timing of their resolution or rejection.

I noticed earlier this week that someone else reached out to @briancavalier on Twitter to demonstrate that passing an Array containing a rejected Promise and a resolved Promise to any() produces a Promise that rejects instead of resolving. This reveals that any() and some() in their current form can also potentially give greater weight to Promises that appear earlier in the supplied Array.

I believe that the correct behavior would be for any() and some() to defer rejection until after all potential resolutions have been ruled out. So, any() would resolve as soon as a Promise resolves but wouldn't reject until all of the supplied Promises have been rejected (and there is no possibility of a Promise resolving). Similarly, some() would resolve as soon as the specified number of Promises resolve, and would only reject once there was an insufficient number of pending Promises available to meet that threshold.

This also raises the question of what rejection value should be returned. Currently, the first rejection value is returned. This also seems odd (and the same applies to all(), map(), and reduce()).

I'm starting to think that all of these utility methods should return consistent rejection values that are oriented relative to the attempt to perform that operation.

If a developer wants to be aware of the reason why a specific Promise in the supplied set was rejected or wants to add recovery logic, that should be applied to the originating Promise (and that recoverable Promise should be supplied in the set instead) rather than to the aggregate utility operation.

If they are adding fault handling or recovery logic to the Promise returned by the aggregate utility method, that should be considered as being relative to the failure of that method's operation, not the individual elements originally supplied to that method.

Thoughts?

Provide convenience for returning rejected / resolved Promises.

A couple of times I've come across a situation where a function can return and resolve a promise in the same statement, take the following example code:

function getThingy(id) {
    // See if we have previously fetched this thingy.
    var cachedThingy = this._thingiesByIdMap[id];
    if (cachedThingy) {
        // Good news - no need for a round-trip, we can resolve straight away.
        var dfd = when.defer();
        dfd.resolve(cachedThingy);
        return dfd.promise;
    }
    else {
        // Another method, which returns a Promise, takes care of the actual fetch.
        return this._fetchThingyAsync();
    }
}

To me, the code inside the first if branch feels messy - we are creating a temporary (dfd) and having to split the logic over three lines (yes, I'm charging by the line now! ;) It would be great if we could have a terser syntax which also removed the need for creating a temporary variable, jQuery manage this by allowing the defer method to accept an optional function argument which will be supplied with the created deferred object, eg:

if (cachedThingy) {
    return when.defer(function (dfd) { 
        dfd.resolve(cachedThingy);
    }); // Optionally tack `.promise` on the end (but it's already resolved, so that's moot)
}
else {
    // ... Logic omitted
}

Thanks! :)
Jonny.

Support object keys in addition to arrays

On several occasions, I've wanted to use when.reduce() on an object's keys rather than on an array, and @sifu recently tweeted something similar about when.all(). Also, promised-io provides allKeys(), in parallel to all().

It'd be great to find an elegant way to support object keys across all the when.js "array" methods. One approach might be to create equivalent "keys" methods: allKeys(), reduceKeys(), etc., or to provide an additional module that houses the object key versions of those methods.

Not sure what is best here, so looking for input, keeping in mind that staying as compact as possible is an important goal :)

What should resolver.resolve(promise) do?

Currently, when.js will use whatever you pass to resolve() verbatim as the resolution value (and similarly for reject()). That means if you pass promiseA as the resolution value of promiseB, promiseB's callbacks will receive promiseA itself, not its value. In some ways that makes sense: it simply passes whatever is passed to resolve()/reject() as is.

On the other hand, that is inconsistent with other scenarios, such as returning promises from callbacks. In every other scenario, when.js will ensure that subsequent callbacks receive resolved values rather than promises. So, for consistency, it seems like resolve(promise) should cause callbacks to be invoked with the fully resolved value of promise.

when/debug doesn't work with RequireJS

I'm using RequireJS.

I set up when/debug in my amd config like this:

require.config({
  paths: {
    "when": "vendors/when/debug",
  }
})

However, I then get the following exception when my app is loaded.

236  whenDebug.defer = deferDebug;
237  whenDebug.isPromise = when.isPromise;

Uncaught TypeError: Cannot read property isPromise of undefined

It seems that "when" is undefined here.

define(['./when'], function(when) {

My files look like this:

/vendors
  /when
     - when.js
     - debug.js

If I use "vendors/when/when" in my path config - it works. I'm not sure what I might be doing wrong.

Small improvement in public API

Hello,

First of all I would like to thank you for the great library.
I have an idea about small improvement in your library that could be helpful in some cases I suppose. The current implementation supports only functions as handlers/callbacks. This is quite restrictive for my application. Of course, I can use closures as wrappers for anything I need but… they are resource consuming tool and this is excess step in my case. So it would be useful to have a special public method (for example, when.execute) that is used to call/execute handlers (in _progress and notify functions) and can be redefined. By default this method may look like:

when.execute = function(handler, value, deferred) {
    return handler(value);
};

The "deferred" parameter can be optional.

With best regards.

Optionally allow a promise to be cancellable

I would like the consumer of a promise to be able to cancel an operation. While the consumer of a promise should not be able to directly resolve or reject the deferred, it should be able to say that it no longer cares about the outcome.

Resolving promises with promises

when.js does have a strange bug when resolving a promise with an other promise. Consider this code:

var when = require('when')

var a = when.defer()
  , b = when.defer()
  , c = when.defer()

a.name = 'A'
b.name = 'B'
c.name = 'C'

// 1) without when()
// a.then(function(resolved) {
// 2) with when()
when(a).then(function(resolved) {
  console.log('A resolved into', resolved.name)
})

a.resolve(b)
b.resolve(c)
c.resolve({name : 'D'})

This (second) variant prints A resolved into C. If you switch to the first variant, it prints A resolved into B.

The two variants are supposed to be equivalent aren't they?

Consider removing .then() from Deferred

Currently when.js's deferred looks like a promise, and encourages people to expose it to callers. The problem is that it's not safe to do so, since it's not frozen, and it also has the resolution methods.

This could be a major breaking change for some folks, I'm sure, so we should start talking about it now.

Add npm install info to README and/or wiki

Due to a name conflict and npm's "one global namespace should be enough for all modules that will ever exist" philosophy, when.js is not in the npm registry. The way to install it is to use the github url. We should add some helpful info to the README and/or wiki.

npm install git://github.com/cujojs/when

Which also works as a dependency in package.json:

"dependencies": {
    "when": "git://github.com/cujojs/when"
    // or for a specific version: "when": "git://github.com/cujojs/when#1.0.3"
}

Unwanted coercion of non-promises

When assimilating foreign promises, we're too aggressive in calling valueOf on things that aren't promises, which is problematic for things like Date, or any object type that might provide a custom valueOf

Examples

Hi, just wondering if you could provide some working examples.

I managed to cobble together a basic example based on my limited knowledge of how Promises work but I can't help thinking I must be missing something.

So, not an 'issue' as such, just a request for example code :)

Need a chain() example

I'm struggling to understand chain()--specifically the resolver portion of it. Could you please provide a quick example? Thanks.

Debug doesn't rethrow exception when used in when().then() form

Debug module rethrows the exception in this case:

when(true, function () {
  iDontExist();
});

But not in this case:

when(true).then(function () {
  iDontExist();
});

Is that the expected behavior? I usually use the second form, for example, one function might be creating the promise and returning it, and then some other function attaches callbacks using then(). If that callback throws an exception it happens silently.

Add resolved promise creation to public API

Now that when.reject has been made public, we should do the same for when.resolve. This has the added benefit of being API compatible with the Promises/B/D promise manager spec, and so also with Q.

Infinite recursion

I just had a problem with when.js + jquery's deferreds.

when.map(someBackboneModels, function (model) {
  return model.save(); // this returns a jquery's ajax result
}).then(
  function () {
    console.log('success');
  },
  function () {
    console.log('error');
  }
);

In case of success, everything works as expected. In case of the error, when.js starts calling it's internal functions recursively and kills the browser..

I will try to reproduce/identify the problem, but do you spot anything wrong with the above?

Revisit decision to forward previous value

Early on, I made a decision that when a handler returned undefined, the previous promise value should be forwarded through to the next promise. That's convenient in some cases, as it allows reusing and writing handlers that have no return statement.

However, it means that it's easy to get into trouble when doing things like this:

when(somethingHappens, function(result) {
    return result["thePropertyIReallyCareAbout"];
}).then(handleProperty);

In cases like that, where the first handler legitimately needs to return undefined, and we want handleProperty to receive undefined, it will instead receive result, which is just plain wrong. Right now, the workaround is:

when(somethingHappens, function(result) {
    return result["thePropertyIReallyCareAbout"] || null;
}).then(handleProperty);

which is ok, but probably would cause someone a real headache trying to figure it out, and means that handleProperty receives null instead of undefined.

Add convenience methods like .always()

Currently, when.js's promise provides .then(), so if you want to register the same callback to run whether the promise is resolved or rejected, you have to pass it explicitly as both the callback and errback to when() or .then(). Both jQuery Deferred, and Q's promises provide convenience methods for this.

Here's how you have to do it with when.js currently:

promise.then(callback, callback);
// or
when(promise, callback, callback);

That's not horrible, but this seems like a nice convenience:

promise.always(callback);
// or
when.always(promise, callback);

It might be interesting to provide both promise.always(callback) and when.always(promise, callback) so that when.always() can be used with other promise implementations. Also, since something like promise.always() is not part of the Promises/A spec, relying on it would mean tying your code to when.js's promise implementation. Using when.always(promise) is a bit more decoupled.

Seems like this also opens the door for a method that registers only a rejection handler. For example, to do that now:

promise.then(null, errback);
// or
when(promise, null, errback);

Which is kind of ugly, and might cause a "WTF". So, could provide something like:

promise.rejected(errback);
// or
when.rejected(promise, errback);

This seems like a much more limited use case to me, though.

Find a better way to enable when/debug in Node/RingoJS

Since Node and (as far as I know) RingoJS don't have a way to remap module paths, as AMD does, right now, the only way to globally enable when/debug is to modify when.js's package.json, which pretty much stinks.

There needs to be an easier and less dangerous way.

One option may be for when.js itself to look for a boolean on Node's global, and then require and set its exports to when/debug.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.