Where'd Promise#done go?
The logging solution proposed is not polyfillable with today's tools, at least not when logging to the browser console.
The idea would be that rejection reasons are logged when nobody has handled them, but then "un-logged" when they are handled. Since there is no console.unlog, you see our problem.
You could "polyfill" this by creating a secondary "console" overlay on top of the browser window. I believe when.js has something like this in an experimental branch:
cujojs/when/commit/65f89e9eeb397186aa1508bc58e49653aca2fcb4
Theirs is based off of a proposed Promises/A+ API, console.unhandledRejection and console.rejectionHandled, which would allow cross-promise-library sharing of such an interface:
promises-aplus/unhandled-rejections-spec#2
I know RSVP.js has expressed interest in this as well, and I speak for Q in saying we would love that. At this point it's just a matter of someone, perhaps myself, putting in the time to create a generic bookmarklet that pops up a unhandled-rejections console and intercepts console.unhandledRejection/console.rejectionHandled.
Le 18/06/2013 23:49, Chad Austin a écrit :
Hi all,
Hoping to proactively polyfill likely parts of upcoming standards, I am adding an implementation of DOM promises to IMVU's base JavaScript library.
Why was done() removed from the draft spec? " Rename Futures per TC39 discussion. Drop done() per same." whatwg/dom/commit/7a741b953990a89c6d27532928fe7817c3b25528
I read that there is an expectation that promise implementations will log when an error goes unhandled, but how is that possible?
To log when an error goes unhandled, an implementation would have to log when a promise has state=rejected and no reject callbacks, AND that the promise has been dropped on the floor. Without weak reference callbacks, how could you know that a promise has been GC'd?
You can't. That's a part that can't be polyfilled today indeed. This feature requires native promises.
As Domenic mentions, there will be no place for "done" in our bright promise debugger future.
It will however be necessary for promise users to keep on ending their chains with "done()" until promise debuggers are ubiquitously available. This is a simple problem. If you are writing code that targets both old and new engines, the promise polyfill will simply have to patch a no-op "done" onto the engine’s Promise.prototype.
Kris Kowal
I don't understand this. I am onboard with
console.unhandledRejection/console.rejectionHandled
and all that for better logging, and with using WeakRef notification to
improve the logging yet further. But I don't see how any of this can
substitute for the need that .done() serves. I think we will still need
.done() in ES7 promises.
Note: I didn't have .done() in E, but that's only because I didn't think of it. I did have the problem that .done() addresses and I would have found it useful.
I am of the opinion that program errors should fail loudly by default, which the current proposal does not support.
To this end, in my own promise implementation I use a form of "fail soon", like so:
Define a promise tree. As a base case we have a promise created with the Promise constructor. Leaves are added to the tree by using the "then" method. The "promise forest" is the collection of all such trees for a running program. An "invalid promise tree" is a tree which contains a rejected leaf node which is not also a root. During a "callback flush", the callback queue is repeatedly emptied until no more callbacks remain. At the end of the callback flush, the promise forest is checked for any invalid promise trees. If there exists an invalid promise tree, then an unhandled error is thrown (which will of course propagate to window.onerror).
It's worked well for me so far, and has obviated the need for bolt-ons like "done" or "console.unlog", etc. YMMV.
From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Kevin Smith
To this end, in my own promise implementation I use a form of "fail soon", like so:
It sounds like this does not support handling rejections in an event loop turn after they are generated, e.g. it would disallow
var rejected = Promise.reject(new Error("bad news!"));
var fulfilled = Promise.resolve(5);
fulfilled.then(() => {
rejected.catch(err => console.error(err));
});
Is that correct?
From: Mark S. Miller [mailto:erights at google.com]
I don't understand this. I am onboard with
console.unhandledRejection/console.rejectionHandledand all that for better logging, and with using WeakRef notification to improve the logging yet further. But I don't see how any of this can substitute for the need that .done() serves. I think we will still need .done() in ES7 promises.
While I think I see what you're getting at, let me play devil's advocate for a bit to draw this out more clearly. Using the sample code from promises-aplus/unhandled-rejections-spec/issues/1:
var rejectPromise;
var promise = new Promise((resolve, reject) => rejectPromise = reject);
promise.then(() => console.log("I only attached a handler for fulfillment"));
// All is OK (A)
rejectPromise(promise, new Error("who handles me?"));
// Nobody sees the error! Oh no, maybe we should crash here? (B)
setTimeout(function () {
promise.then(undefined, (err) =>console.error("I got it!", err));
// But if we crashed there, then how would this code ever get run? (C)
}, 5000);
Using a done-less promise implementation with a unhandled rejections console, we have the flow that:
- At line (A), all is fine, and the unhandled rejections console is empty.
- At line (B), the unhandled rejections console contains
Errror: "who handles me?". This remains true for the next five seconds. - At line (C), after five seconds have passed, the unhandled rejections console becomes yet again empty.
This seems to neatly solve the problem without done, at least in my devil's-advocate world. Where's the problem? :)
On Tue, Jun 18, 2013 at 8:11 PM, Domenic Denicola < domenic at domenicdenicola.com> wrote:
From: Mark S. Miller [mailto:erights at google.com]
I don't understand this. I am onboard with
console.unhandledRejection/console.rejectionHandledand all that for better logging, and with using WeakRef notification to improve the logging yet further. But I don't see how any of this can substitute for the need that .done() serves. I think we will still need .done() in ES7 promises.While I think I see what you're getting at,
What do you think I'm getting at? ;)
I've often looked at Promise#then() as sugar over Promise#done() for something like:
Promise.prototype.then = function(resolve, reject) {
return new Promise(resolver => {
this.done(
value => {
try {
resolver.resolve(resolve ? resolve(value) : value);
}
catch (e) {
resolver.reject(e);
}
},
err => {
try {
resolver.resolve(reject ? reject(value) : value);
}
catch (e) {
resolver.reject(e);
}
});
});
}
Promise#done() doesn't have the overhead that Promie#then does (allocating a new chained Promise), so it is more efficient if you don't need to chain. It feels easier to be more explicit with done then without, since to polyfill done requires calling something like setImmediate to raise the error to the engine/window.onerror, since throwing it in the reject handler would just result in a new rejected Promise.
If we had an 'await' keyword I might find the need for done to be less important, as it would be easy to write "await p" to bubble the exception to user code. Although, 'await' would also be more efficient with 'done' rather than 'then'.
If Promise#done is out, a polyfill could just have an array of unhandled exceptions that could be analyzed programmatically in user code or via the console.
From: Mark S. Miller [mailto:erights at google.com]
What do you think I'm getting at? ;)
Heh. In short, non-browser environments.
var rejected = Promise.reject(new Error("bad news!")); var fulfilled = Promise.resolve(5);
fulfilled.then(() => { rejected.catch(err => console.error(err)); });
In my implementation, this works for two reasons. First, "root" promises
(i.e. not created via then) such as rejected above are not "throwable".
Second, errors are not thrown until after the callback queue has been repeatedly flushed, until empty. Both arrow functions above would execute before the next checkpoint.
This, however, would crash the program:
Promise.reject(new Error("bad news!")).then(x => x);
As would this:
var rejected = Promise.reject(new Error("bad news!")).then(x => x);
setTimeout($=> rejected.catch(err => console.log(err)), 0);
I use promises a lot, and (for all valid points stated above) I see done as
a must have for implementation to be practically usable.
Also it needs to be provided natively, as it's not possible to shim it just
with then.
Proposed log/un-log in background mechanism doesn't solve the issue, as
you're still left with overhead of then and it complicates error handling
which with done is straightforward. I hope it won't land in any spec.
-- View this message in context: mozilla.6506.n7.nabble.com/Where-d-Promise-done-go-tp281461p281493.html Sent from the Mozilla - ECMAScript 4 discussion mailing list archive at Nabble.com.
On Wed, Jun 19, 2013 at 3:28 AM, Alex Russell <slightlyoff at gmail.com> wrote:
On Wednesday, June 19, 2013, Ron Buckton wrote:
I’ve often looked at Promise#then() as sugar over Promise#done() for something like:
Promise.prototype.then = function(resolve, reject) { return new Promise(resolver => { this.done( value => { try { resolver.resolve(resolve ? resolve(value) : value); } catch (e) { resolver.reject(e); } }, err => { try { resolver.resolve(reject ? reject(value) : value); } catch (e) { resolver.reject(e); } }); }); }Promise#done() doesn’t have the overhead that Promie#then does (allocating a new chained Promise), so it is more efficient if you don’t need to chain.
That is my only latent argument for #done(), and one that I think I agree with Luke to re-visit at some later date. We can always add.
What was less clear is that there's any real problem with error handling: polyfills can (should?) keep lists of unhandled promises and make them available to tools while we wait for devtools to surface the list. The concerns here with direct logging seem, to me, to be premature.
Regarding prematurity, I said "I think we will still need .done() in ES7 promises." I do not think this is something that DOMPromises need to address prior to the careful ES7 promise work still in front of us. Since, as you and Luke say, "We can always add", DOMPromises only needs be approximately the minimum we need quick agreement on, so that we can add the rest of what's needed in the ES7 process. This they seem to be, which is great.
From: Alex Russell [slightlyoff at gmail.com]
On Wednesday, June 19, 2013, Ron Buckton wrote:
Promise#done() doesn’t have the overhead that Promie#then does (allocating a new chained Promise), so it is more efficient if you don’t need to chain.
That is my only latent argument for #done(), and one that I think I agree with Luke to re-visit at some later date. We can always add.
Given the heroics browsers pull for optimizing unused return values already, this argument never made much sense to me. E.g. Brendan has earlier mentioned SpiderMonkey taking a different code path if it sees if (regExp.exec(...)), which only returns a truthy result instead of computing the entire array. It seems like a trivial optimization to notice that nobody's using the return value of then and not create a promise to return.
On Wed, Jun 19, 2013 at 7:01 AM, Mark S. Miller <erights at google.com> wrote:
On Wed, Jun 19, 2013 at 3:28 AM, Alex Russell <slightlyoff at gmail.com>wrote:
What was less clear is that there's any real problem with error handling: polyfills can (should?) keep lists of unhandled promises and make them available to tools while we wait for devtools to surface the list. The concerns here with direct logging seem, to me, to be premature.
Last night I converted a bunch of our continuation-passing code to use promises, so now I have both implementation and usage experience with the current promise proposal. Inadvertent error swallowing and incomplete error bubbling were the primary pain points. I frequently found myself with typos and mistakes that became opaque TypeErrors by the time they reached the browser debugger or error log.
A DOM promise polyfill, in current web browsers*, cannot provide the same level of error handling and reporting as we've come to expect in traditional onload/onerror asynchronous code. Consider:
Promise.fulfill({}).then(function(x) { //... x.oops_not_a_function(); //... });
A naive polyfill would swallow ReferenceError or TypeError and hide it completely. So let us suppose that the polyfill keeps lists of unhandled rejections. How does the promise debugger know whether promises are still live and simply don't have reject callbacks configured yet? In addition, by the time the error in inspected, the stack trace is gone as well as any activation records, so even though Chrome and Firefox provide Error#stack, there's no way to inspect into 'x'. This is reproducible by bubbling the error with setTimeout:
function bubbleToBrowser(e) { setTimeout(function() { throw e; }, 0); }
Promise.reject(new Error("will_bubble")).catch(function(e) { bubbleToBrowser(e); });
Or its fundamental equivalent:
try {
({}).not_a_function();
}
catch (e) {
setTimeout(function() {
throw e;
}, 0);
}
It's very hard in practice to trace the error back to its original cause. There are two issues here:
- Something like Promise#done allows idiomatically expressing "No really, that's it, don't chain anymore, and bubble errors into the event loop"
- Bubbling errors to the main loop with useful amounts of information is not possible in current browsers and JavaScript.
In the meantime, I will likely add an option to our Promise implementation, and perhaps default it on, that makes no attempt to catch errors thrown in Promise callbacks, guaranteeing they reach the event loop at the time they're thrown.
Thanks, Chad
- I am happy to live with limitations of current web browsers as long as there is a clear plan to solve them.
I've been answering quite a few questions about promises on stack overflow lately. One of the key things people seem to struggle to get their head around is the idea of .then as being something that transforms the promise and returns a new promise. They either expect it to mutate the existing promise or they expect it to behave like .done() does.
I think .done() could be extremely useful purely as a teaching device. If we started everyone off by learning to use .done() they would have a much shallower learning curve. Initially they'd get all their errors thrown immediately which would be easier to see. It would be much more similar to typical (but terrible) DOM APIs and jQuery APIs that are event based or have a callback and an errback. Having learnt to use .done() we could teach .then() as a more advanced feature that let you compose asynchronous operations by returning a promise that has been transformed by the callbacks.
Stating then() is something more advanced than done() doesn't make much
sense to me.
They're different methods, that serve different purpose, I think they should
be valued on a similar level.
Key thing is that then() is conceptually a map(), and there's
something wrong if just to access the value we have to use map(). Second
issue, mentioned already numerous times is not desired (in case of "just
access") error suppression.
Le 20/06/2013 14:55, Forbes Lindesay a écrit :
I've been answering quite a few questions about promises on stack overflow lately.
Do you have a link to a list to these questions (and/or your answers) off-top your browser history by any chance?
One of the key things people seem to struggle to get their head around is the idea of
.thenas being something that transforms the promise and returns a new promise. They either expect it to mutate the existing promise or they expect it to behave like.done()does.
I wasn't there when that started, but it feels like "then" organically grew out of the experience of using promises a lot which naturally leads to promise pipelining. It doesn't feel like the most "fundamental brick" to understand what promises are (what .done looks like though), because it isn't. At a first approximation, people can use .then the way the expect (without caring for the return value)
I think
.done()could be extremely useful purely as a teaching device. If we started everyone off by learning to use.done()they would have a much shallower learning curve. Initially they'd get all their errors thrown immediately which would be easier to see.
That's how Q behaves out of necessity, but native promises can have better integration with debugging tools and don't need to reflect the error at runtime.
It would be much more similar to typical (but terrible) DOM APIs and jQuery APIs that are event based or have a callback and an errback.
Having learnt to use.done()we could teach.then()as a more advanced feature that let you compose asynchronous operations by returning a promise that has been transformed by the callbacks.
.then can be taught without telling it returns something ;-)
On Thu, Jun 20, 2013 at 7:50 AM, David Bruant <bruant.d at gmail.com> wrote:
Le 20/06/2013 14:55, Forbes Lindesay a écrit :
I’ve been answering quite a few questions about promises on stack overflow lately.
Do you have a link to a list to these questions (and/or your answers) off-top your browser history by any chance?
One of the key things people seem to struggle to get their head around is the idea of
.thenas being something that transforms the promise and returns a new promise. They either expect it to mutate the existing promise or they expect it to behave like.done()does.I wasn't there when that started,
I was ;)
but it feels like "then" organically grew out of the experience of using promises a lot which naturally leads to promise pipelining. It doesn't feel like the most "fundamental brick" to understand what promises are (what .done looks like though), because it isn't. At a first approximation, people can use .then the way the expect (without caring for the return value)
.then comes directly from E's "when", which comes from Original-E's "when", which comes from experience with Joule. See Concurrency Among Strangers < www.erights.org/talks/promises/paper/tgc05.pdf> or the expanded and
updated version in Part 3 and Chapter 23 of < erights.org/talks/thesis/markm-thesis.pdf>. For the history
especially, see that Chapter 23 -- "From Objects to Actors and Back Again".
I'm always surprised when people think that any of these callback forms are the fundamental starting point. The fundamental starting point is the asynchronous message send, expressed in E as infix "<-", in Q as .send, and proposed for ES7 as infix "!". In both E and Joule, .then-like callbacks were built as a pattern on top of message sending.
On the particular issue in question, the return value of .then, historically you are correct. Original-E and early E had a "when" that did not return a promise for the result. That idea was contributed by Mark Seaborn on the e-lang list, I believe sometime in the late '90s, but I haven't yet found the crucial message in the archive < www.eros-os.org/pipermail/e-lang>.
This change made a fundamental difference in the style of using "when". Without the return result, the only purpose of "when" is the side-effects performed by the callbacks, which necessarily takes you out of functional thinking into imperative thinking. With the return result, "when" is still potentially functional. As with lazy evaluation, when used functionally, both asynchronous message sending and "when" are just ways of rearranging the time of functional computation.
So the question about the order of teaching of .done vs .then depends on the overall teaching strategy. Some teaching strategies, such as SICP and CTM, teach functional first and only get to imperative constructs rather late. Prior to imperative constructs, .done makes sense only for its error reporting, when capping a promise sequence -- which, after all, was the reason for introducing .done in the first place. For this purpose, I recommend teaching only the parameterless form: p.done(); But I do recommend teaching it, as dropping errors silently is especially harmful to someone first learning a new programming paradigm.
On Thu, Jun 20, 2013 at 7:50 AM, David Bruant <bruant.d at gmail.com> wrote:
Le 20/06/2013 14:55, Forbes Lindesay a écrit :
I’ve been answering quite a few questions about promises on stack overflow lately.
Do you have a link to a list to these questions (and/or your answers) off-top your browser history by any chance?
One of the key things people seem to struggle to get their head around is the idea of
.thenas being something that transforms the promise and returns a new promise. They either expect it to mutate the existing promise or they expect it to behave like.done()does.I wasn't there when that started, but it feels like "then" organically grew out of the experience of using promises a lot which naturally leads to promise pipelining.
I'm worried that you may be suffering from and spreading a terminology confusion. "Promise pipelining" is an important latency reduction optimization when using promises over a network. See Chapter 16 of < erights.org/talks/thesis/markm-thesis.pdf>. Using .then, either with
or without the return result, prevents promise pipelining, which is another reason to emphasize asynchronous message sending and deemphasize .then.
Do you have a link to a list to these questions?
Here are a few examples:
- how to make-sequential rest webservices calls with angularjs
- Why does my Q chained promise rejection not behave the way I expect?
- Chaining 2 asynchronous calls to run serially
You can see many more questions by selecting the promises tag on stack overflow.
native promises can have better integration with debugging tools
In deed they can, there are issues though. We will never be able to have the application crash in the event that an unhandled rejection occurs. That's no big deal for browsers, where the idea of carrying on with a best effort attempt is so ingrained anyway, but it's a significant issue for node.js and WinJS apps. In both those cases I don't really look at the logs unless I know something's gone wrong, which might lead to bugs going undiscovered for a long time.
I think it's important that there be a way to say, "If this rejection is not handled, crash my application".
I'm always surprised when people think that any of these callback forms are the fundamental starting point.
They aren't the fundamental starting point from either a conceptual point of view or an implementation point of view. However, most people in the short term will start out with knowledge of either callbacks (if they come from node.js) or the weird event style onSuccess and onFail handlers (if they come from the browser). This means that most of the time when we teach promises we're going to have to teach it with that as the starting point of knowledge, not with message sending as the starting point.
I'm worried that you may be suffering from and spreading a terminology confusion. "Promise pipelining" is an important latency reduction optimization when using promises over a network. See Chapter 16 of erights.org/talks/thesis/markm-thesis.pdf. Using .then, either with or without the return result, prevents promise pipelining, which is another reason to emphasize asynchronous message sending and deemphasize .then.
I couldn't imagine why you would think that using .then would prevent promise pipelining. A properly designed, monadic .then, is nothing but a more powerful let. Perhaps you could elaborate?
Naively translating the standard pipeline example gives
x.a().then( t1=>
y.b().then( t2=>
t1.c(t2).then( t3=>
... ) ) )
where x, y, t1, and t2 are meant to live on the same remote machine, computation is meant to proceed without delay into the ... part, and the implementation is meant to take care of avoiding unnecessary round-trips for the intermediate results.
This is naïve because the synchronous method calls should really be asynchronous message sends. If we assume local proxies that forward local method calls to remote objects and remote results to local callbacks, then y.b() will not start until t1 comes back.
But if t1 is itself a promise, then it can come back immediately, and the same applies to t2 and t3. So computation can proceed directly to the ... part, with delays happening only when required by data dependencies.
A user-level promise will not have the same opportunities for network traffic optimization as an implementation-level future (an obvious one would be moving the callback code to where the data is), but the .then itself does not prevent optimizations.
Unless, that is, one insists on flattening promises (no promises passed to .then-callbacks), which would sequentialize the chain...
What am I missing here? Claus
2013/6/20 David Bruant <bruant.d at gmail.com>
I wasn't there when that started, but it feels like "then" organically grew out of the experience of using promises a lot which naturally leads to promise pipelining.
Terminology nit: if by "promise pipelining" you mean the fact that p.then() returns a new promise dependent on p, I would call that "promise chaining". In the two languages that established promises, i.e. Argus and E, the term "promise pipelining" specifically refers to minimizing network round-trips to reduce latency when using promises combined with RPCs. See < www.erights.org/elib/distrib/pipeline.html>.
Sorry for the noise. I didn't see Mark's reply while offline.
On Thu, Jun 20, 2013 at 9:29 AM, Forbes Lindesay <forbes at lindesay.co.uk>wrote: [...]
I'm always surprised when people think that any of these callback forms are the fundamental starting point.
They aren't the fundamental starting point from either a conceptual point of view or an implementation point of view. However, most people in the short term will start out with knowledge of either callbacks (if they come from node.js) or the weird event style onSuccess and onFail handlers (if they come from the browser). This means that most of the time when we teach promises we're going to have to teach it with that as the starting point of knowledge, not with message sending as the starting point.
Any beginning JavaScript programmer learns the meaning of "a.foo(b,c)", i.e., synchronous message send, long before they learn about callbacks.
On Thu, Jun 20, 2013 at 10:34 AM, Claus Reinke <claus.reinke at talk21.com>wrote:
I'm worried that you may be suffering from and spreading a terminology
confusion. "Promise pipelining" is an important latency reduction optimization when using promises over a network. See Chapter 16 of <erights.org/talks/**thesis/markm-thesis.pdferights.org/talks/thesis/markm-thesis.pdf>. Using .then, either with or without the return result, prevents promise pipelining, which
is another reason to emphasize asynchronous message sending and deemphasize .then.
I couldn't imagine why you would think that using .then would prevent promise pipelining. A properly designed, monadic .then, is nothing but a more powerful let. Perhaps you could elaborate?
Naively translating the standard pipeline example gives
x.a().then( t1=> y.b().then( t2=> t1.c(t2).then( t3=> ... ) ) )
where x, y, t1, and t2 are meant to live on the same remote machine, computation is meant to proceed without delay into the ... part, and the implementation is meant to take care of avoiding unnecessary round-trips for the intermediate results.
This is naïve because the synchronous method calls should really be asynchronous message sends. If we assume local proxies that forward local method calls to remote objects and remote results to local callbacks, then y.b() will not start until t1 comes back.
But if t1 is itself a promise, then it can come back immediately,
I think this is what you are missing. If x.a() returns, for example, an int, then x!a() returns a promise that will turn out to be a promise-for-int. In that case, x!a().then(t1 => ...t1...), the callback
will only be invoked with t1 bound to the int itself. This can't happen prior to the completion of the round trip.
and the same applies to t2 and t3. So computation can proceed directly to the ... part, with delays happening only when required by data dependencies.
A user-level promise will not have the same opportunities for network traffic optimization as an implementation-level future (an obvious one would be moving the callback code to where the data is)
Moving the callback code to where the data is is indeed an important idea. Back when .then was called "when", this was called "where". With the renaming of "when" to "then", "where" is renamed "there" < strawman:concurrency#there>. .there
and .then must be kept distinct because they have very different security properties, as well as very different failure and progress properties under partition.
, but the .then itself does not prevent optimizations.
Unless, that is, one insists on flattening promises (no promises passed to .then-callbacks), which would sequentialize the chain...
I don't see how this is relevant, as the round-trip above is still forced on flatMap. But yes, we do insist on this flattening. That's the difference between .then and .flatMap.
From context, I suspect you're talking about "promise chaining".
Yes, I meant "chaining". Sorry for the confusion.
Naively translating the standard pipeline example gives
x.a().then( t1=> y.b().then( t2=> t1.c(t2).then( t3=> ... ) ) ) .. This is naïve because the synchronous method calls should really be asynchronous message sends. If we assume local proxies that forward local method calls to remote objects and remote results to local callbacks, then y.b() will not start until t1 comes back.
But if t1 is itself a promise, then it can come back immediately,
I think this is what you are missing. If x.a() returns, for example, an int, then x!a() returns a promise that will turn out to be a promise-for-int. In that case, x!a().then(t1 => ...t1...), the callback will only be invoked with t1 bound to the int itself. This can't happen prior to the completion of the round trip.
As I was saying, that restriction is not necessary - it is a consequence of the flatten-nested-promises-before-then-callback philosophy. Instead, the local proxy can send the remote message and locally pass a receiver promise to its callback. That way, the callback can start to run until it actually needs to query the receiver promise for a value.
If we did this for the .a and .b calls, the translation would change to
x.a().then( t1p=>
y.b().then( t2p=>
t1p.then( t1=>
t2p.then( t2=>
t1.c(t2).then( t3=>
... ) ) ) ) )
and the .b call could be triggered before the .a call roundtrip completes. If we want to push the "lazy-evalutation" into the ... part, things get more interesting, as one would need to model the data-dependencies and delay looking at t1p/t2p further. One could define an inline then-able to capture this:
x.a().then( t1p=>
y.b().then( t2p=>
let t3p = { then(cb): { t1p.then( t1=> t2p.then( t2=>
t1.c(t2).then( t3=> cb(t3) ) ) ) };
...' ) )
(where ...' is ..., transformed to work with a promise t3p instead of t3)
Now, waiting for the .a and .b roundtrips would be delayed until some code in ...' actually needs to look at t3. One could further delay looking at t2 if t1.c() could deal with a promise t2p.
This additional flexibility is not available in a flat-promise design, which is why I think such a design is a mistake. Of course, even if one wants to accept the restrictions of a flat-promise design, the flattening should happen in promise construction, not in .then.
Claus
Returning to Mark Miller's comment:
Any beginning JavaScript programmer learns the meaning of
a.foo(b,c), i.e., synchronous message send, long before they learn about callbacks.
I don't see any simple and obvious path from understanding the synchronous message send to understanding how promises work. How does that explanation look?
It seems on the other hand I can see a natural progression from callbacks to .done and from .done to .then because if you write any code using .done it's infinitely obvious that you need .then.
I think it's interesting to also reflect on Tasks in C# which have been around for a while. They don't have any methods for automatically tracking unhandled rejections even though the language supports handling object destruction. All the methods that are built in to the Task object match up with .done rather than .then. This becomes much less of a problem once the language has await or similar.
On Thu, Jun 20, 2013 at 3:19 PM, Claus Reinke <claus.reinke at talk21.com>wrote:
Naively translating the standard pipeline example gives
x.a().then( t1=> y.b().then( t2=> t1.c(t2).then( t3=> ... ) ) ) .. This is naïve because the synchronous method calls should really be asynchronous message sends. If we assume local proxies that forward local method calls to remote objects and remote results to local callbacks, then y.b() will not start until t1 comes back.
But if t1 is itself a promise, then it can come back immediately,
I think this is what you are missing. If x.a() returns, for example, an int, then x!a() returns a promise that will turn out to be a promise-for-int. In that case, x!a().then(t1 => ...t1...), the callback will only be invoked with t1 bound to the int itself. This can't happen prior to the completion of the round trip.
As I was saying, that restriction is not necessary - it is a consequence of the flatten-nested-promises-**before-then-callback philosophy. Instead, the local proxy can send the remote message and locally pass a receiver promise to its callback. That way, the callback can start to run until it actually needs to query the receiver promise for a value.
If we did this for the .a and .b calls, the translation would change to
x.a().then( t1p=> y.b().then( t2p=>
t1p.then( t1=> t2p.then( t2=> t1.c(t2).then( t3=>
This corresponds only to running .a and .b overlapped. Since .c isn't run until both the .a and .b round trips complete, this isn't promise pipelining. This isn't just a matter of definitions. You lose most of the important optimization if all you're doing is overlapping independent requests.
... ) ) ) ) )
and the .b call could be triggered before the .a call roundtrip completes. If we want to push the "lazy-evalutation" into the ... part, things get more interesting, as one would need to model the data-dependencies and delay looking at t1p/t2p further. One could define an inline then-able to capture this:
x.a().then( t1p=> y.b().then( t2p=> let t3p = { then(cb): { t1p.then( t1=> t2p.then( t2=> t1.c(t2).then( t3=> cb(t3) ) ) ) }; ...' ) )
I can't yet respond to this because I don't understand your notation. What does "{ then(cb): " mean?
On Thu, Jun 20, 2013 at 7:03 PM, Mark S. Miller <erights at google.com> wrote:
x.a().then( t1p=> y.b().then( t2p=> let t3p = { then(cb): { t1p.then( t1=> t2p.then( t2=> t1.c(t2).then( t3=> cb(t3) ) ) ) }; ...' ) )
I can't yet respond to this because I don't understand your notation. What does "{ then(cb): " mean?
It seems pretty clear that's intended to be a concise method definition. Claus accidentally added a colon, likely through force of habit.
Wasn't clear to me. But now that you mention it, it does fit. Unless I hear to the contrary from Claus, I will respond on that basis.
Thanks for the clarification.
On Thu, Jun 20, 2013 at 3:19 PM, Claus Reinke <claus.reinke at talk21.com>wrote:
If we want to push the "lazy-evalutation" into the ... part, things get
more interesting, as one would need to model the data-dependencies and delay looking at t1p/t2p further. One could define an inline then-able to capture this:
x.a().then( t1p=> y.b().then( t2p=> let t3p = { then(cb) { t1p.then( t1=> t2p.then( t2=> t1.c(t2).then( t3=> cb(t3) ) ) ) }; ...' ) )
(where ...' is ..., transformed to work with a promise t3p instead of t3)
Now, waiting for the .a and .b roundtrips would be delayed until some code in ...' actually needs to look at t3. One could further delay looking at t2 if t1.c() could deal with a promise t2p.
[colon removed from above quote per Tab's suggestion]
Ok, you've delayed sending the .c until you needed t3p. But that's the opposite of the promise pipelining optimization! The point is not to delay sending .c till even later. The point is to send it before the round trips from .a or .b complete. This code still cannot do that.
I don't see any simple and obvious path from understanding the synchronous message send to understanding how promises work. How does that explanation look?
Take it in three steps.
Step 1
var d = a.foo(b,c);
This does synchronous message sending. It delivers the message foo(b,c)
to the object designated by a immediately, transferring control to that
object -- the callee -- now. The caller blocks waiting for the callee to
respond. The callee runs to completion, finally returning a value. At this
point the caller receives that value into d and continues.
In order for the callee to be invoked synchronously, it must be local, since we don't want to wait on a round trip to a remote object.
In a communicating event-loop system, such synchronous call/return has a strong side effect contract with pros and cons.
- pros: no local time passes between the calling and the being called, so the callee gets control in the state in which the caller made the request.
- cons: the callee runs while the caller, and the caller's caller, etc, are suspended in the midst of some operations. They might have suspended invariants, or otherwise be unprepared for recursive entry in their current state. This threatens both caller and callee.
Step 2
var dP = aP ! foo(b,c);
This does asynchronous message sending. It delivers the message foo(b,c)
to the object designated by aP eventually, eventually causing that object
-- the callee -- to gain control. But the caller proceeds now without any
interleaving of control by others. Since the caller proceeds immediately,
dP cannot yet provide access to what the callee will return. But it still
designates that value, whatever it will be. A designator whose designation
is not yet determined is a promise. If the callee eventually returns an
int, then dP is already a promise for that int, though neither it nor we
know that yet.
Since we're only sending a message to the callee eventually and not waiting for it to respond, we don't much care whether it is local or remote.
In a communicating event loop system, such asynchronous message sending has a strong side effect contract with the opposite pros and cons.
- pros: The caller executes to completion without possibility of interference from the callee. Any delicate state the caller was in the midst of manipulating is unperturbed, and the caller can complete its manipulation, confident that its invariants were not disrupted. Likewise, the callee receives the message in an empty stack state, in which all previous turns have presumably restored all heap invariants. This is a robust situation from which to start running.
- cons: Between the caller requesting and the callee receiving, and arbitrary number of previously queued turns may run in the meantime, changing the world from the one in which the caller decided to send the message. By the time it arrives, it may no longer be relevant or appropriate.
Step 3
Ok, so we can . on local objects like a, and we can ! on local or
remote objects like aP. What about dP? It also designates something, but
that something is separated from us in time, not (necessarily) in space. No
matter! Asynchrony handles that too. If aP is remote, the message gets
queued on the event-loop hosting the object aP designates. If dP is
pending, the message gets queued in dP itself. Once dP knows what it
designates, it forwards all these queued messages to that target using !.
x.a().then( t1p=> y.b().then( t2p=> let t3p = { then(cb) { t1p.then( t1=> t2p.then( t2=> t1.c(t2).then( t3=> cb(t3) ) ) ) }; ...' ) )
(where ...' is ..., transformed to work with a promise t3p instead of t3)
Now, waiting for the .a and .b roundtrips would be delayed until some code in ...' actually needs to look at t3. One could further delay looking at t2 if t1.c() could deal with a promise t2p.
[colon removed from above quote per Tab's suggestion]
oops, force of habit, as Tab guessed correctly.
Ok, you've delayed sending the .c until you needed t3p. But that's the opposite of the promise pipelining optimization! The point is not to delay sending .c till even later. The point is to send it before the round trips from .a or .b complete. This code still cannot do that.
I do not expect to be able to emulate pipelining fully in user-land (at least not in JavaScript).
My aims were to demonstrate that .then does not need to stand in the way of such an optimization, and that the additional flexibility/ expressiveness provided by non-flattening .then is relevant here.
Back to your objection: there is nowhere to send the .c until you have t1 at hand. You could, however, move waiting on the t2 dependency to later, by passing the t2 receiver promise t2p to .c
x.a().then( t1p=>
y.b().then( t2p=>
let t3p = { then(cb) { t1p.then( t1=>
t1.c'(t2p).then( t3=> cb(t3) ) ) };
...' ) )
(where t1.c' is t1.c, modified to work with a promise)
Now, the call to t1.c' can go out after the .a roundtrip yet before the .b roundtrip completes. If you have also moved the callback code to the remote site, then the call to t1.c' could happen even without the .a roundtrip completing (from the perspective of the local site that triggered the chain) because t1 would be on the same site as the callback code and the remaining data.
This latter aspect of pipelining is simpler to do in the language implementation (unless the language itself supports sending of instantiated code, aka closures) - my point was merely to question the statement that .then would be in the way of such optimization.
Claus
On Fri, Jun 21, 2013 at 1:30 AM, Claus Reinke <claus.reinke at talk21.com>wrote:
x.a().then( t1p=>
y.b().then( t2p=> let t3p = { then(cb) { t1p.then( t1=> t2p.then( t2=> t1.c(t2).then( t3=> cb(t3) ) ) ) }; ...' ) )
(where ...' is ..., transformed to work with a promise t3p instead of t3)
Now, waiting for the .a and .b roundtrips would be delayed until some code in ...' actually needs to look at t3. One could further delay looking at t2 if t1.c() could deal with a promise t2p.
[colon removed from above quote per Tab's suggestion]
oops, force of habit, as Tab guessed correctly.
Ok, you've delayed sending the .c until you needed t3p. But that's the
opposite of the promise pipelining optimization! The point is not to delay sending .c till even later. The point is to send it before the round trips from .a or .b complete. This code still cannot do that.
I do not expect to be able to emulate pipelining fully in user-land (at least not in JavaScript).
< code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/makeQ.js>
supports promise pipelining in user land, using the makeRemote and makeFar extension points.
My aims were to demonstrate that .then does not need to stand in the way of such an optimization, and that the additional flexibility/ expressiveness provided by non-flattening .then is relevant here.
Back to your objection: there is nowhere to send the .c until you have t1 at hand. You could, however, move waiting on the t2 dependency to later, by passing the t2 receiver promise t2p to .c
x.a().then( t1p=> y.b().then( t2p=> let t3p = { then(cb) { t1p.then( t1=> t1.c'(t2p).then( t3=> cb(t3) ) ) }; ...' ) )
(where t1.c' is t1.c, modified to work with a promise)
Now, the call to t1.c' can go out after the .a roundtrip yet before the .b roundtrip completes. If you have also moved the callback code to the remote site, then the call to t1.c' could happen even without the .a roundtrip completing (from the perspective of the local site that triggered the chain) because t1 would be on the same site as the callback code and the remaining data. This latter aspect of pipelining is simpler to do in the language implementation (unless the language itself supports sending of instantiated code, aka closures) - my point was merely to question the statement that .then would be in the way of such optimization.
If we distinguish .then vs .there, you are describing .there above. With this distinction, do you agree that .then prevents this optimization?
< code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/makeQ.js> supports promise pipelining in user land, using the makeRemote and makeFar extension points.
Hmm. If you are moving JS code (from the callbacks) to another site, some local references (including some that aren't even behind promises) become remote and vice versa. How do you manage this? And could you please link to an application that shows how makeRemote would be used in context?
If we distinguish .then vs .there, you are describing .there above. With this distinction, do you agree that .then prevents this optimization?
No. I described how a specific variant of .then, passing promises to callbacks, could account for more flexibility in resolution, time-wise, than a flattening .then could. Providing an interface that fits with the protocol of a remote-executing .there is just one application of this additional flexibility (and my code left the remote-executing aspects implicit).
For language-level futures, the lack of explicit nesting gives the implementation the freedom to rearrange resolution as needed. For JS promises, ruling out nesting robs programmers of the freedom to rearrange resolution explicitly.
Claus
Hoping to proactively polyfill likely parts of upcoming standards, I am adding an implementation of DOM promises to IMVU's base JavaScript library.
Why was done() removed from the draft spec? " Rename Futures per TC39 discussion. Drop done() per same." whatwg/dom/commit/7a741b953990a89c6d27532928fe7817c3b25528
I read that there is an expectation that promise implementations will log when an error goes unhandled, but how is that possible?
To log when an error goes unhandled, an implementation would have to log when a promise has state=rejected and no reject callbacks, AND that the promise has been dropped on the floor. Without weak reference callbacks, how could you know that a promise has been GC'd?
I found done() to be a useful concept and in the absence of relevant notes from the below minutes, an explanation for why it was removed.
Thanks, Chad Austin
Hi all, Hoping to proactively polyfill likely parts of upcoming standards, I am adding an implementation of DOM promises to IMVU's base JavaScript library. Why was done() removed from the draft spec? " Rename Futures per TC39 discussion. Drop done() per same." https://github.com/whatwg/dom/commit/7a741b953990a89c6d27532928fe7817c3b25528 I read that there is an expectation that promise implementations will log when an error goes unhandled, but how is that possible? To log when an error goes unhandled, an implementation would have to log when a promise has state=rejected and no reject callbacks, AND that the promise has been dropped on the floor. Without weak reference callbacks, how could you know that a promise has been GC'd? I found done() to be a useful concept and in the absence of relevant notes from the below minutes, an explanation for why it was removed. Thanks, Chad Austin On Sun, Jun 2, 2013 at 9:46 AM, Rick Waldron <waldron.rick at gmail.com> wrote: > Technical Notes (by Erik Arvidsson): > Tuesday May 21 > John Neumann (JN), Allen Wirfs-Brock (AWB), Eric Ferraiuolo (EF), Erik > Arvidsson (EA), Luke Hoban (LH), Doug Crockford (DC), Yehuda Katz (YK), > Brendan Eich (BE), Sam Tobin-Hochstadt (STH), Alex Russell (AR), Dave > Herman (DH) (calling in), Bernd Mathiske (BM), Andreas Rossberg (ARB), Mark > Miller (MM), Tom Van Cutsem (TVC), Jasvir Naga (JNA), Istvan Sebestyen (IS) > > > > JN: Going through the agenda > Adding __proto__ > Unifying iterator/generator APIs > Talking about getting user stats for test-262... > YK: Prioritize ES6 items. So that we don’t get do ES7+ items before > > Minutes approved unanimously > > 4.1 Object.freeze > > DC: Today Object.freeze throws when primitives are passed in. Suggesting > not throwing when a value type is passed in. > MM: Object.isExtensible would return false for primitives > EA: This would give an inconstint view for primitives. > AWB/YK: (In strict mode) numbers and strings lazily box so the assignment > never fails. > MM: Proxies are allowed to be non extensible and throw away. > ARB: Is the suggestion to lazily wrap primitives? > MM: No, then isExtensible(7) would return true because the wrapper is > extensible. > AWB: In most of the new changes we are not doing unnecessary coercion. > YK: The Chrome dev tools, console.dir(7), says “no properties” which > supports treating these as empty objects. > MM: The only observable wrapper is the `this` wrapper in non strict mode. > AWB: In the new spec, Object.setPrototypeOf(7) throws. > MM: Agrees violently! > > Conclusion: DC+AWB to work out the details > > 4.2 WeakSet > > Do we need them? > MM: Trivial shim around WeakMap. > YK: Often wanted it > AWB: Adds no new capabilities. > AR: We should not limit ourselves to what is a new primitive capabilities > AI(AWB): add to spec > > Consensus to add WeakSet. > > 4.4 Proxies > > TVC’s presentation on Notification Proxies: > https://docs.google.com/file/d/0B9iYRsLxmdqUd1RsdHZtazliWmc/edit?usp=sharing > > Arguments against: > - shifts the burden from spec writers/implementors to users (need to use > shadow target even for non-frozen objects) > - implementors will deal with spec bugs related to invariant violations as > they come up > > Conclusion: Notification proxies are not approved. MM & TVC are still > happy with direct proxies. > > > Proxy Invoke Trap and wrong |this|-binding on built-in methods > > AWB: with current default behavior of “get”, “Caretaker” will break on > built-ins such as Date, because the |this| binding is by default set to the > proxy, so the Date built-in method will not find the correct private state. > ARB: Same issue with binary methods > ... > STH: We should add invoke trap but not change the object model > MM: Pleasant to have. Separate from private state. > AWB: used to think this was an issue with proxies, but convinced that it’s > an API issue: we need to provide default handlers that do the right thing, > and which users can subclass. In particular, want a handler that, on > forwarding, rebinds |this| to the target. > STH: If you want to proxy a Date method the underlying `this` needs to be > a non wrapped Date object. > TVC: previously proposed a Handler API that defines derived traps and > fundamental traps, allows you to subclass and inherit correct behavior for > derived traps. Can be used as the basis. > > AWB/TVC: invoke trap would make it easier to control |this|-binding > DH: Never liked breaking the semantics of [[Get]] + [[Call]] > TVC: there already exist invoke-only properties on platforms with > __noSuchMethod__ > AWB: For a [[Call]] it might be important to control `this` but by the > time the [[Call]] is happening you do not know what `this` to use. > DH: ActionScript has a proxy and they do have an invoke trap. > BM: The most common action is to invoke a method. > ? : we already gave up on the |this| invariant for accessors: in ES5, if > obj.x is a getter, |this| will always be bound to obj in the getter. With > proxies this is no longer true. > > AI(AWB, TVC): Add spec for invoke. Tom and Allen to work out details of a > Handler API that accommodates both “caretaker” (aka forwarding) and > “virtual object” use cases. > > Consensus: Add invoke trap. > > > 4.11 > MM: Everybody in this room wants classes and want to post pone private > state to after ES6 > ARB: Disagrees. > ARB: Based on feedback, people do not want unique symbols, only private > symbols. > MM: Private symbols do not work with proxies. > TVC: can still use WeakMap for private state. > DH: The most common cases where true information hiding is self hosting. > The stakes are too high for the browser engines. > YK: If “iterator” would be a private symbol, you cannot create a proxy > that will work with for-of loops. > ARB: Symbols (unique and private) and relations overlap. > BE: If we add symbols now we are stuck with them. > LH: Future users will be confused. They will not know what to use > BE: Unique symbol is very different from class private syntax. > AWB/MM: If we first did relationships we might not need symbols. > MM: Relationship published but not reflective. > MM: Difference between relationships and symbols: where is the mutability? > This forces us to have both relationships and unique symbols. > > Conclusion: ? > > Report from Geneva > > IS: IPR, vote on june 11, 2 docs about policy. royalty free task group > should function. When approved, create royalty free group within tc39. must > transition to the royalty free task group. Collect before November. Hope to > make switch in 2 or 3 months. > IS: Intel submitted royalty free statement on ecma 262. > BE: So did Mozilla > IS: IPR ad hoc group, work out a solution for a software contribution from > non members. IPR ad hoc group has not finished their work. > AWB: Public RF solution? We never talked about that here. > IS: Additional channel from non members. Compromise; Fill in form on tc39 > web site. Click through process. Agree RF TF. Can submit contribution > through the web site. > AWB: But not the software. > IS: It is still missing. It will be the next step. > AWB: This allows someone to write up a proposal and we are allowed to read > it and maybe even incorporate it. > IS: There is an ECMA recognition program. List contributors. Requests a > short list of nominees. > JN: Nominees before Thursday morning. > STH: And maybe a trophy? > > Item 9.1.2 > JN: RFTG mode. Keep things transparent. Unanimously approved > JN: Doc 24. Any objections? Unanimously approved > 4.13 Endianness of Typed array > ARB: Remember it as if we should specify this. > BE: Endianness in Typed Arrays is unspecified. > DH: Keep it open for now... Same system to same system. Using data view, > which is explicit, there is no problem. > STH: We don’t know what WiiU will do? > AWB: Or they decide not to comply to the spec > DH: WebGL is endian agnostic. > > Conclusion: Leaving it unspecified in ES6. > > 4.18 __proto__ > > STH: Recollection, first as data property, then as an accessor. Then > discussed the power of that setter. Set the [[Prototype]] in the [[Realm]]. > Then Allen wrote the spec. Realized that there were some problems with that > design. Roughly the same power as Object.setPrototypeOf. > MM: Existence of a setter... as long as we have the extensibility > restriction, that is sufficient. > AWB: Why restrict __proto__ and not other > DH: Objects belonging to a realm is a bad idea. > MM: No more reason to restrict the setter. > STH: Bind __proto__ setter to the object upon extraction > MM: In SES objects that are non extensible. Not going to remove __proto__ > going forward. > ARB: If Object.prototype.__proto__ is a data property, making it non > writable prevents other objects to use assign to set __proto__. > AWB: If Object.prototype.__proto__ is an accessor that just calls > Object.{set,get}PrototypeOf. > AR: Best practice on the web is important even in the future. > TVC: If we have O.p.__proto__ do we want Object.setPrototypeOf or just > Reflect.setPrototypeOf? > AWB: Makes sense to have Object.setPrototypeOf for consistency. > EA: Where do we draw the line (Object.x or Reflect.x)? > DH: People will need to be able to get this before we have a reflect > module. > TVC: We need both because they have different return value (reflect > setPrototypeOf returns boolean success value). > > Conclusion: __proto__ is an accessor on Object.prototype. The setter > mutates [[Prototype]]. There is no “poison pill”. We will provide both > Object.setPrototypeOf and std:reflect setPrototypeOf. > > > Naming of @@iterator > AWB: Suffix with $ > STH: Opposed to special naming. People don’t do this kind of naming > convention. Why do we want to introduce this concept? > > class Foo { > *[iterator]() { > yield ... > } > } > > Conclusion: No special naming > > Generators and iterators > AWB: send is gone in favor of next(arg) (only first arg is passed through > in yield*) > YK: Whether generators return a frozen object or not? > BE: close is gone > > > > Wednesday May 22 2013 > John Neumann (JN), Allen Wirfs-Brock (AWB), Eric Ferraiuolo (EF), Erik > Arvidsson (EA), Luke Hoban (LH), Doug Crockford (DC), Yehuda Katz (YK), > Brendan Eich (BE), Sam Tobin-Hochstadt (STH), Alex Russell (AR), Dave > Herman (DH) (calling in), Bernd Mathiske (BM), Andreas Rossberg (ARB), Mark > Miller (MM), Tom Van Cutsem (TVC), Istvan Sebestyen (IS), Jasvir Naga (JNA) > > > 4.16 Spec update > > http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts > > YK: ToPositiveInteger is needed by JSIDL > AI(YK+AWB): Put an algorithm in the spec that DOM can use so that we get > the same behavior in JS and DOM. > > 6 General implementation experiences > > ARB: We started implementing generators but things are pretty smooth. > BE: Doing modules at the moment. > AWB: Bunch of bug fixes in the spec related to classes. > > 4.9 String templates > > MM: Suggests status quo. > AR: Objects. > MM: Controversy related to tag-less templates. Alternatives include making > tag-less templates an error, delayed evaluation (contextually provided) > AR: Econics: naked interpolation is too attractive. Should always have a > tag to encourage users to think about which behavior is correct. > YK: I cannot support Alex’s proposal. > STH: What would the name of this tag be? > AR: Something that is imported from a module. > STH: Concerned about short names and conflicts. > YK: People will just use ‘s’ without thinking. > YK: People should use HTML templating engines. > DC: Alex’s testimony about application developer feedback is relevant. > LH: it sounded like Google engineers were using a template system > EA: Correct. > MM: Does anyone prefer taking out TS if they don’t get tag-less TS? > Everyone: Agrees that it is better to require tag to remove TS from ES6. > AR: Strings are always used later in some context. Communicating the intent > AWB: String concat vs string interpolation have the same issue. > LH: Assumes that maybe only 20% of the uses ot TS are susceptible to XSS > MM: Removing tag-less does not reduce XSS because people will just use s`. > TS helps people transition to a better world. Once they have have a TS it > will be easy to add an html tag at the front as needed. > ST: It will be painful to import String raw and alias that to s. > MM: Maybe put tag-less in appendix? Withdrawn idea because no one likes > it. > YK: You should not have use string based APIs. > AR: Willing to abstain but “Y’all are making a big mess” > BM: Half convinced by Alex. > LH: Different code bases will use different tags for normal string > interpolation so moving between code bases will be hard to. > AR: That is a good thing. Forces people to think. > MM: Template strings in E. > STH: Lots of contexts where XSS is not an issue. > BM: More ways to XSS is a bad thing. > BE: if people have to import s then the economics change and people will > stick to + > > Conclusion: AR and BM sustains. We continue with the status quo (tag-less > ts is supported) > > JSON > > DC: IETF wants to change JSON > MM: The 2 documents should have exactly the same text except for > boilerplate. > IS: Should it be done in TC39? > DC: Most of the work will be on the mailing lists > AWB: Who will be the editor? > DC: Hopes they (IETF) will provide an editor. > JN: Should this be fast tracked to ISO? > DC: That makes sense. > JN: How long do you expect this to take? > DC: Has taken a long time to coordinate and get started. > DC: 5.1 specs the 2 functions that uses the JSON format. > > 4.10 Modules > > STH: Progress since last meeting. Discuss “module naming”, “naming > standard modules”. > STH: http://wiki.ecmascript.org/doku.php?id=harmony:modules > STH: Wiki is up to date with the current proposal. Spec is “wiki > complete”. Jason Orendorff of Mozilla has worked on flushing out semantic > issues. Moz is implementinb parsing of modules. > STH: Syntax: Made a couple of changes. > A. To support anonymous exports > > export default expr; > > import $ from ‘jquery’; // imports default anonymous export > > If there is no default then the above is an error > > import {ajax} from ‘jquery’; > > import {ajax as A} from ‘query’; > > to reduce confusion and to make it clear that this is not destructuring. > > module fs from ‘js/fs’ > > fs is a module instance object > > The following is not valid: > > import {...} from fs; // SyntaxError > > Renaming on export: > > let foo = 13; > export {foo as bar}; > export {foo}; > > The following is not valid: > > export foo; > > STH: The only evaluation here is “13”. The rest are just bindings that are > shared with the outside/module importer. > MM: Bad idea to allow external modules to assign to imports. > DH: Imported bindings are read only to the importer. > AWB: This is new semantics to the language. Is there a list of these new > semantics modules introduce? > AWB: Is there a way to get the default export from the instance module > obejct. > STH: There will be a well known symbol name to get to it. > AWB: Does module instance objects inherit from Object.prototype. > DH: No. Because we do not want any pollution. > JNA: Is it an error to assign to an imported binding? > > import {ajax} from ‘jquery’; > ajax = 14; // Error > > AR: What is the reason for not extending Object.prototype or some other > object? > YK: To prevent people from expecting toString to be there (???) > DH: fs.readFile We don’t want to statically check this deeply inside an > expression. > > fs.toString > > THS: The plan is to allow the above to be a static error in the future. > DH: To keep things clean. > AWB: Concerned about the dot operator > ARB: Don’t want less checking if you do not use import. > DH: Do not want refactoring hazards. > ARB: This only affect the static semantics. > AWB: Can you use square bracket? > STH: Square bracket is dynamic. > AR: This is only a static check that is lost. At runtime there will still > be errors. > LH: Concerned about default export. Now people will have to decide which > approach to use. > STH: This is already the case in Node.js today. > LH: Today you might get any object, it might be callable with properties. > > var fs = require(‘fs’); // module instance > var glob = require(‘glob’); // function with properties > var parse = require(‘parse’); // function > > module fs from ‘fs’; > import glob from ‘glob’; > import {sync} from ‘glob’; > import parse from ‘parse’; > > Lots of discussion... > > import {sync} from ‘glob’; > > _alt_ > import glob from ‘glob’; > var {sync} = glob; > import {ajax} from ‘jquery’; > > LH: Prefers “export =” and lose static checking when people opt in to > single anonymous export. > STH/YK: We already agreed that we want static checking. > LH: Even for new things being built, this is causing a confusion. > AWB: It is unclear when and what you want to export as the default export. > BM: Wants > > import default $ from ‘jquery’ > > to ensure that people have to be explicit about what they import. > DH: This is just syntax and we are wasting time “bikeshedding” > AWB: What is the best practice? Is there a single module containing Map, > Set & WeakMap or... > YK: WeakMap should be its own import: > > import WeakMap from ‘collections/WeakMap’; > > BE: We have to pay attention to what Node/AMD do today. > YK: AMD tries to make modules small to reduced byte size of the > dependencies. > > STH: And now to semantics > https://github.com/jorendorff/js-loaders/blob/master/browser-loader.js > STH: Major things that changed. Use options object more consistently. > STH: The wiki page is up to date. > STH: Need to decide whether the browser loader is in the appendix or if it > is in some w3c spec. Want core language semantics to treat the names as > strings, not the semantics of these strings. > STH: Bulk loading. One HTTP request to load multiple modules. Possible to > implement. Create fecth hook. Stores module notations in a side table. In > the xhr response, split the result and call the different fulfill hooks. > EF: Sounds like what we do today in YUI loaders. > EF: How would you write the HTML? > DH: Initial script tag with configuration. Second script tag as usual. Alt > 2 is to have configuration and dynamic module load in the same script block. > > <script> > ondemand > </script> > <script src=”main.js” async></script> > > alt 2 > > <script> > ondemand > System.require(“main.js”, function() { …. }); > </script> > DH: script[async] today have to use an external src. > > STH: Naming and declarations of modules. > ARB: Presenting slides... > AWB: The rate that internal vs external names changes is very different. > STH: > > module ‘m’ { … } > module ‘n’ { > import x from ‘m’; > … // this part is not executed. > } > import x from ‘m’; > > STH: Configuration step is mostly about other people’s code. > …. > > <script> > module ‘m’ { … } > module ‘n’ { > import m from ‘m’; > function f() { > Loader.eval(“import m from ‘m’”); > } > } > </script> > > m is fixed at compile time > > ARB: Not opposed to logical modules. Wants both lexical and logical > DH: Not opposed to lexical modules. > YK: Too late to work out lexical modules for ES6. > ARB: If we wait we will have redundancy. > YK: Want declarative form to be able to prefetch etc. > BE: I want lexical modules (in the future) but logical modules are easier > to use. > ARB: Since I don’t seem to be able to convince anyone I’m going to drop > this > ARB: For the record. Major concern about the global registry becoming the > new global object. > > Conclusion: Move along with Dave and Sam’s proposal. Work on lexical > modules for ES7 > > > Promises vs Monads > > MM: Presenting... > > > Thursday May 23 2013 > John Neumann (JN), Allen Wirfs-Brock (AWB), Eric Ferraiuolo (EF), Erik > Arvidsson (EA), Luke Hoban (LH), Doug Crockford (DC), Yehuda Katz (YK), Sam > Tobin-Hochstadt (STH), Alex Russell (AR), Dave Herman (DH) (calling in), > Bernd Mathiske (BM), Andreas Rossberg (ARB), Mark Miller (MM), Tom Van > Cutsem (TVC), Istvan Sebestyen (IS) > > Promises vs Monads > > MM: Continuing from yesterday > > AR: https://github.com/slightlyoff/Futures/blob/master/Promise.idl > STH: Don’t like resolve but not willing to die on this hill. > AR: DOM has a bunch of ad hocs APIs to do promise like things. > YK: Mozilla is also actively working on APIs using promises. > AR: A lot of methods today return void so we can change these to return a > promise. This is forward compatible. > AR: then does recursive unwrapping > ... > > Next Meetings > > July 23 - 25 @ Microsoft, Redmond > Sept 17 - 19 @ Bocoup, Boston > Nov 19 - 21 @ PayPal, San Jose > > ES6, ES7, ES8... Mark’s Strawman Roadmap > > LH: The important part is not the features but the process. > AWB: Can things be decoupled? > LH: These kind of structural questions are the important part > MM: Suggests “currency” to be the main theme. > AWB: Thought about the event loop. All we need is a processing queue... > put things in the front and the back. > DH: Only need to add to the back. > AWB: OK. > STH: The callback is called at some later point. > AR: Don’t think we need to specify the order. > STH: If we are going to specify promises etc we need to be able to specify > things in detail. We can be loose in ES6 and then come back in ES7 and > provide a more tight spec. > DH: We could specify the pending events as a set or something. > DH: Not sure if there is a consensus that we want a fast small ES7. Not > opposed to a modularized approach. > AR: Are there any browsers that are not shipping stable ES6 features today. > YK: Yes. V8. > AWB: Where we have problem today is that there is a lot of interdependency. > MM: These (“concurrency”) are coupled together to the event loop > AWB: We can do it as a separate non 262 spec > DH: Opposed to a separate spec. Introduces versioning confusion. > AWB: Roll up > DH: Think of all the extra overhead. > STH: Big difference with 402 since it was run by different people. > LH: Lack of confidence in new features has been an issue for implementers. > Good exceptions were Object.observe and Proxies where the wiki contained a > mostly complete spec. > AWB: We need to have wiki proposals be deltas to the spec. > TVC: We could have “stable” wiki pages. These would have complete spec > deltas. > DH: Very concerned about over modularizing. > AWB: We need to find a way to work faster and be less monolithic. > DH: Agree. ES6 process has blocked implementation work. > LH: We are not committed to our designs. > STH: We are not resolving issues until we start to spec. We are not > getting feedback until engines starts to implement. > EA: The problem is that we didn’t start to spec things until very late. We > had agreements on features long before there was any spec drafts for them. > YK: More from our champions before we get to concensus. > ARB: Lots of the proposals were very vague. > AWB: The more complete spec you bring to tc39 the better chance you have > to reach consensus. > ARB: Lack of early spec leads to lack of early implementations... > AWB: ...which leads to lack of feedback. > LH: Not more work, just doing the work earlier before things pile up too > much. > DH: Need to look at the dependency graph. Hold of the work of later > feature. > ARB: We need to higher bar before we accept proposals. > MM: What we agreed to 2 years ago was that the features are the one we > want to spend work on speccing. > LH: Less features to bite of. > DH: A lot of us have a hard time not getting too engage in too many > features. > YK: if we focused more effort on managing the overall complexity instead > of getting stuck on a lot of technical discussions (and nit picking). > DH: Object.observe and Proxy moved fast but are fairly isolated features > TVC: Didn’t involve syntax. > AWB: With ES6 we had a long backlog. > DH: A language will have smaller and smaller complexity budgets as it > grows. > AR: ES future needs events > DH: Since this is Mark’s wishlist people will throw in their pet features. > MM: This is the direction I am going to work. > LH: There is a page on the wiki outlining the goals. > LH: Looking for 2 things: Something that would allow earlier > implementations. Have not brought proposals (over the last 2 years) because > we have been blocked by ES6. > LH: When is the appropriate time to bring new proposals to TC39? > AWB: We are free to do what we want. We can issue 6.1, 6.2 etc or > technical reports which would serve as a recommendation. > DH: We cannot exclusively work on ES6. > YK: Time at f2f is the most important. Champions can go off and do what > they want. > DH: Suggests adding non ES6 items to the agenda. We will prioritize the > non es6 stuff we can get to given our limited time. > YK: We should reinstate the rule that agenda items needs links to wiki > pages. > YK: Spec language is good but examples at the top are a must. > ARB: Add step after proposal. For example “stable” or “spec” which a > proposal gets promoted to once there is a spec draft, good enough to start > implementing. > DH: Strawman: Anything goes. > YK: Proposals used to mean approved. > DH: 3 sections: strawman, proposal, spec/candidate. Keep strawman. Work on > improving as a proposal, and when mature enough promoted to next level. > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > -- Chad Austin Technical Director, IMVU http://www.imvu.com/members/Chad/ -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130618/0caf1e65/attachment-0001.html>