Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assimilation as a construction strategy?? #15

Open
domenic opened this issue Feb 10, 2013 · 7 comments
Open

Assimilation as a construction strategy?? #15

domenic opened this issue Feb 10, 2013 · 7 comments

Comments

@domenic
Copy link
Member

domenic commented Feb 10, 2013

This came out of a discussion with MarkM and @kriskowal. It seems promising.

Start from this formulation of the "promise constructor" idea we've seen in many other issues so far:

var promise = new Promise(function (resolve, reject) {
  resolve(5);
});

Imagine if instead we specced a thenable assimilator, we'll call it when. Then we could instead accomplish the above with:

var promise = when({ then: function (resolve, reject) {
  resolve(5);
} });

This is between one and three more characters, depending on if you prefer {x:y} or { x: y }.

ES6 style:

let promise = new Promise((resolve, reject) => resolve(5));
let promise = when({ then(resolve, reject) => resolve(5) });

This time between -1 and +1 character deltas.

Q of course gets it easy, because our assimilator would be called Q, making it a win on characters in every case.


Important note: then must be executed in the next turn of the event loop to prevent malicious thenables from breaking execution flow. (On the plus side, this means that any calls to resolve or reject can synchronously inform the returned promise, since we're already in the next tick.)


Alternately we could spec both assimilation and construction at the same time, given an equivalence between two. Maybe new Promise((resolve, reject) => { ... }) is equivalent to Promise.from({ then(resolve, reject) => { ... })?

@ForbesLindesay
Copy link
Member

I like it. Things to consider:

  1. How does extending to support progress look
  2. How does extending to support cancellation propagation look
  3. Behavior when then throws an error
  4. Is resolve polymorphic

My thoughts:

1: Simple, lots of implementations already provide an onProgress argument to then.

2: Could be as simple as:

let promise = when({ then(resolve, reject) => resolve(5),
                     cancel() => xhr.abort() });

3: I think it should be equivalent to calling reject

4: not sure yet

@ForbesLindesay
Copy link
Member

The more small failings I see with other ideas the more I like this one. Everything that's really hard to get right for any other spec is really easy with this one.

I still think it should be a constructor, so that:

promise instanceof Promise

is always true.

let promise = new When((resolve, reject) => resolve(5));
let promise = new When({ then(resolve, reject) => resolve(5) });

@briancavalier
Copy link
Member

This is definitely interesting, and I'm personally a fan of the functional style.

I believe that when.js and Q are both effectively compliant with this proposal.

Given that each library will have their own Promise constructor, what are the near-term advantages of promise instanceof Promise over the standard duck type test? Or is the primary benefit looking (waaay) ahead to the day when there is some official Promise constructor?

@domenic
Copy link
Member Author

domenic commented Feb 10, 2013

The advantage of the promise constructor is as in #5: it allows utility libraries to create promises that match those of the implementation in use. I.e., in this case:

function timeout(promise, time) {
  var Promise = promise.constructor;

  return new Promise({ then: function (resolve, reject) {
    promise.then(resolve);
    setTimeout(function () {
      reject(new Error('Operation timed out.'));
    }, time);
  } });
}

@domenic
Copy link
Member Author

domenic commented Feb 10, 2013

I think it's a bit awkward to have the constructor use the assimilation signature, though, thus what I proposed in #18.

@ForbesLindesay
Copy link
Member

I really only want the constructor stuff for discoverability, and perhaps we should do that another way and in another spec. We'd still have real problems deciding whether the constructor we'd found was a Resolvers/A+ constructor anyway.

Provided people agree with me that: when then throws an error we turn that into a rejection I'm fully in favor of going forward with just specifying assimilation as our only construction method (assuming we can get buy in from the library authors).

@novemberborn
Copy link

Even though I understand how this would work, it seems too weird a concept creating promises through assimilation. "I have to call when(), and pass it an object that has a then(), but I'm not calling then(), it gets called for me… argh just let me have defer()!"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants