Code Monkey home page Code Monkey logo

promises's Introduction

promises's People

Contributors

annevk avatar arv avatar cwilso avatar dan-cx avatar davidbruant avatar dj2 avatar domenic avatar jakearchibald avatar mounirlamouri avatar nicoder avatar sicking avatar slightlyoff avatar wycats 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

promises's Issues

Futures-for-futures

I feel like I tried to bring this up earlier, but it deserves its own issue.

Futures-for-futures are hazardous in the following manner (via @briancavalier, indirectly via MarkM, I believe):

let future = new Future(({ accept }) => accept(5));
let futureForFuture = new Future(({ accept }) => accept(future));

let identity = x => x;

// With ordinary futures, this is a composable identity transformation:
let a = future.then(identity);
// That is, `a` will be accepted with `5` as its value, just like `future`.

// But with futures for futures, identity is broken:
let b = futureForFuture.then(identity);
// That is, `b` will be accepted with `5` as its value, but
// `futureForFuture` of course has `future` as its value.

// This prevents `identity` from being used as a no-op in the general case.

Do you agree that this is a problem? If so, I am happy to discuss solutions, as we have been doing over in Promises/A+ land. If not, /shrug.

Typo in error message

Search for Cannot resolve a Future mutliple times. Replace mutliple with multiple.

No practical way to handle uncaught exception

The latest version of Promise places the done() method with catch(). The problem is that under Google Chrome you cannot do the following:

futureInstance.catch(error)
{
  window.onerror(error);
}

because window.onerror is undefined, and you cannot throw error; because the Promise will catch the error and translate it into a Promise.reject() which is never handled.

In short, there is no practical way to cause errors to bubble up to the browser's default exception handler. Yes, I can log the error myself but I should be able to push exceptions to the default handler.

Remove attributes on Future

The Future interface currently has .value, .state and .error. It doesn't seem that those attributes are common in the Javascript libraries/definitions of promises [1][2][3]. Indeed, those values are simply useless because in a success or error callback, they are all known because of the context.

For example:

asyncCall().then(function(result) {
  /* We know that:
    - .value == result;
    - .error == undefined;
    - .state == "accepted" */
}, function(error) {
  /* We know that:
    - .value == undefined;
    - .error == error;
    - .state == "rejected" */
});

There is also:

var future = asyncCall();
/* At that point, we know that:
  - .value == undefined;
  - .error == undefined;
  - .state == "pending". */

The only case where we might need to know what .value, .error and .state values are is this one:

var future = asyncCall();
methodThatWillProduceMultipleTripToTheEventLoopBeforeReturning();
// We no longer know the state of |future|, we might want to check with .state.

However, I believe that .then() should behave so that this is working:

var future = asyncCall();
methodThatWillProduceMultipleTripToTheEventLoopBeforeReturning();
future.then(function(result) {
}, function(error) {
});

The .then() method should work even if |future| was no longer pending. I we agree on that behaviour (I am not sure if the documentation specify that and, honestly, it is hard to read un-formated documentation), I think we could simply remove those three attributes and make the Future interface slimmer.

[1] http://wiki.commonjs.org/wiki/Promises/A
[2] https://github.com/kriskowal/q
[3] http://promises-aplus.github.com/promises-spec/

CC: @annevk @sicking @slightlyoff

Add a .fail method

