promises-aplus / constructor-spec

Discussion and drafts of a possible spec for creating and resolving promises
10 stars 4 forks source link

Strawman: Promise Creation API/B #13

Closed domenic closed 11 years ago

domenic commented 11 years ago

Terminology

  1. "Settled" means either fulfilled or rejected.

    The Promise Constructor

var promise = new Promise(factory);
  1. Can be called without new, with the same results.
  2. promise instanceof Promise must be true.
  3. Object.getPrototypeOf(promise) === Promise.prototype must be true.
  4. promise.constructor === Promise.prototype.constructor === Promise must be true.
  5. If factory is not a function, the implementation must throw a TypeError.
  6. If factory is a function, it must be called immediately (i.e. within the same turn of the event loop), with a resolver. (note 1)

    The Resolver

var promise = new Promise(function (resolver) {
  // ..
});

The resolver is a function with several properties, which are also functions.

  1. Resolver functions must not be dependent on their this value.

    resolver(x)

  2. If x is a non-promise,
    1. If promise is pending, promise must be fulfilled with x as its fulfillment value.
    2. If promise is settled, nothing happens (in particular, no exception may be thrown).
  3. If x is a promise, promise must assume the state of x (see Promises/A+ spec).

    resolver.fulfill(value)

  4. If value is a non-promise,
    1. If promise is pending, promise must be fulfilled with value as its fulfillment value.
    2. If promise is settled, nothing happens (in particular, no exception may be thrown).
  5. If value is a promise, the implementation must throw a TypeError.

    resolver.reject(reason)

  6. If reason is a non-promise,
    1. If promise is pending, promise must be rejected with reason as its rejection reason.
    2. If promise is settled, nothing happens (in particular, no exception may be thrown).
  7. If reason is a non-promise, the implementation must throw a TypeError.

    resolver.yield(otherPromise) [normative optional? cut entirely?]

  8. If otherPromise is a promise,
    1. If promise is pending, promise must assume the state of otherPromise (see Promises/A+ spec).
    2. If promise is settled, nothing happens (in particular, no exception may be thrown).
  9. If otherPromise is not a promise, the implementation must throw a TypeError.

    Notes

  10. Note that if factory throws an exception, since it is called within the same turn of the event loop as new Promise, promise will never be successfully created and the error will be uncaught by the implementation. [is this note even necessary?]
domenic commented 11 years ago

This strawman is based on a few guiding ideas:

ForbesLindesay commented 11 years ago

I don't like Notes-1:

Consider this function:

function doAsyncWork() {
  return new Promise(function (resolver) {
    var resA = doSynchronousPreparationWhichMightThrowAnException();
    resolver.resolve(doAsyncWork(resA));
  });
}

If you want to call it and be sure of handling all errors you need code to handle both synchronous and asynchronous errors, this is really bad.

Otherwise this is mostly fine. I'd be inclined to cut yield entirely. Especially given it's potential confusion with the ES6 yield keyword.

bergus commented 11 years ago

I strongly oppose points 3 and 4. This would prevent any inheritance approaches (and this constructor proposal is all about inheritance, isn't it?). While I really like instanceof, settling the [[prototype]] makes it unnecessary limited.

For example, in my https://github.com/bergus/F/blob/master/Promise.js I have implemented a Stream to be a "subclass" of Promise (basically with chunked fulfillments).

My proposal would be to omit point 3 (2 should be enough), and change 4 to:

PS: Didn't we forget something substantial (point 0 or so):

juandopazo commented 11 years ago

I think points 3 and 4 only refer to a promise created from var promise = new Promise(fn).

bergus commented 11 years ago

OK, that's good (though it excludes implementations that want to return subclass instances from the Promise constructor, maybe dependent on optional further arguments). However I fear it could confuse people (just as it confused me) and make them to (ab-)using these properties for type checking (especially the constructor thing).

Still they don't seem really necessary to me, it would be nice to add at least some reasoning. Also, point 3 should be fixed to

Object.getPrototypeOf(promise) === Promise.prototye // encloses Point 2, btw
ForbesLindesay commented 11 years ago

Promise can be substituted for whatever library name you want to give it in this spec, so you would just need to ensure chunkifiedPromise.constructor === ChunkifiedPromise.prototype.constructor === ChunkifiedPromise etc.

domenic commented 11 years ago

Killing this in favor of #18, mainly because it contains terminology and behavior confusion around "resolved" and "settled". If someone wants something more like this, with argument-type checking, but with #18's not-confused resolution process, we can create a new draft.