From experience with the Q library, when the 2 callbacks of the then are long, it isn't very clear where the onrejected starts.
Adding a .fail(function) gives a more declarative impression and is less ambiguous (doesn't need to keep track of indentation of a long function)

IDBTransaction should become a Future as well

In the "reworked APIs" section, IDBRequest becomes a subclass of Future.

IDBTransaction behaves similarly, although it's not as simple a type as a IDBRequest. A transaction has a lifetime bounded by other asynchronous activity, after which it completes successfully ("complete" event) or fails ("abort" event), and in both cases action is likely to be taken by the script.

With current IDB you write:

  tx = db.transaction(...);
  tx.objectStore(...).put(...);
  tx.oncomplete = function() { alert("yay, done"); };

I'd expect a Future-ish IDB to let me write:

  tx = db.transaction(...);
  tx.objectStore(...).put(...);
  tx.then(function() { alert("yay, done"); });

... which of course gets more useful when you have join() operations letting you wait on multiple futures and all that other goodness.

Capture resolution of forwarding for accept() in API and prose

After much investigation and discussion with Mark Miller, the decision regarding forwarding in accept() is:

  • If you pass a Future to accept(), the resolution of the "near" future is merged with the (perhaps eventual) resolution of the "far" future.
  • The API will be extended with an acceptDirectly() which allows Futures to be accepted values, anticipating that this is the rare and exceptional case.

Handling "Watches"

I am attempting to map the Geolocation API to the DOMFuture API, getPostition seems pretty simple. However the watchPosition API and the associated clearWatch are slightly troublesome.

Firstly, watchPostition is an asynchronous API that returns a value immediately (like setInterval and setTimeout) and then "onSuccess" is called multiple time as the position changes until you call clearWatch.

I am not sure how this maps to DOMFutures API, where to me at least, it seems very much like .then() will only be called once before passing to the next .then in the chain. Should I be using the Progress Future?

If we use the ProgressFuture, how should one handle the "watchId" so that the event can be cancelled. Would it be ok to assume the .value property returned is the watchId?

Expanding DOMFutures

[http://lists.w3.org/Archives/Public/public-sysapps/2013Feb/0121.html](some thoughts).
I don't know if that belongs to the core DOMFuture spec. Maybe a non-normative section. Maybe a different document where other advice on how to design an API would be listed.

I'm creating the issue mostly to share the thoughts and discuss if there is agreement on the advice and whether that belongs to the DOMFuture spec.

Clean up exception stack-traces

I like Future.then()-style callback chaining but it has the nasty side-effect of mangling exception stack-traces in a way that makes code harder to debug.

Is it possible for the implementation to programmatically rewrite the stack-trace so Future.then(A).then(B).then(C) ends up with a stack-trace that reads C -> B -> A instead of C -> Future.wrap -> Future.pump -> anonymous function -> anonymous function?

Creating the Resolver/Future connection

The current text implies that the Future and Resolver are conencted and that only the Resolver that creates a Future can eventually resolve it. To set up this linkage, we need some way to explain how that linkage is created without magic or ES6 @-names, since they're delayed to ES7 at the soonest.

One thought is that the Resolver might expose some callback-registration methods or even events. The Future will be passed the Resolver in it's ctor and automatically connect itself to the Resolver at creation time.

Thoughts? Preferences?

/cc @domenic @wycats @arv

Not installable via npm

npm requires that package.json be at the top-level a git repo for a node module. Could things be shifted around so that this command can be run successfully:

npm install git://github.com/slightlyoff/DOMFuture.git

One simple solution would be to just move the polyfill contents up a directory. These changes would also help people forking the repository and installing w/npm.

.then() on Resolver instances?

One question has nagged at me related to @domenic's question regarding cancelation. It seems that some APIs will with to return resolvers to expose the ability to resolve. The natural style of use is broken for these APIs:

// Note that we must dig out the future property in order to .then()
var r = doThing();
// ...
r.future.then(function(v) { ... }, ...);

// This doesn't work:
doThing().then(...);

To repair this, should a Resolver expose a .then() method which forwards to its .future's .then()?

E.g.:

Resolver.prototype.then = function() {
   return this.future.apply(this.future, arguments);
};

If we do this, should returning a Resolver in a .then() also be treated the same way as returning a Future?

Throwing non-errors

It seems from the IDL that resolvers are not going to allow rejecting with non-Errors. OK. But what about this sentence?

If the callback throws, resolve the returned future with an Error

What if the callback throws a non-Error? E.g.

future.then(undefined, function () {
    throw 5;
});

or worse,

future.then(undefined, function () {
    throw undefined;
});

?

A proposal for ending the wrapping/unwrapping war.

Doing this here because the mailing lists have turned into an unfollowable mess.

First, some cautionary notes: this thread will use the terms "wrapping" and "unwrapping" to refer to APIs that treat Futures as something other than direct values. Posts here that use different terminology will be edited to conform.

Next, please be civil and cordial. Those who don't agree with you aren't bad and they might not even be wrong. Goodwill will get us where we want to go.

OK, down to it:

It seems to me that the contended bit of the design is .then() (and by extension, .catch()). Both pro and anti-unwrapping camps can have their own way at resolution time using .resolve() and .accept() respectively. And both have valid points:

  • for those who live in a very Futures-driven world, it's hugely common to want to treat values and Futures/Promises without distinction.
  • for those making the transition, or who have different contracts that they want to service with regards to some non-Futures-loving service, having directness can be a virtue.

On top of these points, there are preferences for styles of use. These can't ever be truly enforced with rigor because it's trivial to write wrapping/unwrapping versions of direct functions and vice versa. So there are cultural differences that we must admit with regards to preferred styles of use.

With the caveat that all involved are willing to live-and-let-live, I observe that it is possible to end the current debate in everyone's favor so long as we add a "direct" version of .then(). That is to say, one which has the semantics of .accept() with one level of unwrapping for a returned thenable and not .resolve() (infinite unwrapping) for values returned from callbacks.

I'll submit .when() as a straw-man name, but I'm not precious about it.

So the question now is: is everyone cc'd here willing to make such live-and-let-live compromise? And if not, why not? And is there some other detail which I've missed?

/cc @domenic @dherman @wycats @annevk @tabatkins @erights

Make DOMRequest inherit from DOMFuture

We're seeing the proliferation of DOMRequests in current specs. In recent example is the alarm API.
Discussion is probably needed, but I feel it's unlikely the FirefoxOS folks will move away from DOMrequests, because a lot of Gaia code relies on them.
A way forward is to make DOMRequest inherit from DOMFuture and ask everyone to move away from DOMRequests (or creating equivalents)

WebIDL no longer supports "raises"

A number of the methods in this API (notably ResolverCallbacks) sport "raises(Error)" but the raises keyword is no longer in WebIDL. Exceptions are specified in prose rather than through the language itself.

Pass resolver to then?

Merging of returned Futures is a good feature, but by default it creates a lot of objects for the GC to clean up. I worry that our current design creates the need for alloc when it isn't strictly necessary. One way around this is to provide the resolver to the then callbacks, allowing them to drive the lifecycle of the vended future directly (vs. merging). Merging is, of course, still supported, but savvy then() users could prevent mandatory alloc by returning the Resolver instead of a Future, signaling that they want to drive the process.

Crazy?

Resolver API should hide resolve()

I'd like to propose the following design change:

Invoking accept from a Resolver invokes a hidden method resolve if there are any chained Resolvers, otherwise it invokes another hidden method finalAccept.

This way users would only have to be aware of accept and reject. Currently we require users to invoke resolve which I found to be very confusing. I shouldn't have to invoke different methods depending on whether chaining is taking place or not; that should be an implementation design.

semantics of "is-a Future"

Currently the prolyfill uses this code for testing whether a value is a Future:

!!x && typeof x.then === 'function' && x.then.length === 2

The issue with "duck testing" is that, as @domenic raised in Issue #13, the type of a fulfilled future is not fully polymorphic: you must never fulfill with a future that you intended to be a completed value. Bottom line: there needs to be a super-clear and ideally super-simple definition of what it means to be a future.

I'll pitch a few alternatives here, but I make no claim to exhaustiveness. Other suggestions welcome.

Approach: a "Future" [[Class]] with an isFuture test

Using instanceof is traditionally brittle because when multiple frames interact, they can have multiple, mutually incompatible versions of a given "class." That's why ES5 introduced Array.isArray as a way of testing whether something has the [[Class]] "Array", which is robust across frames, even when they have different Array constructors. So an analogous approach would be to introduce a new "Future" [[Class]] and an isFuture test that inspects the class. This is very clear and very robust, but has the downside that you can't create futures conveniently with an object literal.

Approach: a Symbol to definitely brand futures

Another approach would be to create a unique symbol (a new ES6 feature for creating opaque unforgeable property keys) that's shared in the standard library and identical across all frames. Then to create a future you would have to have this symbol, a la:

var myFuture = {
    [futureSymbol]: true,
    then: function(/* ... */) { /* ... */ },
    // ...
};

Approach: a string key to "probabilistically" brand futures

A lower-tech approach is to have some sort of published key that is simply a standard string key, but perhaps distinctive enough that it's highly unlikely (though still possible) that someone could accidentally create one. For example, Promises/A+ discussed alternatives like p.then.aplus or p.isPromise.

This approach is certainly the easiest, but I would hope we could do better when we have the opportunity to define this in the web platform and/or ES standard. What's particularly galling about it is that it effectively declares that whatever public key it's using is once and for all permanently associated with this typeโ€”that's global pollution of the entire JavaScript language. Scumbag futures!

Approach: type-and-arity check (again, probabilistic)

We could always just do what the prolyfill currently does. Again, while it's unlikely that someone would accidentally fulfill a value that is not intended to be a future but looks like one, that doesn't mean it can't happen. For example, you might be writing future-aware code that nevertheless fulfills with the result of calling some third-party code, and passes it back over to some other third-party code. And those third-parties may not be future-aware. If they happen to be using values that look like futures, not only will the program be buggy, it'll be buggy in badly unpredictable ways.

See also:

Dave

Remove EventedFuture

Future no longer inherits from EventTarget but EventedFuture still does. It is not clear what is the use case of that interface.

Using .then() for Future seems to be preferred over DOM events. For example, it looks like node moved from an event based system to something else [1]. To be fair, their event based system wasn't allowing .then() but the point is that we can deduct that developers do not like event based system for promises.

Also, it is not clear what would DOM events add to the Future interface. With events, the developers would be allowed to do:

// First:
asyncCall().onaccepted = function() {
};
// Second:
asyncCall().onrejected = function() {
};
// Third:
var future = asyncCall();
async.onaccepted = function() {
};
async.onrejected = function() {
};

Which can easily be expressed that way:

// First:
asyncCall().then(function() {
});
// Second:
asyncCall().catch(function() {
});
// Third:
asyncCall.then(function() {
}, function() {
});

Using DOM events doesn't make the code easier to understand or to write. It actually removes the ability to chain calls and introduce a terrible confusion because .onaccepted and .then() do not actually behave the same way.

If you do:

var future = asyncCall();
spendTimeDoingStuffAndTripsToTheEventLoop();
// The future has been accepted.
future.onaccepted = doSomethingWhenAccepted();

and:

var future = asyncCall();
spendTimeDoingStuffAndTripsToTheEventLoop();
// The future has been accepted.
future.then(doSomethingWhenAccepted);

In the first snippet, doSomethingWhenAccepted() will never be called but in the second snippet it will.

I think it would be better to prevent that kind of inconsistency and not have this EventedFuture interface.

[1] http://howtonode.org/promises

CC @annevk @sicking @slightlyoff

Where to define the data passed.

Firstly, sorry if this sounds stupid.

In my hackjob of a reworking of the Geolocation API, I am trying to determine where the values passed in to "then" or "done" are defined.

The Geolocation spec has http://www.w3.org/TR/geolocation-API/#position and http://www.w3.org/TR/geolocation-API/#position_error_interface objects which are only ever used when passed into the success or error callbacks.

I think (I could be wrong) that in the current model of DOMFutures, their usage in the spec is left hanging? i.e, they might be defined but there is no usage in the IDL for them.

Is it expected that authors will create a {X}Future IDL for their API by either specifiying that the accept callback takes a API{X} specific object, or that done and then will be defined more deeply for the API?

For example, Geo might have a GeoIDL of:
dictionary Position {
/* what ever the spec defines */
};

dictionary PositionError {
/* what ever the spec defines */
};

callback GeoCallback = void (Position);
callback GeoErrorCallback = void (PositionError);

interface GeoOperation : DOMFuture {
void done(optional GeoCallback onaccept, optional GeoErrorCallback onreject);
void then(optional GeoCallback onaccept, optional GeoErrorCallback onreject);
};

Is this along the correct way of thinking or am I miles off?

How is cancellation going to be used?

This is a fairly innovative approach to cancellation. However, I'm not sure it allows the use cases one might hope for. E.g. how could a future-returning XHR method allow you to cancel the XHR?

var xhrFuture = doXhrPostWithLotsOfData(options);

xhrFuture.then(function (whatever, resolverForXhrFuture) {
    // I want to call `resolverForXhrFuture.cancel` to cancel the POST upload,
    // but I'm not even called until way after it's finished!
});

Some examples of how you anticipate cancellation working would help clarity a lot.

Add an equivalent to Q.all

From experience, that's necessary to use promises effectively.
Yes, people can write it as a library... in which they'll do off-by-one errors or equivalent.

Move Future polyfill off of the global

At a minimum, it should have a non-conflicting name with whatever global DOM eventually defines.

The bikeshed is now open. FuturePolyfill? PolyFuture?

.catch() prevents .then() from getting executed

Given:

function third()
{
    return new Future(function(future)
    {
        console.log("third");
        future.resolve();
    }).then(function()
    {
        console.log("third.then")
    });
}

function second()
{
    return new function()
    {
        console.log("second");
        return third().catch(errorHandler);
    };
}

return new Future(function(future)
{
    console.log("first");
    future.resolve(second());
}).then(function()
{
    console.log("first.then");
});

I am expecting the following output:

first
second
third
third.then
first.then

but first.then is missing. If I comment out .catch(errorHandler) I get first.then as expected. It seems that .catch() is blocking .then().

Expected behavior: .catch() should intercept errors thrown by child Futures, but allow control to bubble up to parent Futures on success.

No need to inherit from EventTarget

The .then and .done already take care of registering callbacks.
Futures can be seen as an idomatic construct built on top of promises, paving the cowpath of a very recurrent pattern (make a request which succeeds or fail)

Extensibility for .then()?

It turns out that allowing extensibility via the callbacks parameter only exacerbates the lack of it in .then()'s signature. E.g., if you add a progress callback to a subclass, how does one register to hear about it in .then()? Today, you fight over a 3rd positional parameter, which is as inelegant as the same fight in the API of Future.

One idea sparked from coversation with @jakearchibald is to allow a mirroring bag of callbacks in the signature of .then().

You might then write something like:

f.then({ accept: function(v) { ... },
         reject: function(e) { ... },
         progress: function(...) { ... },
         // other extensions here...
});

Make this repository public?

We could still restrict who can comment I suppose, but it would be nice to point others to it, e.g. Jonas Sicking.

Confusion about `done`, unhandled rejections, and dev tools

Intro

Both in the current IDL:

// No matter what approach is pursued, integration with developer tooling is
// key. Developer consoles SHOULD log un-caught errors from future chains.
// That is to say, if future.done(onresponse); is the only handler given for
// a resolver which is eventually rejected, developer tools should log the
// unhandled error.

...

  // An end-of-chain callback. Equivalent to .then() except it does not generate
  // a new Future and therefore ignores the return values of the callbacks.

and in @DavidBruant's message:

.done to end the chain (and have the devtools report uncaught errors at the end of the chain)

I see some confusion over how done and unhandled exceptions are expected to work. Let me outline how I envision them, and how they work in current promise libraries, which I think is better than---or at least clearer than---the sentiments expressed above.

The Problem

See promises-aplus/unhandled-rejections-spec#1 for a more in-depth explanation if the below doesn't immediately make sense to you.

If you do this:

let future = new Future({ accept, reject } => reject(new Error('boo!')));

you have created a promise with an unhandled rejection. This is equivalent to the synchronous code

let result = (() => throw new Error('boo!'))();

Of course, the synchronous code would hit window.onerror, since nobody's there to catch it. But we can't do that with promises, because they are first-class values who we can give to someone else who might handle them, say, after 100 ms.

This is the essential problem, in a nutshell. If nobody ever attaches a rejection handler, errors will go unreported and be silenced. Bad!!

The done solution

See promises-aplus/unhandled-rejections-spec#5 for a more in-depth explanation if the below doesn't immediately make sense to you

One solution is to have a rule that you enforce on promise users and creators: always either

  • return the promise to your caller, or;
  • call done to signal that the buck stops here, and if there are any unhandled rejections, they should hit window.onerror.

That is, done is essentially:

Future.prototype.done = (onaccept, onreject) => {
  this.then(onaccept, onreject)
      .catch(reason => setImmediate(() => throw reason));
};

This rule is pretty good, and used in Q and in WinJS promises with some amount of success. @lukehoban has previously remarked that it ends up not being as much of a burden on developers as you'd initially think, and I agree.

But, it's still error-prone: if you slip up and don't follow the rule even once, you might silence an error forever.

The dev-tools solution

See promises-aplus/unhandled-rejections-spec#2 for some speculations on how to implement this solution in current promise libraries.

This solution involves the dev tools having a hook into the promise library, such that any time an unhandled rejection is created, they are notified. But more importantly, whenever an as-yet-unhandled rejection becomes handled, it notifies the dev tools of that.

With this in hand, you can imagine a panel in the dev tools that shows outstanding unhandled rejections, dynamically adding them as they are created and removing them as they are handled. If they stick around for too long, you know there's a problem.

One way of thinking of this is as an "erasable console.log", i.e. if we could just console.log the unhandled rejections, but then when they get handled, "erase" that entry we wrote to the log. Indeed, Q implements something like this using the fact that when you log arrays in Chrome, they are "live" and auto-update as the array changes. So we log the array of unhandled rejections, and add/remove from it as appropriate. If you ever see a non-empty array in your console for too long, you know there's an issue.

The weak refs solution

This solution was mentioned to me by @erights. Using something like the weak references strawman, promise libraries could maintain a table mapping promises to unhandled rejections. Once the promise gets garbage collected, and the weak ref thus points to nothing, it logs those unhandled rejections.

Unfortunately this cannot be implemented in terms of weak maps, from what I understand. You need the garbage-collection observability which weak maps specifically do not provide.

Conclusion

So, hope that was clarifying. I think the current IDL doesn't express how done is meant to work very well, and seems to tie dev tools to done, whereas they are actually separate and somewhat-complementary solutions.

Hope this helps!

Throwing an exception inside a Future

Currently, throwing an exception inside a Future triggers an uncaught exception.

Instead, I am proposing that throwing an exception should trigger Resolver.reject(exception) so that if there is an error handler registered, it be allowed to handle the exception.

This also results in cleaner code. Consider:

    return new Future(function(future)
    {
      if (!uri || typeof(uri) !== "string")
          throw new TypeError("Invalid uri: " + uri);
      if (!department || typeof(department) !== "string")
          throw new TypeError("Invalid department: " + department);
      ...
    }

versus:

    return new Future(function(future)
    {
      if (!uri || typeof(uri) !== "string")
      {
          resolver.reject(new TypeError("Invalid uri: " + uri));
          return;
      }
      if (!department || typeof(department) !== "string")
      {
          resolver.reject(new TypeError("Invalid department: " + department));
          return;
      }
          ...
    }

Need a way to synchronously resolve a Future

For background and details, see
http://lists.w3.org/Archives/Public/www-dom/2013JanMar/0202.html
http://lists.w3.org/Archives/Public/www-dom/2013JanMar/0205.html

In short, in some cases we need the ability to resolve a Future synchronously. In particular, for some APIs it's important that the callback which indicates success/failure happens at a particular instance.

For Event dispatch, it's important that the callback is called during the actual event dispatch. Otherwise doing things like calling Event.preventDefault() or Event.stopPropagation() will have no effect.

There will likely be other similar scenarios. I could definitely imagine that we'll run into situations when we'd want to do something like:

function topLevelCallback() {
  setState1();
  resolver1.accept(...);
  setState2();
  resolver2.accept(...);
  setState3();
}

where topLevelCallback is called as a separate microtask and where we want the callbacks which are triggered by resolver 1 to see "state 1" and the callbacks triggered by resolver 2 to see "state 2".

Ideally the cases when we need to synchronously resolve a Future will be rare and will only happen when we're basically at the top of the callstack. Synchronously dispatching Events is an obvious exception and really is a wart, but one that we're many years too late to fix.

Making this repo public...when?

I think we're pretty close to consensus on the most pressing issues (modulo #11, the MOST IMPORTANT ISSUE OF ALL). The polyfill and tests aren't done and I won't likley have time to get to them until next week at the earliest. What is the feeling of those who have been paying attention to this repo about opening it up now? Is there a compelling reason to wait any longer?

State-distinguishing comment is no longer accurate

Given that errors or values can be undefined or null, this comment is no longer accurate. The state property is probably sufficient to distinguish.

// markm points out that we need to be able to distinuish between all
// available states:
//
// pending (cancelled == false && value == null && error == null)
// accepted (cancelled == false && value != null)
// rejected (cancelled == false && value == null && error != null)
// cancelled (cancelled == true);

Bikeshed: `Future` or `DOMFuture`?

The current design squats on the name Future, potentially precluding others from defining it sanely in ES5 contexts. This is potentially problematic as one goal of this proposal is to allow ES7 to specify a subset of the behavior we outline in a JS-standard Future type. Should we move Future to DOMFuture?

Nullable onaccept

We currently have:

Future then(optional AnyCallback onaccept, optional AnyCallback onreject);

My reading of WebIDL is that the above does not allow you to call:

fut.then(null, errCB);

("Web IDL operations do not support being called with omitted optional arguments unless all subsequent optional arguments are also omitted.")

I know that there's catch for that case, but it can lead to unpleasant code when you're getting the callbacks from somewhere else. To give an example, I bumped into that issue recently in which I had to support an interface that could accept success and error callbacks and needed to return a future. I ended up with the equivalent of:

function (foo, successCB, errorCB) {
    var fut = new Future();
    if (errorCB && !successCB) fut = fut.catch(errorCB);
    else if (successCB) fut = fut.then(successCB, errorCB);
   return fut;
}

Not dreadful, but ugly and annoying. Instead we could have:

Future then([TreatUndefinedAs=Null] optional AnyCallback? onaccept, [TreatUndefinedAs=Null] optional AnyCallback? onreject);

Or something along those lines.

New WebIDL notation for DOMFuture

Having documented a bunch of WebAPIs implemented in FirefoxOS, I've noticed that a lot of methods return a DOMRequest. But nobody cares. What people care about is not the value they get immediatly, but the value the get eventually. It would be good if WebIDL was expanded to accept the following notation:

interface I{
    DOMFuture<File> getFile(in DOMString name);
}

I'm not 100% sure it's relevant for this repo, but it'd be good to keep this in mind.

handleEvent support

If Future will pass event object as a first parameter to callback

EventTarget.prototype.once = function(eventType, options) {
   var self = this;
   return new Future(function (resolver) {
     self.on(eventType, options, function(event)) {
       resolver.accept(event);
     });
   });
 };

I suggest this:

EventTarget with DOM Future someway, somehow, should support handleEvent. Right now I can write:

var model = {
    handleEvent: function(e) {
        var handlerName = "$" + e.type;

        if( handlerName in this ) {
            return this[handlerName](e);
        }
    }

    , $click: function(e) {
        console.log(e.type)
    }

    , $mouseover: function(e) {
        console.log(e.type)
    }

    , init: function(node) {
        for( var eventName in this ) {
            if( eventName[0] == "$" ) {
                node.addEventListener(eventName.substr(1), this);
            }
        }
    }
}

model.init(document.find(".some_node"));

or

var xhrController = {
    handleEvent: function(e) {
        switch(e.type) {
            case "load":
                //do something
            break;
            case "abort":
                //do something
            break;
            default:
                console.log(e.type, e);
        }
    }

    , init: function(xhr, url) {
        this.xhr = xhr;
        this.url = url;
        ["load", "error", "abort", "loadend", "loadstart", "progress", "timeout"].forEach(function(name) {
            xhr.addEventListener(name, this);
        }, this);
    }

    , send: function(data) {
        xhr.open("POST", this.url);
        xhr.send(data);
    }
}

xhrController.init(new XMLHttpRequest({anon: true}), "//example.com/");

or even

var rootClickEvents = [];
document.addEventListener("click", rootClickEvents);

//somewhere in script

rootClickEvents.handleEvent = function(e) {
    this.forEach(function(handler) {
        handler.call(e.currentTarget, e);
    })
}

//...
rootClickEvents.push(function(e) {
    console.log(e.type, e);
});

In DOMFuture proposal it doesn't even mention the handleEvent.

I think event object should contains a Future/Promise state information.

Fast forwarding un-handled values

"Chaining fast-forwards un-handled values to the end of the chain." - is it always the end of the chain?

asyncTask().then(function(val, nextResolver) {
    throw new Error("BAM!");
}).then(function() {
    console.log('Ok 1')
}).then(function() {
    console.log('Ok 2');
}, function() {
    console.log('Error 1');
}).done(function() {
    console.log('Ok 3');
}, function() {
    console.log('Error 2');
});

What gets logged here?

Please tag releases

Hi,

I'd like to publish this Javascript file using http://www.webjars.org/. In order for users to reference your library, I need to introduce some sort of version number or tag.

Is it possible for you to tag future releases?

Let's resolver return a dispose function

While implementing Promises i had some problems on making resources eligible for garbage collection. The typical use cas is the Promise.some method. Let's consider this code :

var p= Promise.some(
    new EventPromise(document,'click'),
    new EventPromise(document,'keypress'),
    new EventPromise(document,'touch')
).then(function() {
    // start my app
});

Internally, the EventPromises are registering callbacks to the DOM document. So, it's callbacks will stay in memory during all the application life cycle. Knowing that the promises are unique operations, it could lead to important memory consumption for applications. XHRPromises could lead to very important memory leaks cause they often retrieves a huge amount of datas.

That's why i think we should let the resolver function return a "disposer" function that would let the Promise.some method dispose cancelled Promises.

The main advantage of this method is it keeps backward compatibility while it allows the dispose function to be propagated througth promises trees.

It implies 2 things :

  • keeping promise callbacks synchronous to be able to dispose other promises in a Promise.some before any other event can be fired.
  • Execute promise callbacks in their registration order for users beeing able to predict which promises will be disposed and when.

Thanks for reading.

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.