Re: Promises: unhandled rejections and finally()
It does pass through the fulfillment status - but it does that by adding both an onFulfilled and an onRejected handler, so it does affect the "unhandled rejection" hook. On Sat, Mar 28, 2020 at 7:23 PM Felipe Gasper wrote: > Hi Logan, > > Thank you .. that makes sense. I’m not sure why now but I had in mind that > finally() creates a “passthrough” promise that doesn’t affect its parent > promise’s resolved/rejected status. > > Cheers, > -Felipe > > On Mar 28, 2020, at 21:40, Logan Smyth wrote: > > > > Could someone point me to something that would help me to understand the > logic here? It looks like the first finally() is getting a “free pass” > while only the 2nd and subsequent ones trigger their own > unhandled-rejection warnings. > > I think the best place to start in understanding this would be to step > back and make sure you understand what it is that triggers these errors. > Node triggers these errors when a promise object has been rejected but has > no handlers to do respond to the rejection. I forget exactly what point > Node checks for handlers these days, if that point is at the end of the job > execution or on GC of promises now or what but that's not important for > this case. > > Let's look at your example. I'm also simplifying it to > ``` > var p = Promise.reject(789); > var one = p.finally(() => {}); > var two = p.finally(() => {}); > var three = p.finally(() => {}); > ``` > > 1) > ``` > var p = Promise.reject(789); > ``` > There is only one promise here, `p`, and it has no handlers ever attached > to it so there is nothing to handle the rejection error, hence the single > uncaught rejection error. > > 2) > ``` > var p = Promise.reject(789); > var one = p.finally(() => {}); > ``` > There are 2 promises here, `p`, which has one handler (the `finally` that > will take the rejection of `p` and in turn reject `one`) and `one`, which > has no handlers attached, so you again get a single uncaught rejection. It > as not that the first "finally" gets a "free pass", it is that rejections > from `p` are no longer uncaught, but you have added a new promise that is > uncaught, so the overall number of uncaught rejections does not change. > > 3) > ``` > var p = Promise.reject(789); > var one = p.finally(() => {}); > var two = p.finally(() => {}); > ``` > Hopefully you can see where this is going. `p` now has 2 handlers attached > so its rejection isn't uncaught, but now both `one` and `two` have no > handlers, so _both_ will trigger an uncaught rejection error. > > 4) > ``` > var p = Promise.reject(789); > var one = p.finally(() => {}); > var two = p.finally(() => {}); > var three = p.finally(() => {}); > ``` > And finally now we have `one`, `two` and `three` all with no handlers > attached, so you will get three uncaught rejection errors. > > Hope that helps! > > > On Sun, Mar 29, 2020 at 9:03 AM Felipe Gasper > wrote: > >> Hello, >> >> In node 12 as well as the latest Chrome and FF (all on macOS) I see the >> following: >> >> - >> var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); n(789); >> ==> produces 1 unhandled-rejection warning >> >> var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () >> => {} ); n(789); >> ==> produces 1 unhandled-rejection warning >> >> var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () >> => {} ); p.finally( () => {} ); n(789); >> ==> produces 2 unhandled-rejection warnings >> >> var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () >> => {} ); p.finally( () => {} ); p.finally( () => {} ); n(789); >> ==> produces 3 unhandled-rejection warnings >> >> - >> >> Could someone point me to something that would help me to understand the >> logic here? It looks like the first finally() is getting a “free pass” >> while only the 2nd and subsequent ones trigger their own >> unhandled-rejection warnings. >> >> Thank you! >> >> cheers, >> -Felipe Gasper >> ___ >> es-discuss mailing list >> es-discuss@mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: unhandled rejections and finally()
Hi Logan, Thank you .. that makes sense. I’m not sure why now but I had in mind that finally() creates a “passthrough” promise that doesn’t affect its parent promise’s resolved/rejected status. Cheers, -Felipe > On Mar 28, 2020, at 21:40, Logan Smyth wrote: > > > > Could someone point me to something that would help me to understand the > > logic here? It looks like the first finally() is getting a “free pass” > > while only the 2nd and subsequent ones trigger their own > > unhandled-rejection warnings. > > I think the best place to start in understanding this would be to step back > and make sure you understand what it is that triggers these errors. Node > triggers these errors when a promise object has been rejected but has no > handlers to do respond to the rejection. I forget exactly what point Node > checks for handlers these days, if that point is at the end of the job > execution or on GC of promises now or what but that's not important for this > case. > > Let's look at your example. I'm also simplifying it to > ``` > var p = Promise.reject(789); > var one = p.finally(() => {}); > var two = p.finally(() => {}); > var three = p.finally(() => {}); > ``` > > 1) > ``` > var p = Promise.reject(789); > ``` > There is only one promise here, `p`, and it has no handlers ever attached to > it so there is nothing to handle the rejection error, hence the single > uncaught rejection error. > > 2) > ``` > var p = Promise.reject(789); > var one = p.finally(() => {}); > ``` > There are 2 promises here, `p`, which has one handler (the `finally` that > will take the rejection of `p` and in turn reject `one`) and `one`, which has > no handlers attached, so you again get a single uncaught rejection. It as not > that the first "finally" gets a "free pass", it is that rejections from `p` > are no longer uncaught, but you have added a new promise that is uncaught, so > the overall number of uncaught rejections does not change. > > 3) > ``` > var p = Promise.reject(789); > var one = p.finally(() => {}); > var two = p.finally(() => {}); > ``` > Hopefully you can see where this is going. `p` now has 2 handlers attached so > its rejection isn't uncaught, but now both `one` and `two` have no handlers, > so _both_ will trigger an uncaught rejection error. > > 4) > ``` > var p = Promise.reject(789); > var one = p.finally(() => {}); > var two = p.finally(() => {}); > var three = p.finally(() => {}); > ``` > And finally now we have `one`, `two` and `three` all with no handlers > attached, so you will get three uncaught rejection errors. > > Hope that helps! > > >> On Sun, Mar 29, 2020 at 9:03 AM Felipe Gasper >> wrote: >> Hello, >> >> In node 12 as well as the latest Chrome and FF (all on macOS) I see the >> following: >> >> - >> var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); n(789); >> ==> produces 1 unhandled-rejection warning >> >> var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () => >> {} ); n(789); >> ==> produces 1 unhandled-rejection warning >> >> var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () => >> {} ); p.finally( () => {} ); n(789); >> ==> produces 2 unhandled-rejection warnings >> >> var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () => >> {} ); p.finally( () => {} ); p.finally( () => {} ); n(789); >> ==> produces 3 unhandled-rejection warnings >> >> - >> >> Could someone point me to something that would help me to understand the >> logic here? It looks like the first finally() is getting a “free pass” while >> only the 2nd and subsequent ones trigger their own unhandled-rejection >> warnings. >> >> Thank you! >> >> cheers, >> -Felipe Gasper >> ___ >> es-discuss mailing list >> es-discuss@mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: unhandled rejections and finally()
> Could someone point me to something that would help me to understand the logic here? It looks like the first finally() is getting a “free pass” while only the 2nd and subsequent ones trigger their own unhandled-rejection warnings. I think the best place to start in understanding this would be to step back and make sure you understand what it is that triggers these errors. Node triggers these errors when a promise object has been rejected but has no handlers to do respond to the rejection. I forget exactly what point Node checks for handlers these days, if that point is at the end of the job execution or on GC of promises now or what but that's not important for this case. Let's look at your example. I'm also simplifying it to ``` var p = Promise.reject(789); var one = p.finally(() => {}); var two = p.finally(() => {}); var three = p.finally(() => {}); ``` 1) ``` var p = Promise.reject(789); ``` There is only one promise here, `p`, and it has no handlers ever attached to it so there is nothing to handle the rejection error, hence the single uncaught rejection error. 2) ``` var p = Promise.reject(789); var one = p.finally(() => {}); ``` There are 2 promises here, `p`, which has one handler (the `finally` that will take the rejection of `p` and in turn reject `one`) and `one`, which has no handlers attached, so you again get a single uncaught rejection. It as not that the first "finally" gets a "free pass", it is that rejections from `p` are no longer uncaught, but you have added a new promise that is uncaught, so the overall number of uncaught rejections does not change. 3) ``` var p = Promise.reject(789); var one = p.finally(() => {}); var two = p.finally(() => {}); ``` Hopefully you can see where this is going. `p` now has 2 handlers attached so its rejection isn't uncaught, but now both `one` and `two` have no handlers, so _both_ will trigger an uncaught rejection error. 4) ``` var p = Promise.reject(789); var one = p.finally(() => {}); var two = p.finally(() => {}); var three = p.finally(() => {}); ``` And finally now we have `one`, `two` and `three` all with no handlers attached, so you will get three uncaught rejection errors. Hope that helps! On Sun, Mar 29, 2020 at 9:03 AM Felipe Gasper wrote: > Hello, > > In node 12 as well as the latest Chrome and FF (all on macOS) I see the > following: > > - > var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); n(789); > ==> produces 1 unhandled-rejection warning > > var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () > => {} ); n(789); > ==> produces 1 unhandled-rejection warning > > var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () > => {} ); p.finally( () => {} ); n(789); > ==> produces 2 unhandled-rejection warnings > > var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () > => {} ); p.finally( () => {} ); p.finally( () => {} ); n(789); > ==> produces 3 unhandled-rejection warnings > > - > > Could someone point me to something that would help me to understand the > logic here? It looks like the first finally() is getting a “free pass” > while only the 2nd and subsequent ones trigger their own > unhandled-rejection warnings. > > Thank you! > > cheers, > -Felipe Gasper > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises, async functions, and requestAnimationFrame, together.
Ah, I think the reason that this animation loop using promises works is because promise handlers resolve in the next microtask after the current macrotask. I believe that the animation frame fires in what is essentially a macrotask, then immediately after this macrotask the resolution of the `animationFrame()` promise happens in the following microtask. At some point later, the browser renders stuff, which I think might the next macrotask after the animation frame macrotask. */#!/*JoePea ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises, async functions, and requestAnimationFrame, together.
This is definitely interesting stuff. Have you considered rewriting this so that it only uses generators? If you did then you could test natively in Chrome and see if you get the same results. - Matthew Robb On Sat, Apr 23, 2016 at 7:01 PM, /#!/JoePeawrote: > Just to show a little more detail, here's a screenshot that shows that > the logic of the while-loop version of my animation loop fires inside > each animation frame. I've zoomed out and we can see there's nothing > fired between the frames: > > > https://cloud.githubusercontent.com/assets/297678/14764323/c28e83cc-0968-11e6-8771-8e726158aa52.png > > On Sat, Apr 23, 2016 at 3:18 PM, /#!/JoePea wrote: > > Alright, I did an experiment, and I'm really surprised at the results! > > Apparently, the logic (what would be drawSomething() in my previous > > example) is fired within the frame!! > > > > So, let me show you my original method for starting an animation loop. > > I'm working on a 3D project at http://infamous.io. The Scene class > > (https://github.com/infamous/infamous/blob/master/src/motor/Scene.js) > > has a method for starting an animation loop the standard way: > > > > ```js > > async _startAnimationLoopWhenMounted() { > > this._animationLoopStarted = true > > > > if (!this._mounted) await this.mountPromise > > > > // So now we can render after the scene is mounted. > > const loop = timestamp => { > > this._inFrame = true > > > > this._runRenderTasks(timestamp) > > this._renderNodes(timestamp) > > > > // If any tasks are left to run, continue the animation loop. > > if (this._allRenderTasks.length) > > this._rAF = requestAnimationFrame(loop) > > else { > > this._rAF = null > > this._animationLoopStarted = false > > } > > > > this._inFrame = false > > } > > > > this._rAF = requestAnimationFrame(loop) > > } > > ``` > > > > Here's what the Chrome timeline shows for the logic that is fired > > inside the loop: > > > https://cloud.githubusercontent.com/assets/297678/14764236/8eb72d4a-0965-11e6-9bb9-5db02cc23520.png > > > > Now, I went ahead and modified my Scene class so the method now looks > like this: > > > > ```js > > function animationFrame() { > > let resolve = null > > const promise = new Promise(r => resolve = r) > > window.requestAnimationFrame(resolve) > > return promise > > } > > > > // ... > > > > async _startAnimationLoopWhenMounted() { > > this._animationLoopStarted = true > > > > if (!this._mounted) await this.mountPromise > > > > this._rAF = true > > let timestamp = null > > while (this._rAF) { > > timestamp = await animationFrame() > > this._inFrame = true > > > > this._runRenderTasks(timestamp) > > this._renderNodes(timestamp) > > > > // If any tasks are left to run, continue the animation loop. > > if (!this._allRenderTasks.length) { > > this._rAF = null > > this._animationLoopStarted = false > > } > > > > this._inFrame = false > > } > > } > > ``` > > > > And the timeline results are surprising! As you can see in the > > following screenshot, all of the logic happens within the frame > > (though you can see there's extra overhead from what I assume are the > > extra function calls due to the fact that I'm using Facebook > > Regenerator for the async functions): > > > https://cloud.githubusercontent.com/assets/297678/14764237/8eb71ce2-0965-11e6-942a-3c556c48b9a0.png > > > > Near the bottom right of the screen shot, you can see the tooltip as > > I'm hovering on the call to `animationFrame` which returns the promise > > that I am awaiting in the loop. > > > > Although this behavior seems to be exactly what I was hoping for, it > > seems like there is something wrong. Could there be a bug in > > regenerator that is failing to defer my loop code to a following tick? > > Or is my code deferred to a following tick that somehow the animation > > frame knows to execute within the same tick? Maybe there's something > > I'm missing about the Promise API that allows for .then() of a promise > > (which I assume is what Regenerator is using) to be executed in the > > same tick? What's going on here? > > > > I was expecting to see the code of my loop execute outside of the > > "Animation Frame Fired" section. > > > > On Sat, Apr 23, 2016 at 6:28 AM, Boris Zbarsky wrote: > >> On 4/23/16 4:09 AM, Salvador de la Puente González wrote: > >>> > >>> AFAIK, that should execute `drawSomething()` once per frame. Given a > >>> frame is each time the animatinFrame() promise resolves. > >> > >> > >> What's not obvious to me is whether it will execute it before the actual > >> paint for the frame (i.e.
Re: Promises, async functions, and requestAnimationFrame
Note that there is no guarantee that the `then` handlers (after the await) will fire in the same loop since they defer execution on their own and might defer it further. In practice I assume they'll probe to see if they need to actually schedule asynchronously or the constructed promise is already handling that (like libraries do) and won't defer it any further but that's just a guess. The only spec I'm aware of that does not defer in this case is the es-observable spec. There has also been talk about being able to set the scheduler for promises which is something I wanted to bring up in ESDiscuss but haven't been able to yet. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises, async functions, and requestAnimationFrame, together.
Just to show a little more detail, here's a screenshot that shows that the logic of the while-loop version of my animation loop fires inside each animation frame. I've zoomed out and we can see there's nothing fired between the frames: https://cloud.githubusercontent.com/assets/297678/14764323/c28e83cc-0968-11e6-8771-8e726158aa52.png On Sat, Apr 23, 2016 at 3:18 PM, /#!/JoePeawrote: > Alright, I did an experiment, and I'm really surprised at the results! > Apparently, the logic (what would be drawSomething() in my previous > example) is fired within the frame!! > > So, let me show you my original method for starting an animation loop. > I'm working on a 3D project at http://infamous.io. The Scene class > (https://github.com/infamous/infamous/blob/master/src/motor/Scene.js) > has a method for starting an animation loop the standard way: > > ```js > async _startAnimationLoopWhenMounted() { > this._animationLoopStarted = true > > if (!this._mounted) await this.mountPromise > > // So now we can render after the scene is mounted. > const loop = timestamp => { > this._inFrame = true > > this._runRenderTasks(timestamp) > this._renderNodes(timestamp) > > // If any tasks are left to run, continue the animation loop. > if (this._allRenderTasks.length) > this._rAF = requestAnimationFrame(loop) > else { > this._rAF = null > this._animationLoopStarted = false > } > > this._inFrame = false > } > > this._rAF = requestAnimationFrame(loop) > } > ``` > > Here's what the Chrome timeline shows for the logic that is fired > inside the loop: > https://cloud.githubusercontent.com/assets/297678/14764236/8eb72d4a-0965-11e6-9bb9-5db02cc23520.png > > Now, I went ahead and modified my Scene class so the method now looks like > this: > > ```js > function animationFrame() { > let resolve = null > const promise = new Promise(r => resolve = r) > window.requestAnimationFrame(resolve) > return promise > } > > // ... > > async _startAnimationLoopWhenMounted() { > this._animationLoopStarted = true > > if (!this._mounted) await this.mountPromise > > this._rAF = true > let timestamp = null > while (this._rAF) { > timestamp = await animationFrame() > this._inFrame = true > > this._runRenderTasks(timestamp) > this._renderNodes(timestamp) > > // If any tasks are left to run, continue the animation loop. > if (!this._allRenderTasks.length) { > this._rAF = null > this._animationLoopStarted = false > } > > this._inFrame = false > } > } > ``` > > And the timeline results are surprising! As you can see in the > following screenshot, all of the logic happens within the frame > (though you can see there's extra overhead from what I assume are the > extra function calls due to the fact that I'm using Facebook > Regenerator for the async functions): > https://cloud.githubusercontent.com/assets/297678/14764237/8eb71ce2-0965-11e6-942a-3c556c48b9a0.png > > Near the bottom right of the screen shot, you can see the tooltip as > I'm hovering on the call to `animationFrame` which returns the promise > that I am awaiting in the loop. > > Although this behavior seems to be exactly what I was hoping for, it > seems like there is something wrong. Could there be a bug in > regenerator that is failing to defer my loop code to a following tick? > Or is my code deferred to a following tick that somehow the animation > frame knows to execute within the same tick? Maybe there's something > I'm missing about the Promise API that allows for .then() of a promise > (which I assume is what Regenerator is using) to be executed in the > same tick? What's going on here? > > I was expecting to see the code of my loop execute outside of the > "Animation Frame Fired" section. > > On Sat, Apr 23, 2016 at 6:28 AM, Boris Zbarsky wrote: >> On 4/23/16 4:09 AM, Salvador de la Puente González wrote: >>> >>> AFAIK, that should execute `drawSomething()` once per frame. Given a >>> frame is each time the animatinFrame() promise resolves. >> >> >> What's not obvious to me is whether it will execute it before the actual >> paint for the frame (i.e. before or right after the loop that's notifying >> the animation frame callbacks completes) or whether it will do >> drawSomething() after the paint... >> >> -Boris >> >> ___ >> es-discuss mailing list >> es-discuss@mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises, async functions, and requestAnimationFrame, together.
Alright, I did an experiment, and I'm really surprised at the results! Apparently, the logic (what would be drawSomething() in my previous example) is fired within the frame!! So, let me show you my original method for starting an animation loop. I'm working on a 3D project at http://infamous.io. The Scene class (https://github.com/infamous/infamous/blob/master/src/motor/Scene.js) has a method for starting an animation loop the standard way: ```js async _startAnimationLoopWhenMounted() { this._animationLoopStarted = true if (!this._mounted) await this.mountPromise // So now we can render after the scene is mounted. const loop = timestamp => { this._inFrame = true this._runRenderTasks(timestamp) this._renderNodes(timestamp) // If any tasks are left to run, continue the animation loop. if (this._allRenderTasks.length) this._rAF = requestAnimationFrame(loop) else { this._rAF = null this._animationLoopStarted = false } this._inFrame = false } this._rAF = requestAnimationFrame(loop) } ``` Here's what the Chrome timeline shows for the logic that is fired inside the loop: https://cloud.githubusercontent.com/assets/297678/14764236/8eb72d4a-0965-11e6-9bb9-5db02cc23520.png Now, I went ahead and modified my Scene class so the method now looks like this: ```js function animationFrame() { let resolve = null const promise = new Promise(r => resolve = r) window.requestAnimationFrame(resolve) return promise } // ... async _startAnimationLoopWhenMounted() { this._animationLoopStarted = true if (!this._mounted) await this.mountPromise this._rAF = true let timestamp = null while (this._rAF) { timestamp = await animationFrame() this._inFrame = true this._runRenderTasks(timestamp) this._renderNodes(timestamp) // If any tasks are left to run, continue the animation loop. if (!this._allRenderTasks.length) { this._rAF = null this._animationLoopStarted = false } this._inFrame = false } } ``` And the timeline results are surprising! As you can see in the following screenshot, all of the logic happens within the frame (though you can see there's extra overhead from what I assume are the extra function calls due to the fact that I'm using Facebook Regenerator for the async functions): https://cloud.githubusercontent.com/assets/297678/14764237/8eb71ce2-0965-11e6-942a-3c556c48b9a0.png Near the bottom right of the screen shot, you can see the tooltip as I'm hovering on the call to `animationFrame` which returns the promise that I am awaiting in the loop. Although this behavior seems to be exactly what I was hoping for, it seems like there is something wrong. Could there be a bug in regenerator that is failing to defer my loop code to a following tick? Or is my code deferred to a following tick that somehow the animation frame knows to execute within the same tick? Maybe there's something I'm missing about the Promise API that allows for .then() of a promise (which I assume is what Regenerator is using) to be executed in the same tick? What's going on here? I was expecting to see the code of my loop execute outside of the "Animation Frame Fired" section. On Sat, Apr 23, 2016 at 6:28 AM, Boris Zbarskywrote: > On 4/23/16 4:09 AM, Salvador de la Puente González wrote: >> >> AFAIK, that should execute `drawSomething()` once per frame. Given a >> frame is each time the animatinFrame() promise resolves. > > > What's not obvious to me is whether it will execute it before the actual > paint for the frame (i.e. before or right after the loop that's notifying > the animation frame callbacks completes) or whether it will do > drawSomething() after the paint... > > -Boris > > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises, async functions, and requestAnimationFrame, together.
On 4/23/16 4:09 AM, Salvador de la Puente González wrote: AFAIK, that should execute `drawSomething()` once per frame. Given a frame is each time the animatinFrame() promise resolves. What's not obvious to me is whether it will execute it before the actual paint for the frame (i.e. before or right after the loop that's notifying the animation frame callbacks completes) or whether it will do drawSomething() after the paint... -Boris ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises, async functions, and requestAnimationFrame, together.
AFAIK, that should execute `drawSomething()` once per frame. Given a frame is each time the animatinFrame() promise resolves. On Sat, Apr 23, 2016 at 1:38 AM, /#!/JoePeawrote: > Is it possible? > > I thought maybe something like this: > > ```js > function animationFrame() { > let resolve = null > const promise = new Promise(r => resolve = r) > window.requestAnimationFrame(resolve) > return promise > } > > async function game() { > // the game loop > while (true) { > await animationFrame() > drawSomething() > } > } > > game() > ``` > > But, I'm not sure what to expect: does the code that follows the await > statement execute in the frame, sometimes in the frame, or never in > the frame? What might we expect from the various browsers? > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
*F# cancellation* - on second thought implicit cancellation through cancellation like in F# is impractical because of the eagerness of promises. I don't think it's a valid alternative here. I've discussed this with Reed Copsey (an F# expert) and he explained the philosophy behind it to me - it doesn't sound practical for JavaScript promises at all. *Splitting into abort and ignore semantics - *that sounds like a great idea, I think that ignore semantics are far more common for the use cases I run into by the way. I think it might be a good idea to fire this off in a separate ES-Discuss thread. *Throwing and implicit cancellation *- I agree with Katelyn, throwing and ignore cancellation are exclusive. As Bradley said - `return` (as in generator `.return`) is a better mental model. `finally` blocks are run but not `catch` blocks. In bluebird 3, cancellation is done with ignore semantics - and we don't `throw` on cancellation (link again for comparison: http://bluebirdjs.com/docs/api/cancellation.html ). *Implicit cancellation being opt in - *I agree too, the nice property is that cancellation becomes an optimization library authors get to make but no other code breaks. A library can add support for freeing requests earlier and you don't have to change your code in order to get the optimization. *Socket Lifetime Example - *In bluebird we map that with disposers (ref: http://bluebirdjs.com/docs/api/promise.using.html ), we have found that in the last few years although the library gets millions of downloads very few people actually use `using`. It is an interesting motivating example and I think we should collect those for any future proposal. *XHR Example *- I think that ignore semantics can model that. The XHR saving important changes would simply not be cancellable under implicit cancellation - trying to cancel it would result in a no-op. Cheers On Wed, Jan 13, 2016 at 12:13 AM, Katelyn Gaddwrote: > Implicit cancellation doesn't make sense if it involves a throw. > Furthermore, implicit cancellation would never happen for your example > - the 'await' clearly depends on the result of the operation, so it is > in use and it would not make sense for it to be implicitly cancelled. > For the record, every time I've successfully dealt with implicit > cancellation, it's opt-in - the library author implementing the > asynchronous logic decides whether or not it will respond to implicit > cancellation. > > Explicit cancellation ('abort', to use the terminology split Bradley > advocated above) would potentially introduce a throw there, but that > should be unsurprising, just as a socket being closed due to an > ethernet cable getting unplugged would cause a socket operation to > throw "unexpectedly". > > > There are some subtle distinctions here that APIs tend to get wrong, > of course. An example that might be informative: > > If you look at sockets, various APIs tend to have differences in how > you deal with lifetime & closing handles. In some cases there are > subtly different forms operations - for example if you look at Socket > in the .NET framework, it exposes *three different/overlapping forms > of lifetime management*, through the Shutdown, Close and Dispose > methods. If you compare this with Rust's sockets API, the only > explicit operation it exposes is 'shutdown', and the other two are > expressed by dropping the value. > > For the average .NET user, you can treat all three as equivalent. For > casual use cases you can just Dispose all IDisposable resources > (files, sockets, graphics resources...) at the end of a function and > be ready to go. But behaviorally they are different, and semantically > they are different. A socket Shutdown followed by a Dispose will flush > the read/write buffers (which can take time) and allow you to cleanly > tear everything down, while a standalone Dispose is an instantaneous > destruction of the socket, potentially breaking a transmission in the > middle. In practice, this distinction represents that the socket is > actually a group of related resources - read/write buffers, a network > connection, etc - that can't simply be treated as a single unit with > consistent lifetime. > > Oh yeah, and there's also the detail that you can shut down the write > end of a socket but not the read end. :-) > > If you map this over to the browser, you can trivially come up with > equivalent examples. When I issue XHR requests, some of those requests > are more important than others. For example, an XHR that is saving > important changes to an email draft should not be aborted if the user > clicks on a hyperlink to load a new email. However, an XHR that is > downloading an inline image for an email *can* be safely aborted at a > moment's notice. You can think of this as equivalent to > Shutdown/Dispose - you would want to Shutdown the draft save > operation, flushing everything out, before closing the socket, and > that takes time. In comparison, the
Re: Promises as Cancelation Tokens
> > For async functions, it would mean that any await expression could > potentially throw a CancelError Cancellation does not necessarily need to use `throw`, `return` is often more apt. I find. I would also recommend splitting the idea of cancellation into: abort semantics, and ignorance semantics. Trying to use one term for both separately is getting confusing. My usage of cancellation is often being able to undo side-effects which can happily be done using `finally`. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
Implicit cancellation doesn't make sense if it involves a throw. Furthermore, implicit cancellation would never happen for your example - the 'await' clearly depends on the result of the operation, so it is in use and it would not make sense for it to be implicitly cancelled. For the record, every time I've successfully dealt with implicit cancellation, it's opt-in - the library author implementing the asynchronous logic decides whether or not it will respond to implicit cancellation. Explicit cancellation ('abort', to use the terminology split Bradley advocated above) would potentially introduce a throw there, but that should be unsurprising, just as a socket being closed due to an ethernet cable getting unplugged would cause a socket operation to throw "unexpectedly". There are some subtle distinctions here that APIs tend to get wrong, of course. An example that might be informative: If you look at sockets, various APIs tend to have differences in how you deal with lifetime & closing handles. In some cases there are subtly different forms operations - for example if you look at Socket in the .NET framework, it exposes *three different/overlapping forms of lifetime management*, through the Shutdown, Close and Dispose methods. If you compare this with Rust's sockets API, the only explicit operation it exposes is 'shutdown', and the other two are expressed by dropping the value. For the average .NET user, you can treat all three as equivalent. For casual use cases you can just Dispose all IDisposable resources (files, sockets, graphics resources...) at the end of a function and be ready to go. But behaviorally they are different, and semantically they are different. A socket Shutdown followed by a Dispose will flush the read/write buffers (which can take time) and allow you to cleanly tear everything down, while a standalone Dispose is an instantaneous destruction of the socket, potentially breaking a transmission in the middle. In practice, this distinction represents that the socket is actually a group of related resources - read/write buffers, a network connection, etc - that can't simply be treated as a single unit with consistent lifetime. Oh yeah, and there's also the detail that you can shut down the write end of a socket but not the read end. :-) If you map this over to the browser, you can trivially come up with equivalent examples. When I issue XHR requests, some of those requests are more important than others. For example, an XHR that is saving important changes to an email draft should not be aborted if the user clicks on a hyperlink to load a new email. However, an XHR that is downloading an inline image for an email *can* be safely aborted at a moment's notice. You can think of this as equivalent to Shutdown/Dispose - you would want to Shutdown the draft save operation, flushing everything out, before closing the socket, and that takes time. In comparison, the image load is implicitly cancelled as soon as the image content is no longer needed, and that can terminate the underlying request if appropriate. On 11 January 2016 at 19:59, Kevin Smithwrote: >> I think F#'s cancellation approach is also worth mentioning in the >> discussion of alternatives as it has implicit but token-based automatically >> propagating cancellation. > > > If you have any good links to reference materials on F#'s cancellation > architecture, feel free to include them for future reference. I was unable > to find anything that clearly explained how implicit cancellation tokens are > discovered by child tasks, for instance. > > I find implicit cancellation to be somewhat sketchy. For async functions, > it would mean that any await expression could potentially throw a > CancelError: > > async function f() { > let a = await 1; // This could throw? What? > } > > And there are certainly going to be scenarios where you *don't* want to > cancel a subtask. It seems to me that cancellation semantics are best left > to the async operation itself, rather than assuming "crash-and-burn". > > > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> > I think F#'s cancellation approach is also worth mentioning in the > discussion of alternatives as it has implicit but token-based automatically > propagating cancellation. > If you have any good links to reference materials on F#'s cancellation architecture, feel free to include them for future reference. I was unable to find anything that clearly explained how implicit cancellation tokens are discovered by child tasks, for instance. I find implicit cancellation to be somewhat sketchy. For async functions, it would mean that any await expression could potentially throw a CancelError: async function f() { let a = await 1; // This could throw? What? } And there are certainly going to be scenarios where you *don't* want to cancel a subtask. It seems to me that cancellation semantics are best left to the async operation itself, rather than assuming "crash-and-burn". ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> Another cancellation scenario is when the consumer of an asynchronous task no longer > needs the result of an operation. In this case, they will only have access to the Promise > unless the cancellation token is routed to them through some other path. For what it's worth - this is exactly how promise cancellation is modeled in bluebird 3 and we have found it very nice. I think the distinction between cancellation because of disinterest and cancellation because we actively want to cancel the operation is very important - props for nailing it. That said - I think most scenarios that work with tokens work without them and vice versa. I think F#'s cancellation approach is also worth mentioning in the discussion of alternatives as it has implicit but token-based automatically propagating cancellation. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
One key thing to recognize is that there are different reasons to cancel an operation and as a result, different approaches to cancellation. .NET cancellation tokens address one scenario (and do it fairly well), where the objective is for specific operations to expose cancellation and allow the caller to set up a way to cancel the operation once it is in progress. This requires co-operation on both sides but is very easy to debug and reason about. However, this does not address all cancellation scenarios. Another cancellation scenario is when the *consumer* of an asynchronous task no longer needs the result of an operation. In this case, they will only have access to the Promise unless the cancellation token is routed to them through some other path. This becomes especially unwieldy if you are dealing with a graph of asynchronous operations, where you want this 'no longer needed' property to propagate through all of the promises. This is somewhat equivalent to how you want a garbage collector to collect a whole tree of objects once they are no longer reachable. In JS (and in my opinion, basically all language environments) you want this to be explicit even if the GC is able to lazily flag the result of a promise as no longer needed. In the 'result no longer needed' scenario there are also cases where you do not want to cancel the operation even if it is not needed anymore. A third form of cancellation is already addressed by Promise implementations - error handling. In this case, an error occurs and the whole asynchronous process (usually) needs to be aborted and unwound so that the error can be responded to. In this scenario cancellation can occur at any time and in some cases it is not possible for the application to continue correctly under these circumstances. You could use exceptions to implement the other forms of cancellation, but it's pretty unwieldy - and injecting exceptions into code that doesn't expect that is a generally bad policy. .NET used to allow injecting exceptions into other threads but has since deprecated it because of all the nasty corner cases it introduces. In my opinion cancellation tokens are a great model that does not require any browser vendor/VM implementer support, but it can be beneficial for implementations of specific operations (i.e. XHR) to provide some sort of cancellation mechanism. Essentially, the XHR object acts as the cancellation token and you call a method on it to cancel. In most cases you can implement that in user space. 'Result no longer needed' requires some amount of support at the language/base framework level, so that it is possible to flag this state on any given Promise and it is possible for all Promise consumers to appropriately propagate this state through graphs of asynchronous dependencies. For example, cancelling a HTML pageload should flag any dependent image/script loads as 'no longer needed', but not necessarily *abort* them (aborting might kill the keepalive connection, and allowing the request to complete might helpfully prime the cache with those resources). -kg On 5 January 2016 at 19:58, Kevin Smithwrote: > Thanks for posting this. Great stuff! > >> >> On a page that loads 100 images four at a time, you would want 4 cleanup >> actions registered, not 100. > > > And in order to keep it to 4 you need a way to unregister the action when > you complete the operation, which the promise API doesn't give you. I see. > > Conceptually, you could of course set some "complete" flag when you finish > the operation and then have the cleanup action early-exit if that flag is > set, but that would be unwieldy. And it wouldn't stop the unbounded growth > of the promise's handler list. > > Interesting! > > > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
Thanks for posting this. Great stuff! > On a page that loads 100 images four at a time, you would want 4 cleanup > actions registered, not 100. > And in order to keep it to 4 you need a way to unregister the action when you complete the operation, which the promise API doesn't give you. I see. Conceptually, you could of course set some "complete" flag when you finish the operation and then have the cleanup action early-exit if that flag is set, but that would be unwieldy. And it wouldn't stop the unbounded growth of the promise's handler list. Interesting! ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
Thanks Ron! Comments inline... > · Once a callback has been registered for asynchronous notification > of a cancellation signal, it can later be unregistered. > Yes, I see how this could be helpful. > · Asynchronous notifications are queued and handled at a higher > priority than Promise continuations. > > · The reason for cancellation can be explicitly supplied. If not > supplied, the reason would be a generic “The operation was canceled” error. > What's the benefit of allowing a user-supplied error? (I assume that by convention the reason should be an error type.) I don't see that feature in .NET. > Since “source.cancel()” is executed synchronously, one would expect that > `p` would be rejected. If the notification were merely added to the same > micro-task queue as Promise “then” tasks, `p` would be resolved first. > I agree that your example, as written, is counter-intuitive. However, is it still counter-intuitive if I rewrite it like this? // Suppose I have "cancelToken" and "cancel" let p = new Promise((resolve, reject) => { Promise.resolve(1).then(resolve); cancelToken.promise.then(reject); cancel(); }); Written like this, it seems to me that the ordering is as expected. Are there use cases that require a higher priority, beyond user expectation based on the API? In .NET, cancellation notifications are executed synchronously, however > this conflicts with the intent to ensure an asynchronous API is > consistently asynchronous. > Right. The trouble I have with introducing an ad-hoc callback API for cancel tokens centers mostly around the handling and propagation of errors. In .NET, exceptions which occur inside of callbacks are propagated synchronously to the caller via an AggregateException error collection. But if the callbacks are truly executed asynchronously, where do the errors go? Do they propagate out to the host, like HTML event handlers? It would be unfortunate if no mechanism was provided by ECMAScript itself to handle such errors. Since we already have a well-defined and well-understood way to propagate errors using promises, I would prefer to have the async registration capability modeled as a promise, if possible. There is that "unregister" issue, though... ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
Since checking `promise.state` is synchronous, we may as well just write a synchronous Cancel class instead: ```js class CancelError extends Error { /* ... */ } class Cancel { constructor() { this.requested = false } request() { this.requested = true } throw() { throw new CancelError() } } async function f(cancel) { await cheapOperation(cancel) // some other function might call cancel.request() while we await here. if (!cancel.requested) await expensiveOperation(cancel) } let cancel = new Cancel cancelButton.onclick = () => cancel.request() f(cancel) ``` Wouldn't this be better than using a Promise if we are relying on a synchronous check anyways? On Mon, Jan 4, 2016 at 12:30 PM, Kevin Smithwrote: >> We have pretty sound cancellation semantics in bluebird 3. > > > Cool, I'm aware that cancellable promises have been explored in depth. I'd > prefer to keep this thread focused on cancellation tokens though, and avoid > comparisons. > > > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
Cool, yeah, the reveal pattern would indeed be a good way to guard against unwanted/unexpected cancels. ```js class Cancel { constructor(executor) { this._requested = false executor(() => this._requested = true) } get requested() {return this._requested} throw() { throw new CancelError() } } let cancel = new Cancel(function(cancel) { cancelButton.onclick = cancel }) ``` What would be the recommended way of keeping the internal state private? With a WeakMap? On Mon, Jan 4, 2016 at 1:01 PM, Kevin Smithwrote: >> Since checking `promise.state` is synchronous, we may as well just >> write a synchronous Cancel class instead: > > > Right - see upthread. You do need some kind of callback method, though, > like `whenCancelled(callback)`. > >> >> class Cancel { >> constructor() { this.requested = false } >> request() { this.requested = true } >> throw() { throw new CancelError() } >> } > > > We need to separate the capability to "read" the cancellation request from > the ability to request the cancellation. That's why in .NET you have > CancellationTokenSource and CancellationToken. But for JS, we should > probably use the revealing constructor pattern instead (discussed upthread): > > let token = new CancelToken(cancel => { ... }); > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> > Since checking `promise.state` is synchronous, we may as well just > write a synchronous Cancel class instead: > Right - see upthread. You do need some kind of callback method, though, like `whenCancelled(callback)`. > class Cancel { > constructor() { this.requested = false } > request() { this.requested = true } > throw() { throw new CancelError() } > } > We need to separate the capability to "read" the cancellation request from the ability to request the cancellation. That's why in .NET you have CancellationTokenSource and CancellationToken. But for JS, we should probably use the revealing constructor pattern instead (discussed upthread): let token = new CancelToken(cancel => { ... }); ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> > We have pretty sound cancellation semantics in bluebird 3. > Cool, I'm aware that cancellable promises have been explored in depth. I'd prefer to keep this thread focused on cancellation tokens though, and avoid comparisons. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> > The best approach in cases like this is to avoid the word altogether. > The fact that there's confusion at all means people will mess it up > and get annoyed, even if there's a "winner" in overall usage. > Hmmm... Maybe class CancelToken { constructor(init); get cancelRequested() : bool; whenCancelRequested(callback) : void; } Or more/overly tersely: class CancelToken { constructor(init); get requested() : bool; whenRequested(callback) : void; } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
We have pretty sound cancellation semantics in bluebird 3. http://bluebirdjs.com/docs/api/cancellation.html Handles multiple subscribers soundly. Solves the common use cases pretty well - has absolutely zero magic and pretty simple semantics. They work with .all and .race too. We have had a lot of positive feedback regarding the change and it works well (at least in my code) with async/await and other newer proposals. Biggest downside is that it hasn't been used for very long yet in production (On 4 Jan 2016, at 19:55, Tab Atkins Jr. wrote: > >> On Mon, Jan 4, 2016 at 9:01 AM, Domenic Denicola wrote: >> From: Kevin Smith [mailto:zenpars...@gmail.com] >> >>> And what's the deal, is it canceled or cancelled? : ) >> >> This is kind of the worst. Previous discussion at >> https://github.com/promises-aplus/cancellation-spec/issues/4. >> >> Data seems to favor cancelled: >> >> - >> https://books.google.com/ngrams/graph?content=canceled%2Ccancelled_start=1800_end=2020=15=3=_url=t1%3B%2Ccanceled%3B%2Cc0%3B.t1%3B%2Ccancelled%3B%2Cc0 >> - http://www.google.com/trends/explore#q=cancelled%2C%20canceled=q >> - http://www.googlefight.com/canceled-vs-cancelled.php > > The best approach in cases like this is to avoid the word altogether. > The fact that there's confusion at all means people will mess it up > and get annoyed, even if there's a "winner" in overall usage. > > On Mon, Jan 4, 2016 at 9:36 AM, Kevin Smith wrote: >>> I am also unsure when .whenCanceled is necessary >> >> Maybe in the case where you have a promise-returning function and you want >> to reject the returned promise upon cancellation. >> >>function delayWithCancel(ms, cancelToken) { >> return new Promise((resolve, reject) => { >>setTimeout(resolve, ms); >>cancelToken.whenCancelled(reject); >> }); >>} > > Yes, forcing people to poll an attribute of a Promise-like thing is > kinda ridic. ^_^ > > ~TJ > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> Cool, I'm aware that cancellable promises have been explored in depth. I'd prefer to keep this thread focused on cancellation tokens though, and avoid comparisons. Cool, re-reading the discussion you asked for this in the first message - I apologize for missing it. I think using promises as tokens would be problematic. It would have several issues: - You'd want to be able to `cancel` things like Promise.all and `Promise.race` - with a separate type you can use "the last argument", with a promise you would not be able. - The names would get confusing for the users. Like Domenic said - `whenCancelled` sounds a lot clearer. Sort of like the function vs. object discussion on es-observable. - You get some weird scenarios like cancelling a cancellation. - You'd probably need to add properties anyway so that tokens can be aggregated. I do definitely see the use for `whenCancelled` and have used its equivalent myself in C#. It's also important to keep in mind that promises can be subclassed so it's fine to add properties to them if used for a specific purpose like cancellation. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> We could use a promise subclass as the cancellation token, but then tokens (and their constructor) would inherit things that really don't make sense, like "CancelToken.resolve" and "CancelToken.prototype.catch". Generally I dislike inheritance. I was merely saying it's an option. I favor composition here as well. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> > I think using promises as tokens would be problematic. It would have > several issues: > Agreed with all of those. It's also important to keep in mind that promises can be subclassed so it's > fine to add properties to them if used for a specific purpose like > cancellation. > We could use a promise subclass as the cancellation token, but then tokens (and their constructor) would inherit things that really don't make sense, like "CancelToken.resolve" and "CancelToken.prototype.catch". On the other hand, it doesn't really make sense to create a new callback API (`whenCancelled`), when we already have the necessary callback semantics with promises. Perhaps we can expose a promise on the token instead: class CancelToken { get requested() : bool; get promise() : Promise; throwIfRequested() : void; } // Registering a callback token.promise.then(_=> doSomething()); // Doing other things with the promise Promise.race(token.promise, someOtherPromise); ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises as Cancelation Tokens
This gist<https://gist.github.com/rbuckton/b69be537e41feec7fea7> contains the TypeScript declarations for the version of CancellationToken I’ve been using for a number of small projects. The basic API shape is: ```ts class CancellationTokenSource { constructor(linkedTokens?: CancellationToken[]); token: CancellationToken; cancel(reason?: any): void; close(): void; } class CancellationToken { static none: CancellationToken; canBeCanceled: boolean; canceled: boolean; reason: any; throwIfCanceled(reason?: any): void; register(callback: (reason?: any) => void): CancellationTokenRegistration; } interface CancellationTokenRegistration { unregister(): void; } ``` The approach I use has the following considerations: • The responsibility for cancellation is split between two types: o CancellationTokenSource – This type is responsible for propagating a cancellation signal and linking multiple tokens together (useful to cancel a graph of async operations, such as cancelling all pending fetch request). o CancellationToken – A read-only sink for a cancellation signal. Asynchronous operations can either synchronously observe whether the cancellation signal has been set, or register an callback for asynchronous notification of a cancellation signal. • Once a callback has been registered for asynchronous notification of a cancellation signal, it can later be unregistered. • Asynchronous notifications are queued and handled at a higher priority than Promise continuations. • The reason for cancellation can be explicitly supplied. If not supplied, the reason would be a generic “The operation was canceled” error. Splitting a cancellation signal across two classes has several benefits. One benefit is that it allows the producer of a cancellation signal to limit access to the ability to initiate that signal. This is important if you wish to have a single shared cancellation source for multiple concurrent asynchronous operations, but need to hand off a cancellation token to a third party without worrying that the third party code would unintentionally cancel operations that it does not itself own. Another benefit is the ability to create more complex cancellation graphs through linking multiple cancellation tokens. I agree with Domenic that it is necessary to be able to synchronously observe the canceled state of a cancellation token, and that it is also necessary to be able to observe a cancellation signal asynchronously. However, I have found that the asynchronous notification needs to be triggered at a higher priority than Promise “then” tasks. Consider the following, albeit contrived, example: ```js let source = new CancellationTokenSource(); let p = new Promise((resolve, reject) => { Promise.resolve(1).then(resolve); source.token.register(reject); source.cancel(); }); ``` Since “source.cancel()” is executed synchronously, one would expect that `p` would be rejected. If the notification were merely added to the same micro-task queue as Promise “then” tasks, `p` would be resolved first. By making the micro-task queue into a priority queue, we can allow cancellation notifications to be processed at a higher priority while still keeping the API itself asynchronous. In .NET, cancellation notifications are executed synchronously, however this conflicts with the intent to ensure an asynchronous API is consistently asynchronous. This approach also allows cancellation tokens to be more general-purpose. By not directly tying cancellation into Promises, they can be used with other future asynchronous primitives such as Observables, async generators, etc. Ron From: Kevin Smith<mailto:zenpars...@gmail.com> Sent: Monday, January 4, 2016 11:44 AM To: Tab Atkins Jr.<mailto:jackalm...@gmail.com>; Domenic Denicola<mailto:d...@domenic.me> Cc: es-discuss<mailto:es-discuss@mozilla.org> Subject: Re: Promises as Cancelation Tokens The best approach in cases like this is to avoid the word altogether. The fact that there's confusion at all means people will mess it up and get annoyed, even if there's a "winner" in overall usage. Hmmm... Maybe class CancelToken { constructor(init); get cancelRequested() : bool; whenCancelRequested(callback) : void; } Or more/overly tersely: class CancelToken { constructor(init); get requested() : bool; whenRequested(callback) : void; } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
>From experience, I'm very much in favor of the cancellation token. Though promises provide a good inspiration for cancellation, they don't quite fit the problem directly. The approach has been explored, though the details are not published. I implemented cancellation for the Midori system. Midori was an incubation project to build a new OS, applications, etc. in a safe language (a C# variant) using lightweight processes communicating via promises (easily 2M+ lines of C#). Among other things, it strongly influenced the C# async support. See http://joeduffyblog.com/2015/11/19/asynchronous-everything/ for more info. After looking at various options, the approach developed for .Net covered all the requirements we had and could be adapted surprisingly well to the async world of promises and Midori. There were some important differences though. In a concurrent, async, or distributed system, cancellation is *necessarily *asynchronous: there's fundamentally a race between a computation "expanding" (spawning more work) and cancellation running it down. But as you note, a cancellation system must support the ability to *synchronously *and cheaply test whether a computation is being cancelled. That allows one for example to write a loop over thousands of records that will abort quickly when cancelled without scheduling activity for the remaining records. (Because of the inherent race in cancellation, I named that "isCancelling" rather than "isCancelled" to be a small reminder that computation elsewhere might not yet have heard that it should stop.) In async cancellation, the promise "then" seems like it could support the asycn "cleanup" action on cancellation. However there are a lot of cancellation use cases in which the appropriate cleanup action changes as a computation proceeds. For those, using "then" is not adequate. For example, a browser would have a cancellation token associated with a page load. Thus the same token is used during parsing, retrieving secondary images, layout, etc. If the user hits "stop", the token is cancelled, and so all the various heterogeneous page rendering activities are cancelled. But the cleanup action to "close the socket that you are retrieving an image over" becomes expensive deadweight once the image has been retrieved. On a page that loads 100 images four at a time, you would want 4 cleanup actions registered, not 100. For that and other reasons, we found it much clearer to give cancellationToken it's own type. That also allows convenient patterns to be directly supported, such as: async function f(cancel) { await cheapOperation(cancel); cancel.*throwIfCancelling*(); // throws if the token is cancelling await expensiveOperation(cancel); } Note that the above would more likely have been written simply: async function f(cancel) { await cheapOperation(cancel); await expensiveOperation(cancel); } The reason is that if the cheapOperation was aborted, the first await would throw (assuming cheapOperation terminates abruptly or returns a broken promise). If it got past cheapOperation, we already know that expensiveOperation is going to be smart enough to cancel, so why clutter our world with redundant aborts? e.g., async function expensiveOperation(cancel) { while (hasFramesToRender() && !cancel.isCancelling()) { await renderFrame(this.nextFrame(), cancel); } } Thus, using cancellation tokens becomes simpler as cancellation becomes more pervasive in the libraries. Typically, if an operation takes a cancellation token as an argument, then you don't need to bother protecting it from cancellation. As a result, explicit cancellation handling tends to only be needed in lower level library implementation, and client code passes their available token to either operations or to the creation of objects (e.g., pass in your token when you open a file rather than on every file operation). On Mon, Jan 4, 2016 at 7:46 AM, Kevin Smithwrote: > I'm interested in exploring the idea of using an approach similar to > .NET's cancelation tokens in JS for async task cancelation. Since the > cancelation "flag" is effectively an eventual value, it seems like promises > are well-suited to modeling the token. Using a promise for a cancelation > token would have the added benefit that the eventual result of any > arbitrary async operation could be used as a cancelation token. > > First, has this idea been fully explored somewhere already? We've > discussed this idea on es-discuss in the past, but I don't remember any > in-depth analysis. > > Second, it occurs to me that the current promise API isn't quite ideal for > cancelation tokens, since we don't have synchronous inspection > capabilities. For example, suppose that we have this async function: > > async function f(cancel) { > let canceled = false; > cancel.then(_=> canceled = true); > await cheapOperation(cancel); >
Re: Promises as Cancelation Tokens
> throw() { throw new CancelError() } > This should be `throwIfRequested` I think, e.g. throwIfRequested() { if (this._requested) throw new CancelError(); } } > What would be the recommended way of keeping the internal state > private? With a WeakMap? > Spec-defined objects can have "internal slots" (essentially private fields), which can be simulated with WeakMaps. Although a polyfill would most likely just use underscored names. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
On Mon, Jan 4, 2016 at 9:01 AM, Domenic Denicolawrote: > From: Kevin Smith [mailto:zenpars...@gmail.com] > >> And what's the deal, is it canceled or cancelled? : ) > > This is kind of the worst. Previous discussion at > https://github.com/promises-aplus/cancellation-spec/issues/4. > > Data seems to favor cancelled: > > - > https://books.google.com/ngrams/graph?content=canceled%2Ccancelled_start=1800_end=2020=15=3=_url=t1%3B%2Ccanceled%3B%2Cc0%3B.t1%3B%2Ccancelled%3B%2Cc0 > - http://www.google.com/trends/explore#q=cancelled%2C%20canceled=q > - http://www.googlefight.com/canceled-vs-cancelled.php The best approach in cases like this is to avoid the word altogether. The fact that there's confusion at all means people will mess it up and get annoyed, even if there's a "winner" in overall usage. On Mon, Jan 4, 2016 at 9:36 AM, Kevin Smith wrote: >> I am also unsure when .whenCanceled is necessary > > Maybe in the case where you have a promise-returning function and you want > to reject the returned promise upon cancellation. > > function delayWithCancel(ms, cancelToken) { > return new Promise((resolve, reject) => { > setTimeout(resolve, ms); > cancelToken.whenCancelled(reject); > }); > } Yes, forcing people to poll an attribute of a Promise-like thing is kinda ridic. ^_^ ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> > I am also unsure when .whenCanceled is necessary > Maybe in the case where you have a promise-returning function and you want to reject the returned promise upon cancellation. function delayWithCancel(ms, cancelToken) { return new Promise((resolve, reject) => { setTimeout(resolve, ms); cancelToken.whenCancelled(reject); }); } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
I agree that without synchronous inspection cancellation tokens become very hard to deal with as you may be queued for a long time prior to cancellation on the event loop, and then the operation is cancelled while you are waiting to check for cancellation. Is there a reason to use a Promise as the cancellation token, rather than have something that is synchronously inspectable? Even a small class/interface like: ```javascript let cancelled = false; myPromise.then(() => cancelled = true); task[Symbol.cancelled] = () => cancelled; ``` For this reason I do not think Promises are the right data type to handle Cancellation. There are several existing uses of cancellation tokens that are just `{[nameForIsCancelledMethod](): {/*return boolean here*/}}`. There are several reasons I don't think Promises should be extended any further, but that causes a divide amongst people wanting them to represent all possible async tasks and me. > I am also unsure when .whenCanceled is necessary (maybe for interfacing > with cancelable systems that are not cancelation token aware, like XHR?). > And it seems likely that the rejection path is not necessary. I don't think so, but you do need to create a way to allow canceling the task from inside of another task (can be done by having a data structure that is passed in to your task runner). I had to do that while working with Node's `.pipe` in https://github.com/bmeck/managed-task/blob/master/lib/examples/lockfile.js#L66 I know VSCode has that event, maybe they can shed light on why? On Mon, Jan 4, 2016 at 10:04 AM, Domenic Denicolawrote: > In general I agree that there is a nice conceptual symmetry, but IMO the > day-to-day impedance mismatch would be simply too great. Compare: > > ```js > async function f(cancelationToken) { > cancelationToken.then(() => console.log("FYI you have been canceled")); > > await cheapOperation(cancelationToken); > if (cancelationToken.state === "fulfilled") { > throw new CanceledOperationError(); > } > await expensiveOperation(cancelationToken); > } > > const token = new Promise(resolve => { > cancelButton.onclick = resolve; > }); > > f(token); > ``` > > to the same thing with different names: > > ```js > async function f(cancelationToken) { > cancelationToken.whenCanceled(() => console.log("FYI you have been > canceled")); > > await cheapOperation(cancelationToken); > if (cancelationToken.canceled) { > throw new CanceledOperationError(); > } > await expensiveOperation(cancelationToken); > } > > const token = new CancelationToken(cancel => { > cancelButton.onclick = cancel; > }); > > f(token); > ``` > > I am also unsure when .whenCanceled is necessary (maybe for interfacing > with cancelable systems that are not cancelation token aware, like XHR?). > And it seems likely that the rejection path is not necessary. > > So, if cancelation tokens are the right path, I'd prefer not reusing > promises for them. Maybe it argues for easily being able to create a > cancelation token from a promise, though. > > Semi-related: I was recently reading > http://joeduffyblog.com/2015/11/19/asynchronous-everything/, which has a > section on Cancelation. > > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
And what's the deal, is it canceled or cancelled? : ) On Mon, Jan 4, 2016 at 11:30 AM Kevin Smithwrote: > Is there a reason to use a Promise as the cancellation token, rather than >> have something that is synchronously inspectable? >> > > The only real downside of coming up with a new interface is that we have > to standardize it. : ) It's a core protocol. > > I agree that using a promise directly would feel awkward without a helper > library. You'd probably also want a method that would throw an error if > the cancel token was activated (as in .NET > https://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.throwifcancellationrequested(v=vs.110).aspx > ): > > cancelToken.throwIfCanceled(); > > Instead of the more verbose: > > if (cancelToken.canceled) > throw new OperationCancelledError(); > > I also like the revealing constructor pattern that Domenic mentioned. > It's almost good enough, as-is, for converting from a promise to a > cancelation token: > > new CancellationToken(cancel => somePromise.then(cancel)); > > Nice! > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises as Cancelation Tokens
In general I agree that there is a nice conceptual symmetry, but IMO the day-to-day impedance mismatch would be simply too great. Compare: ```js async function f(cancelationToken) { cancelationToken.then(() => console.log("FYI you have been canceled")); await cheapOperation(cancelationToken); if (cancelationToken.state === "fulfilled") { throw new CanceledOperationError(); } await expensiveOperation(cancelationToken); } const token = new Promise(resolve => { cancelButton.onclick = resolve; }); f(token); ``` to the same thing with different names: ```js async function f(cancelationToken) { cancelationToken.whenCanceled(() => console.log("FYI you have been canceled")); await cheapOperation(cancelationToken); if (cancelationToken.canceled) { throw new CanceledOperationError(); } await expensiveOperation(cancelationToken); } const token = new CancelationToken(cancel => { cancelButton.onclick = cancel; }); f(token); ``` I am also unsure when .whenCanceled is necessary (maybe for interfacing with cancelable systems that are not cancelation token aware, like XHR?). And it seems likely that the rejection path is not necessary. So, if cancelation tokens are the right path, I'd prefer not reusing promises for them. Maybe it argues for easily being able to create a cancelation token from a promise, though. Semi-related: I was recently reading http://joeduffyblog.com/2015/11/19/asynchronous-everything/, which has a section on Cancelation. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises as Cancelation Tokens
> > Is there a reason to use a Promise as the cancellation token, rather than > have something that is synchronously inspectable? > The only real downside of coming up with a new interface is that we have to standardize it. : ) It's a core protocol. I agree that using a promise directly would feel awkward without a helper library. You'd probably also want a method that would throw an error if the cancel token was activated (as in .NET https://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.throwifcancellationrequested(v=vs.110).aspx ): cancelToken.throwIfCanceled(); Instead of the more verbose: if (cancelToken.canceled) throw new OperationCancelledError(); I also like the revealing constructor pattern that Domenic mentioned. It's almost good enough, as-is, for converting from a promise to a cancelation token: new CancellationToken(cancel => somePromise.then(cancel)); Nice! ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises as Cancelation Tokens
From: Kevin Smith [mailto:zenpars...@gmail.com] > And what's the deal, is it canceled or cancelled? : ) This is kind of the worst. Previous discussion at https://github.com/promises-aplus/cancellation-spec/issues/4. Data seems to favor cancelled: - https://books.google.com/ngrams/graph?content=canceled%2Ccancelled_start=1800_end=2020=15=3=_url=t1%3B%2Ccanceled%3B%2Cc0%3B.t1%3B%2Ccancelled%3B%2Cc0 - http://www.google.com/trends/explore#q=cancelled%2C%20canceled=q - http://www.googlefight.com/canceled-vs-cancelled.php ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises vs Streams
Domenic Denicola wrote: Seeing as how I just produced a completely redundant message by failing to read the other responses before firing off my own, let me try to redeem myself with some more-original content. (Nice post!) It’s also important to realize that streams are not the only asynchronous-plural primitive out there. My favorite analogy is “streams are to asynchronous iterables as arrays are to synchronous iterables.” That is, streams are a specialized data structure, made to efficiently map to lower-level I/O syscalls while still providing a unified abstraction and being an instance of some general async-iteration protocol. Similarly, arrays are a specialized data structure, made to efficiently handle random indexed-access and sequential storage, while still being an instance of the general (synchronous) iteration protocol. A more general overview of this entire space can be found in Kris Kowal’s [General Theory of Reactivity][gtor]. He explains how there's actually several representatives in each of the four quadrants of sync/async + singular/plural. (Or more generally, spatial/temporal + singular/plural.) As you might expect, the most-complicated quadrant (temporal + plural) has the most possible representatives, from async iterators to streams to observables to behaviors to signals. As for what the general async iterable interface might look like, which streams would be a special case of (in the same way arrays are a special case of sync iterables), my favorite candidate at present is https://github.com/zenparsing/async-iteration/. It's a straightforward extension of the synchronous iterable interface, and matches with the GTOR's reasoning quite nicely. But, there's no committee consensus on this. Just noting we haven't pushed for consensus either, we're still doing the hermenuetic spiral. I like Kevin Smith's proposal too, FWIW. /be You'll also notice that, similar to arrays, the more specialized type is being developed and shipped first (in response to a specific need of the platform), with its generalization following behind. All of this, of course, doesn't change the fact that it's useful to distinguish between singular and plural data structures. You get many more guarantees with a singular data structure than you do with a plural---similarly to how you get more guarantees working with, say, a value of type number, than you do with a value of type any. These guarantees can be important for many things, from security invariants to simply being able to reason about the flow of your code. And it's also nice to be able to build one on top of the other, in the way streams are built on top of promises! [gtor]: https://github.com/kriskowal/gtor/ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises vs Streams
The same argument also implies that arrays are more powerful than scalar values, and we should e.g. never use a number when we could instead just use a single-element array with a number. From: es-discuss [mailto:es-discuss-boun...@mozilla.org] On Behalf Of Boopathi Rajaa Sent: Saturday, March 28, 2015 08:15 To: es-discuss@mozilla.org Subject: Promises vs Streams I feel this must have already been discussed but couldn't find any discussion threads, just trying to understand them better. The basic doubt is that I feel promises are more like streams, and that streams are much more powerful than promises. With a promise you have a value or an exception, and with a stream, you have a list of values or an exception. Why do we have both ? or more specifically, since we have both, when to use Promises and when to use Streams ? Whatever I imagine to be a Promise can be thought out to be solved by Streams, and sometimes whenever I use streams, it feels like I'm using similar API as Promises. - Boopathi ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises vs Streams
Seeing as how I just produced a completely redundant message by failing to read the other responses before firing off my own, let me try to redeem myself with some more-original content. It’s also important to realize that streams are not the only asynchronous-plural primitive out there. My favorite analogy is “streams are to asynchronous iterables as arrays are to synchronous iterables.” That is, streams are a specialized data structure, made to efficiently map to lower-level I/O syscalls while still providing a unified abstraction and being an instance of some general async-iteration protocol. Similarly, arrays are a specialized data structure, made to efficiently handle random indexed-access and sequential storage, while still being an instance of the general (synchronous) iteration protocol. A more general overview of this entire space can be found in Kris Kowal’s [General Theory of Reactivity][gtor]. He explains how there's actually several representatives in each of the four quadrants of sync/async + singular/plural. (Or more generally, spatial/temporal + singular/plural.) As you might expect, the most-complicated quadrant (temporal + plural) has the most possible representatives, from async iterators to streams to observables to behaviors to signals. As for what the general async iterable interface might look like, which streams would be a special case of (in the same way arrays are a special case of sync iterables), my favorite candidate at present is https://github.com/zenparsing/async-iteration/. It's a straightforward extension of the synchronous iterable interface, and matches with the GTOR's reasoning quite nicely. But, there's no committee consensus on this. You'll also notice that, similar to arrays, the more specialized type is being developed and shipped first (in response to a specific need of the platform), with its generalization following behind. All of this, of course, doesn't change the fact that it's useful to distinguish between singular and plural data structures. You get many more guarantees with a singular data structure than you do with a plural---similarly to how you get more guarantees working with, say, a value of type number, than you do with a value of type any. These guarantees can be important for many things, from security invariants to simply being able to reason about the flow of your code. And it's also nice to be able to build one on top of the other, in the way streams are built on top of promises! [gtor]: https://github.com/kriskowal/gtor/ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises vs Streams
On Sat, Mar 28, 2015 at 1:14 PM, Boopathi Rajaa legend.r...@gmail.com wrote: Why do we have both? Why do we have both values and arrays, not just the latter? -- https://annevankesteren.nl/ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises vs Streams
Synchronously, we have both normal (synchronous) function calls and iteration over a sequence of values (via `for-of` and iterators). It makes sense that we also should have two abstractions for asynchronous interaction. On 28 Mar 2015, at 13:14, Boopathi Rajaa legend.r...@gmail.com wrote: I feel this must have already been discussed but couldn't find any discussion threads, just trying to understand them better. The basic doubt is that I feel promises are more like streams, and that streams are much more powerful than promises. With a promise you have a value or an exception, and with a stream, you have a list of values or an exception. Why do we have both ? or more specifically, since we have both, when to use Promises and when to use Streams ? Whatever I imagine to be a Promise can be thought out to be solved by Streams, and sometimes whenever I use streams, it feels like I'm using similar API as Promises. - Boopathi ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Dr. Axel Rauschmayer a...@rauschma.de rauschma.de ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises vs Streams
Maybe the confusion stems from how Promises were used in ES5? ES5 doesn't support generators, so people ended up adding a sort of psuedo-generator API to their promise APIs, but in reality the concepts solve different problems? FYI, python seems to use promises and event streams together in its async libraries. Joe On Sat, Mar 28, 2015 at 8:20 AM, Anne van Kesteren ann...@annevk.nl wrote: On Sat, Mar 28, 2015 at 1:14 PM, Boopathi Rajaa legend.r...@gmail.com wrote: Why do we have both? Why do we have both values and arrays, not just the latter? -- https://annevankesteren.nl/ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises, the middle state : negociation
On Thu, May 15, 2014 at 11:33 AM, Michaël Rouges michael.rou...@gmail.com wrote: Hi all, As I totally agree that a promise can only be resolved or rejected once ... so I think his behavior is perhaps too black or white. As IRL, when we receive a promise, we expect that the author makes every effort to resolve it, by any way. Progammatically, we can wish the same. By example, we can have : an error to ignore an error that requires to execute other operations leading to a rejection a continuation a retry To cover all these cases, in the current promises state, we can have (a quite) complex promises, easily solvable. My idea? the negociation, where the goal is to find a solution to resolve it (rejection reduction). For the promise fn argument, to add an optional negociate argument. ``` promise = new Promise(function (resolve, reject, negociate) { // the promise body }); ``` An the related Promise.negociate method like : ``` Promise.negociate = function negociate(resolve, reject, negociate, retry, value) { }; ``` That returns a promise, negociable too, where : resolve is shared with the original promise (that contains the negociate call) to continue the process reject is shared with the original promise (that contains the negociate call) negociate, it's own negociation, for multiple attempts/behaviors retry to recall the original promise fn argument (compliant with the promises concept, since our promise was neither rejected nor resolved, at this point) value (or values, I'm not sure) to pass some infos/tools that we need to take our decision, in the negociation process Lemme your advices. ;) This is just normal promise chaining. If you get a promise and want to make *sure* it resolves in a particular way, just use .then() or .catch() to create a new promise that handles things the way you want. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: Promises, the middle state : negociation
I don't pretend that impossible to do with current promises. I'm just saying that we can get a complexity that could be easily avoided. And a catch/then doesn't allow to return in the first promise onFulfilled, without embedded promises. I think a practical example might better illustrate the thing. Imagine, we have to check a data integrity, firstly internal, or from external sources, if the internal data is corrupted. The integrity controller is a foreign promisable controller , with a custom hash method a fingerprint... we can't touch it. ``` var foreignController; foreignController = (function () { var fingerPrint, hash, controller; fingerPrint = 'aforeignfingerprint'; hash = function hash(data) { // foreign ownmade hash calculator }; controller = function controller(resolve, reject, negociate) { var data, isValid, sources; data = this.data; isValid = fingerPrint === hash(data); if (isValid) { return resolve(data); } sources = this.sources; if (sources.length) { // not an error, we only remove that source // and try with the next one return negociate(sources.shift()); } reject(new Error('No valid data found')); }; return controller; }()); ``` Now, the data ownmade data checker : ``` var getDataSync, onFulFilled, onRejected, onNegociated, handler, controller; getDataSync function getDataSync(source) { // data collector }; onFulFilled = function onFulFilled(value) { // valid data treatment }; onRejected = function onRejected(reason) { // no valid data found }; onNegociated = function onNegociated(resolve, reject, negociate, retry, value) { var source; source = this.sources[0]; this.data = getDataSync(source); console.log('data source corrupted : ' + value); retry(); }; handler = { data: 'some data', sources: ['internal', 'an.external.url', 'another.external.url', '...'] }; controller = foreignController.bind(handler); new Promise(controller) .then(onFulFilled, onRejected, onNegociated.bind(handler)) /* a lot of thens */; ``` Personnaly, I don't see how the current promises can solve it as simply. Michaël Rouges - https://github.com/Lcfvs - @Lcfvs ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: Promises, the middle state : negociation
``` var sources = [internal, external1, external2]; function doStuff (e) { // likely check if it's the right kind of error; var url = sources.shift(); if (typeof url === 'undefined) { return Promise.reject(new Error('out of sources')); } return get(url).then(check).catch(doStuff); } ``` On Thu, May 15, 2014 at 10:52 AM, Michaël Rouges michael.rou...@gmail.comwrote: I don't pretend that impossible to do with current promises. I'm just saying that we can get a complexity that could be easily avoided. And a catch/then doesn't allow to return in the first promise onFulfilled, without embedded promises. I think a practical example might better illustrate the thing. Imagine, we have to check a data integrity, firstly internal, or from external sources, if the internal data is corrupted. The integrity controller is a foreign promisable controller , with a custom hash method a fingerprint... we can't touch it. ``` var foreignController; foreignController = (function () { var fingerPrint, hash, controller; fingerPrint = 'aforeignfingerprint'; hash = function hash(data) { // foreign ownmade hash calculator }; controller = function controller(resolve, reject, negociate) { var data, isValid, sources; data = this.data; isValid = fingerPrint === hash(data); if (isValid) { return resolve(data); } sources = this.sources; if (sources.length) { // not an error, we only remove that source // and try with the next one return negociate(sources.shift()); } reject(new Error('No valid data found')); }; return controller; }()); ``` Now, the data ownmade data checker : ``` var getDataSync, onFulFilled, onRejected, onNegociated, handler, controller; getDataSync function getDataSync(source) { // data collector }; onFulFilled = function onFulFilled(value) { // valid data treatment }; onRejected = function onRejected(reason) { // no valid data found }; onNegociated = function onNegociated(resolve, reject, negociate, retry, value) { var source; source = this.sources[0]; this.data = getDataSync(source); console.log('data source corrupted : ' + value); retry(); }; handler = { data: 'some data', sources: ['internal', 'an.external.url', 'another.external.url', '...'] }; controller = foreignController.bind(handler); new Promise(controller) .then(onFulFilled, onRejected, onNegociated.bind(handler)) /* a lot of thens */; ``` Personnaly, I don't see how the current promises can solve it as simply. Michaël Rouges - https://github.com/Lcfvs - @Lcfvs ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- -Calvin W. Metcalf ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises and Decidability in Asynchronous Error Handling
Requiring early registration prevents the use of futures as value containers; i.e. kicking off an operation and storing the Future somewhere so anyone can use it at a later date. I agree that an improved error handling policy would be very, very good to have, though. On Mon, Oct 21, 2013 at 9:40 AM, Kevin Smith zenpars...@gmail.com wrote: A well-known problem with Promises, as implemented in various Javascript libraries, is that program errors are silenced by default. Consider the following program, which simply makes an HTTP request and then prints out the HTTP response headers: fetchUri(http://someauthority.com/;).then(response = { for (let header of repsonse.heders) // Note the misspelling! console.log(header.key, header.value); }); On line 2, the property headers is misspelled, which should cause the program to report an unhandled error. Under the current Promises specification, however, the error will be silenced and the program will end happily with success. Various solutions have been proposed for dealing with this problem, such as: - Extending debugging tools so that unhandled rejections are visible through a specialized tab or view. - Using the garbage collector to determine when rejections can no longer be handled, and therefore constitute a program error. - Adding a `done` method to the promise type which propagates rejections as program errors. While each of these approaches provides a partial solution to the problem, they are ultimately inadequate because they do not address the underlying cause. The root cause of this issue is that, as currently specified, **the problem of determining whether a particular runtime error has an associated handler is Turing undecidable**. This is *not* a desirable property for an error handling mechanism to have, and it is not a design choice that can be reversed at a later date. In order to make error handling decidable, it is sufficient to require that an error handler must be attached to the promise *within a well-defined window*. One such window would be the lifetime of the currently executing user call stack. The designers of Dart have made a similar decision with their Future API. In the following document, users are instructed to register error handlers early: https://www.dartlang.org/articles/futures-and-error-handling/#potential-problem-failing-to-register-error-handlers-early A quick search on StackOverflow relating to Futures and early error handling in Dart yielded no results. This cursory evidence suggests that requiring early registration of error handlers is not a significant problem for Dart users. In my view, it would be a mistake to standardize the undecidable error handling model of current Promises design. Thanks, { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises and Decidability in Asynchronous Error Handling
A well-known problem with loops, as implemented in various programming languages, is that infinite loops are silenced by default. Consider the following program, which simply adds some numbers in a loop: ```js var sum = 0; while (Math.random() config.loopLimt) { // Note the misspelling! sum += Math.random(); } ``` On line two, the property loopLimit is misspelled, which should cause the program to report an infinite loop as the condition will never be true. Under the current programming language paradigm, however, the error will be silenced and the program will loop happily forever. Various solutions have been proposed for dealing with this problem, such as: - Extending debugging tools to allow some visibility into the code currently running in your program, through a specialized tab or view. - Using CPU consumption to determine when a loop has locked up the program, and thus probably consistutes a program error. - Adding a `safeWhile` construct to the language which ensures you cannot loop more than 64K times. While each of these approaches provides a partial solution to the problem, they are ultimately inadequate because they do not address the underlying cause. The root cause of this issue is that, as currently specified, **the problem of deciding whether a particular `while` loop terminates is Turing undecidable**. This is *not* a desirable property for a looping mechanism to have, and it is not a design choice that can be reversed at a later date. In order to make loop termination decidable, it is sufficient to require that the loop condition must be become true *within a well-defined window*. One such window would be the current second. The designers of regular expressions have made a similar decision with their string matching API. A quick search on StackOverflow relating to loops and infinite loops in regular expressions yielded no results. This cursory evidence suggests that requiring looping constructs to be finite is not a significant problem for regular expression users. In my view, it would be a mistake to standardize the undecidable looping model of the current `while` loop design. --- Which is all to say, being unable to Turing-decide how a programming construct will work is not a particularly noteworthy disqualifier, and indeed opens up extremely powerful features in most cases. I believe the same is true of the current promise design. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises and Decidability in Asynchronous Error Handling
wouldn't events better suit and better solve the problem ? fetchUri(http://someauthority.com/;).on('load', response = { repsonse.heders() }); at least this is how it works in eddy.js and I've never had silent errors in current specs would be an addEventListener() within the XHR object On Mon, Oct 21, 2013 at 12:26 PM, K. Gadd k...@luminance.org wrote: Requiring early registration prevents the use of futures as value containers; i.e. kicking off an operation and storing the Future somewhere so anyone can use it at a later date. I agree that an improved error handling policy would be very, very good to have, though. On Mon, Oct 21, 2013 at 9:40 AM, Kevin Smith zenpars...@gmail.com wrote: A well-known problem with Promises, as implemented in various Javascript libraries, is that program errors are silenced by default. Consider the following program, which simply makes an HTTP request and then prints out the HTTP response headers: fetchUri(http://someauthority.com/;).then(response = { for (let header of repsonse.heders) // Note the misspelling! console.log(header.key, header.value); }); On line 2, the property headers is misspelled, which should cause the program to report an unhandled error. Under the current Promises specification, however, the error will be silenced and the program will end happily with success. Various solutions have been proposed for dealing with this problem, such as: - Extending debugging tools so that unhandled rejections are visible through a specialized tab or view. - Using the garbage collector to determine when rejections can no longer be handled, and therefore constitute a program error. - Adding a `done` method to the promise type which propagates rejections as program errors. While each of these approaches provides a partial solution to the problem, they are ultimately inadequate because they do not address the underlying cause. The root cause of this issue is that, as currently specified, **the problem of determining whether a particular runtime error has an associated handler is Turing undecidable**. This is *not* a desirable property for an error handling mechanism to have, and it is not a design choice that can be reversed at a later date. In order to make error handling decidable, it is sufficient to require that an error handler must be attached to the promise *within a well-defined window*. One such window would be the lifetime of the currently executing user call stack. The designers of Dart have made a similar decision with their Future API. In the following document, users are instructed to register error handlers early: https://www.dartlang.org/articles/futures-and-error-handling/#potential-problem-failing-to-register-error-handlers-early A quick search on StackOverflow relating to Futures and early error handling in Dart yielded no results. This cursory evidence suggests that requiring early registration of error handlers is not a significant problem for Dart users. In my view, it would be a mistake to standardize the undecidable error handling model of current Promises design. Thanks, { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises and Decidability in Asynchronous Error Handling
Requiring early registration prevents the use of futures as value containers; i.e. kicking off an operation and storing the Future somewhere so anyone can use it at a later date. One can always do that, provided that you register an error handler *before your call stack is cleared*. { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises and Decidability in Asynchronous Error Handling
Domenic, First, your caricature of my position is patently ridiculous. Second, can you or someone else offer a clear use case which requires undecidable error handling semantics? I have asked for examples several times and so far I haven't seen anything convincing. Usually that indicates that the supposed use case is exaggerated if not fictional. { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises and Decidability in Asynchronous Error Handling
I don't think that's the same thing at all. Detecting an infinite loop is _extremely_ hard at most cases (and of course impossible at others. However instead of discussing the halting problem, I think what's bothering this guy is that `.then` does not throw an error when an error occurs within it even if that error is completely unhandled by the promise flow. Different libraries that implement promises handle errors differently. For example bluebird[0] writes the stack trace to stderr (or console.error) if the rejection is unhandled by the start of the second turn. I quote: your quote doesn't work as expected and you open console and see a stack trace : nice. On that topic, I'd appreciate some reading on promises being added to the spec of the language (since when does the ES language spec even deal with concurrency? I thought that was a part of the host environment. [0]: https://github.com/petkaantonov/bluebird#error-handling Domenic Denicola dome...@domenicdenicola.com wrote A well-known problem with loops, as implemented in various programming languages, is that infinite loops are silenced by default. Consider the following program, which simply adds some numbers in a loop: ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises and Decidability in Asynchronous Error Handling
You have been given examples in several previous conversations, but have chosen to not find them convincing. I (and others) tire of rehashing this argument with you monthly. I instead responded to the novel content of your post, which was some sort of attempt to claim that a language feature allowing Turing-undecidable control flow means it is not a desirable language feature. Now that I have shown how patently ridiculous such a claim is, I do not feel a need to switch the conversation back to your usual line of inquiry. Perhaps others will take you up on it. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises and Decidability in Asynchronous Error Handling
Kevin I have no idea which library you are using but if you do this: ``` fetchUri(http://someauthority.com/;).then(response = { for (let header of repsonse.heders) // Note the misspelling! console.log(header.key, header.value); }).then(Object, function error(e) { console.err(e); }); ``` Wouldn't that solve your problem? Another thing worth trying is a generic window.on('error') handler ? Wouldn't that notify you ? That does not solve the broken flow but it should be an exit point to at least debug or notify problems, am I missing/misunderstanding something? Thanks for any extra hint, apologies if I cannot help further :-/ On Mon, Oct 21, 2013 at 1:39 PM, Domenic Denicola dome...@domenicdenicola.com wrote: You have been given examples in several previous conversations, but have chosen to not find them convincing. I (and others) tire of rehashing this argument with you monthly. I instead responded to the novel content of your post, which was some sort of attempt to claim that a language feature allowing Turing-undecidable control flow means it is not a desirable language feature. Now that I have shown how patently ridiculous such a claim is, I do not feel a need to switch the conversation back to your usual line of inquiry. Perhaps others will take you up on it. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises and Decidability in Asynchronous Error Handling
+1 Nathan Domenic Denicola wrote: A well-known problem with loops, as implemented in various programming languages, is that infinite loops are silenced by default. Consider the following program, which simply adds some numbers in a loop: ```js var sum = 0; while (Math.random() config.loopLimt) { // Note the misspelling! sum += Math.random(); } ``` On line two, the property loopLimit is misspelled, which should cause the program to report an infinite loop as the condition will never be true. Under the current programming language paradigm, however, the error will be silenced and the program will loop happily forever. Various solutions have been proposed for dealing with this problem, such as: - Extending debugging tools to allow some visibility into the code currently running in your program, through a specialized tab or view. - Using CPU consumption to determine when a loop has locked up the program, and thus probably consistutes a program error. - Adding a `safeWhile` construct to the language which ensures you cannot loop more than 64K times. While each of these approaches provides a partial solution to the problem, they are ultimately inadequate because they do not address the underlying cause. The root cause of this issue is that, as currently specified, **the problem of deciding whether a particular `while` loop terminates is Turing undecidable**. This is *not* a desirable property for a looping mechanism to have, and it is not a design choice that can be reversed at a later date. In order to make loop termination decidable, it is sufficient to require that the loop condition must be become true *within a well-defined window*. One such window would be the current second. The designers of regular expressions have made a similar decision with their string matching API. A quick search on StackOverflow relating to loops and infinite loops in regular expressions yielded no results. This cursory evidence suggests that requiring looping constructs to be finite is not a significant problem for regular expression users. In my view, it would be a mistake to standardize the undecidable looping model of the current `while` loop design. --- Which is all to say, being unable to Turing-decide how a programming construct will work is not a particularly noteworthy disqualifier, and indeed opens up extremely powerful features in most cases. I believe the same is true of the current promise design. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises and Decidability in Asynchronous Error Handling
It has been shown that delayed registration, in general, is useful. However, it has not been demonstrated that delayed registration of a primary error handler is necessary. If use cases have been provided, then please provide links. Otherwise, let's not use ad hominem in place of logic. { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
I have not read everything about the promise/future/re-promise subject but what I have read seems to show that everyone has a personal understanding of the thing. So please see http://lists.w3.org/Archives/Public/public-webcrypto/2013Sep/0003.html , code example that I have written for WebCrypto (ie real working case not using WebCrypto rewritten with WebCrypto promises), as explained I am using 'done' despite of the fact that it might be removed, because I don't see why I should use 'then' if I am not chaining anything. As explained again, the example shows maybe that promises here are a kind of artifice, until other APIs implement promises. How should I write this without 'done'? Regards Aymeric Le 08/09/2013 19:06, Anne van Kesteren a écrit : (Added back the other lists.) On Fri, Sep 6, 2013 at 3:58 AM, Brendan Eich bren...@secure.meer.net wrote: Let's put done back in. It's the right thing. Given what has been said thus far https://github.com/domenic/promises-unwrapping/issues/19 my inclination is still to leave it out initially and give a version without done() six months to a year to mature. Not having done() can make promises harder to debug in the short term, but adding done() is trivial to do later. And given the lack of native promise implementations to date there's no way for us to test the done()-less design without trying it first. -- jCore Email : avi...@jcore.fr Peersm : http://www.peersm.com iAnonym : http://www.ianonym.com node-Tor : https://www.github.com/Ayms/node-Tor GitHub : https://www.github.com/Ayms Web :www.jcore.fr Extract Widget Mobile : www.extractwidget.com BlimpMe! : www.blimpme.com ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Subject=Re: Re: Promises: final steps
Tasks in C# throw the recorded exception when the Task is finalized by the GC if it hasn't been handled by user code, though I don't know if something similar could be supported for ES7 Promises nor whether or not that makes sense for ES7 promises either. This is an interesting avenue. The problem is the chain-ability and value like nature of promises. It is difficult to truly determine that the error has not been handled. The error may be handled later down the chain or in a separate context. When is the decision made that the error has not been handled? Is the error determined not handled on garbage collection? Is there a ttl on error handling? This is a curious case, but if feasible would be the most elegant option. rejectedPromise = promise.then(func) .then(func) //Error swallowed .then(func); setTimeout(function () { rejectedPromise.then(func, errorHandler); //Finally handle it }, 2); ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Subject=Re: Re: Promises: final steps
As your example points out, being unhandled is a changing state. All rejections start out unhandled, but can become handled either immediately or in a later turn. The only time in which you can be sure a rejection is unhandled is when the promise is garbage collected, which may never happen (but usually does). From: es-discuss [mailto:es-discuss-boun...@mozilla.org] On Behalf Of e...@evan-borden.com Sent: Sunday, September 8, 2013 20:25 To: To=; es-discuss@mozilla.org Subject: Subject=Re: Re: Promises: final steps Tasks in C# throw the recorded exception when the Task is finalized by the GC if it hasn't been handled by user code, though I don't know if something similar could be supported for ES7 Promises nor whether or not that makes sense for ES7 promises either. This is an interesting avenue. The problem is the chain-ability and value like nature of promises. It is difficult to truly determine that the error has not been handled. The error may be handled later down the chain or in a separate context. When is the decision made that the error has not been handled? Is the error determined not handled on garbage collection? Is there a ttl on error handling? This is a curious case, but if feasible would be the most elegant option. rejectedPromise = promise.then(func) .then(func) //Error swallowed .then(func); setTimeout(function () { rejectedPromise.then(func, errorHandler); //Finally handle it }, 2); ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
While I can agree that monitor feature (that's proposed instead of `done`) has some benefits, I see it only as an aid for developers that are inexperienced with promises, or as a fallback for those experienced. It looks more as a smart add-on, which to be complete can't be implemented in plain JavaScript, is implementation specific and should be treated as optional. What's more important it still doesn't provide developer with full control of error handling on JavaScript level and that can be achieved only with `done`. I have problems understanding why such complex and not natural feature is favored over something so simple and straightforward. -- View this message in context: http://mozilla.6506.n7.nabble.com/Promises-final-steps-tp290303p290518.html Sent from the Mozilla - ECMAScript 4 discussion mailing list archive at Nabble.com. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
Hi Kris, Thanks for the details! This gives us an idea what a promise monitoring feature might look like in a browser's developer tools. I think such a feature would be really cool, but I believe that promise-using programs ought to be debuggable using just a console. Indeed, for a non-GUI embedding like Node, they *must* be debuggable using just a console. I don't think we should ship an API that is not debuggable using a console. However, I'm *not* in favor of a `done` method on the Promise prototype because of functional overlap with `then`. Another option is a static method which takes a promise and throws rejections ala done: Promise.throw(makeSomePromise.then(...)); Personally, I consider it a shame that promise libraries punted on the distinction between rejections and program errors, but I suppose it's too late to go there. { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises: final steps
From: Kevin Smith [zenpars...@gmail.com] Indeed, for a non-GUI embedding like Node, they *must* be debuggable using just a console. This is an important point. A provisional idea that preserves our desire to not introduce new features to promises themselves, requiring user choice at authoring time, might be some kind of `console.unhandledRejections()` function which returns you a snapshot of the current unhandled rejections bucket. Another option is a static method which takes a promise and throws rejections ala done: ```js Promise.throw(makeSomePromise.then(...)); ``` I find these kind of things confusing. RSVP did something similar, introducing ```js RSVP.rethrow = r = setImmediate(() = throw r); ``` so that you write ```js somePromise.then(...).catch(RSVP.rethrow); // actually RSVP uses `fail`. ``` It's not clear to me why this, or your `Promise.throw`, is better than ```js somePromise.done(...) // or somePromise.then(...).done() ``` ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
It's not clear to me why this, or your `Promise.throw`, is better than ```js somePromise.done(...) // or somePromise.then(...).done() ``` Not *much* better, I'd say, but IMO a `done` method which accepts a callback overlaps too much with `then`, and a `done` method without a callback just looks like a wart in need of removal. : ) Visually, to me, wrapping beats capping. But that's just me. In the end, I like neither wrapping nor capping. In my own work I've preferred to define conditions under which certain rejections are interpreted as program errors, thereby avoiding the wrapping/capping issue entirely. (I can elaborate if anyone's interested.) { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
On Thu, Sep 5, 2013 at 12:04 PM, Domenic Denicola dome...@domenicdenicola.com wrote: From: Kevin Smith [zenpars...@gmail.com] Indeed, for a non-GUI embedding like Node, they *must* be debuggable using just a console. This is an important point. A provisional idea that preserves our desire to not introduce new features to promises themselves, requiring user choice at authoring time, might be some kind of `console.unhandledRejections()` function which returns you a snapshot of the current unhandled rejections bucket. That would be a global communications channel. Another option is a static method which takes a promise and throws rejections ala done: ```js Promise.throw(makeSomePromise.then(...)); ``` I find these kind of things confusing. RSVP did something similar, introducing ```js RSVP.rethrow = r = setImmediate(() = throw r); ``` so that you write ```js somePromise.then(...).catch(RSVP.rethrow); // actually RSVP uses `fail`. ``` It's not clear to me why this, or your `Promise.throw`, is better than ```js somePromise.done(...) // or somePromise.then(...).done() ``` As I said, I think we will eventually add something that provides this or similar functionality. But it does not need to be added to the subset this repository seeks to define -- for the immediate needs of DOM. Since the topic of this thread is final steps, I'm just trying to be clear in this context that this .done-ish issue is after these final steps. This is the kind of issue that is best decided after more experience with several of these debugging aids, in both browser and server. So we should of course continue to discuss these options on es-discuss regarding ES7. Also, we won't have Weak References until ES7, and that bears on this discussion and expected experience. FWIW, so far, I still like .done better than the suggested alternatives. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Text by me above is hereby placed in the public domain Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
On Thu, Sep 5, 2013 at 1:21 PM, Kevin Smith zenpars...@gmail.com wrote: It's not clear to me why this, or your `Promise.throw`, is better than ```js somePromise.done(...) // or somePromise.then(...).done() ``` Not *much* better, I'd say, but IMO a `done` method which accepts a callback overlaps too much with `then`, and a `done` method without a callback just looks like a wart in need of removal. : ) Visually, to me, wrapping beats capping. But that's just me. In the end, I like neither wrapping nor capping. In my own work I've preferred to define conditions under which certain rejections are interpreted as program errors, thereby avoiding the wrapping/capping issue entirely. (I can elaborate if anyone's interested.) Please do. { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises: final steps
Tasks in C# throw the recorded exception when the Task is finalized by the GC if it hasn't been handled by user code, though I don't know if something similar could be supported for ES7 Promises nor whether or not that makes sense for ES7 promises either. Having Promise rejections hold on to unhandled rejections can be mitigated either with eventual language support around Promises (either an async/await-style operator, or the ! operator in the strawman) or through a userland approach by way of trampoline functions. In the C# world with async/await, exceptions are thrown at the site of the await keyword when the operand becomes Faulted. In general this alleviates many of the issues around Tasks swallowing exceptions, although there are still a few cases where you have to take care around exception handling (e.g. an async method that returns `void` rather than `Task` or `TaskT`, as it cannot be awaited). Promises in the short-term may have some deficiencies until there is a syntax defined for asynchronous functions. That said, I'm fine with not having `Promise#done`, as useful as it is, as long as there is a reliable and well defined mechanism to raise the rejection to the host if needed (outside of reaching out to setImmediate to throw the exception, as it may not be obvious to consumers of the Promise API). Ron -Original Message- From: es-discuss [mailto:es-discuss-boun...@mozilla.org] On Behalf Of medikoo Sent: Thursday, September 5, 2013 2:46 AM To: es-discuss@mozilla.org Subject: Re: Promises: final steps While I can agree that monitor feature (that's proposed instead of `done`) has some benefits, I see it only as an aid for developers that are inexperienced with promises, or as a fallback for those experienced. It looks more as a smart add-on, which to be complete can't be implemented in plain JavaScript, is implementation specific and should be treated as optional. What's more important it still doesn't provide developer with full control of error handling on JavaScript level and that can be achieved only with `done`. I have problems understanding why such complex and not natural feature is favored over something so simple and straightforward. -- View this message in context: http://mozilla.6506.n7.nabble.com/Promises- final-steps-tp290303p290518.html Sent from the Mozilla - ECMAScript 4 discussion mailing list archive at Nabble.com. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
This looks like good work. I like the name cast in particular, as I can imagine a future casting operator which provides sugar for the cast function defined on a constructor. The only concern I have is over error-swallowing. What's the approach for this minimal API? { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
On Wed, Sep 4, 2013 at 7:34 AM, Kevin Smith zenpars...@gmail.com wrote: This looks like good work. I like the name cast in particular, as I can imagine a future casting operator which provides sugar for the cast function defined on a constructor. The only concern I have is over error-swallowing. What's the approach for this minimal API? As far as I know, the current plan is still that devtools should handle swallowed exceptions, since we got rid of .done() some time ago. Plans may have changed without me knowing, though! ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
As far as I know, the current plan is still that devtools should handle swallowed exceptions, since we got rid of .done() some time ago. Plans may have changed without me knowing, though! ~TJ I'd be interested in more detail since I think this will be an important usability issue. Are any of the various devtool efforts currently implementing or planning such a feature? { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
My colleagues and I are working on an extension for Chrome Web Inspector that can communicate with promise libraries, particularly Q, over the window message port. The tool, which will be renamed and rewritten before it is ready for general use, adds a Promises tab to Web Inspector that shows all currently pending and unhandled asynchronous errors, as well as stack traces for both, and also progress information, albeit determinate, indeterminate, or indeterminate but lively. There is a video accompanying for demonstration. https://github.com/montagejs/continuum https://www.dropbox.com/s/2h68ax9j5mj7i6c/continuum.mov The promise client broadcasts when a promise is deferred, when a deferred promise is resolved, when a deferred promise makes progress (through the `deferred.notify` interface), when a fulfilled promise is created, when a rejected promise is created, and when a rejection is handled. As such, the inspector can reconstruct whatever portion of the program’s promise history it elects to retain. In time, I intend to formalize a protocol. Ideally this system would be useful for both “primordial” and library promises, and combinations of both. Of course, any assistance would be valuable. Also, ideally this would approach the functionality available to Causeway and perhaps even *become* a manifestation of Causeway. https://code.google.com/p/causeway/wiki/CausewayIntroduction It would certainly be possible to show promises in multiple contexts, including cross-origin iframes, and even show time sequence / Stevens graphs for message passing between promises in multiple JavaScript contexts, when promises are used as proxies for remote objects through a facility like Q-Connection https://github.com/kriskowal/q-connection Kris Kowal ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises: final steps
On Tue, Sep 3, 2013 at 6:37 AM, Anne van Kesteren ann...@annevk.nl wrote: As many of you hopefully know, we're trying to nail down the design of promises in JavaScript so we can declare consensus on it and start shipping it in implementations. If you're interested in the particulars I strongly recommend reading through https://github.com/domenic/promises-unwrapping/blob/master/README.md and partaking in the https://github.com/domenic/promises-unwrapping/issues discussion. The next TC39 is coming close and this really needs to be resolved (not settled!) by then as there are many APIs relying on promises now. Eg in https://github.com/domenic/promises-unwrapping/issues/8 we decided on Promise.cast() as IsPromise(x) ? x : Promise.resolve(x). https://github.com/domenic/promises-unwrapping/issues/18 suggests adding Promise.prototype.finally() and https://github.com/domenic/promises-unwrapping/issues/13 discusses which convenience methods we should add in the first iteration. I suggest we focus on the minimal subset that works (which I know is different for people, but let's aim for consensus) and then iterate again after we have a couple of implementations out there. I'm fine with the subset defined in Domenic's spec - it's compatible with what we've discussed, and is easy to extend into the flatMap semantics I want. I've commented on the issue threads in github. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises Consensus
From: Mark S. Miller [erig...@google.com] No. Assuming that p and q are both promises and that q is pending, p is resolved to q when either p adopts q or p accepts q. From the .then perspective these are the same, so we'd say p follows q or p is resolved to q. In neither care would p.then fire until q is settled (fulfilled or rejected). However, there's an operational difference between p adopts q and p accepts q at the .flatMap level: p adopts q does not fire p.flapMap. p accepts q does fire p.flatMap with q as the acceptance value. After being confused on this point for a while, I hashed it out with Tab over IRC (thanks Tab!) and thought I'd share my moment of enlightenment with all involved. ```js var foreverPending = new Promise(() = {}); var notAcceptedAndNotResolved = Promise.resolve(foreverPending); var acceptedButNotResolved = Promise.fulfill(foreverPending); // Neither of them are fulfilled, so the distinction doesn't matter for `then` usage. notAcceptedAndNotResolved.then(() = console.log(this will never happen (never fulfilled))); acceptedButNotResolved.then(() = console.log(this will never happen (never fulfilled))); // But it matters for `flatMap` usage. notAcceptedAndNotResolved.flatMap(() = console.log(this will never happen (never accepted))); acceptedButNotResolved.flatMap(() = console.log(this *will* happen)); ``` ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises Consensus
Er, replace `notAcceptedAndNotResolved` with `resolvedButNotAccepted`. X_x ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
On Fri, Aug 2, 2013 at 4:58 PM, Anne van Kesteren ann...@annevk.nl wrote: On Thu, Aug 1, 2013 at 8:25 PM, Tab Atkins Jr. jackalm...@gmail.com wrote: On Thu, Aug 1, 2013 at 11:27 AM, Mark S. Miller erig...@google.com wrote: For #2, since whatever DOM does quickly becomes a compat constraint on all future decisions, DOM should take the minimum subset of #1 which actually meets their short term needs. I leave it to those who understand those needs to argue about what these are. Unfortunately, the thenable question still impinges on the minimum subset. :/ It seems there might be consensus on that now? It just seems safer and loads simpler to start out with deploying a subset that's more commonly agreed upon. This remained unanswered for some reason. Promises are spreading like wildfire through Gecko and B2G now. Chrome is implementing them. Getting the details nailed down, or at least agreement on the /A+ subset, and then record them in http://dom.spec.whatwg.org/ as a start would be great. -- http://annevankesteren.nl/ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
Le 1 août 2013 à 00:53, Claude Pache claude.pa...@gmail.com a écrit : Le 31 juil. 2013 à 20:23, Tab Atkins Jr. jackalm...@gmail.com a écrit : The first issue still up for community discussion involves the definition of promise-like. We'd like the definition to be: (a) a Promise or subtype, or (b) a branded non-Promise (with the branding done via Symbol or similar). Promises/A+ wants the branding to be done via a method named then (the thenable concept). This, unfortunately, goes directly against TC39 practices in a number of other areas, such as iterators, where we don't want short string names as branding due to the possibility of collision. (In the case of then, collision isn't a possibility, it's a certainty - we *know* there are libraries out there today that put a then method on their objects without referring to Promises.) Thoughts? I suggest an @@isPromise builtin symbol, which works the same way as @@isRegExp in the ES6 spec [1]: An object is reputed to be a promise if and only if it has a property (either own or inherited) named @@isPromise. And `Promise.prototype` has initially an @@isPromise own property, so that instances of subclasses of `Promise` are recognised as promises. (With this solution, you have not to choose between subclassing or branding, but you have the both. :-) ) —Claude [1] search the occurrences of @@isRegExp in: http://people.mozilla.org/~jorendorff/es6-draft.html One more idea: a `Promise.register` function, which takes a class (i.e. a constructor) `C` as argument, and whose purpose is to declare that instances of `C` are to be treated as promises. Concretely, if the @@isPromise design is retained, that function can be implemented as following: ``` Promise.register = function(C) { C.prototype[@@isPromise] = true } ``` But the trick with the symbol is an implementation detail. —Claude ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
On Thu, Aug 1, 2013 at 8:25 PM, Tab Atkins Jr. jackalm...@gmail.com wrote: On Thu, Aug 1, 2013 at 11:27 AM, Mark S. Miller erig...@google.com wrote: For #2, since whatever DOM does quickly becomes a compat constraint on all future decisions, DOM should take the minimum subset of #1 which actually meets their short term needs. I leave it to those who understand those needs to argue about what these are. Unfortunately, the thenable question still impinges on the minimum subset. :/ It seems there might be consensus on that now? It just seems safer and loads simpler to start out with deploying a subset that's more commonly agreed upon. -- http://annevankesteren.nl/ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
Le 01/08/2013 20:27, Mark S. Miller a écrit : whatever DOM does quickly becomes a compat constraint on all future decisions (was the comma omitted on purpose? ;-) ) This also means that if there is a delta between the agreement and the implementation, we'll have yet another de facto standard. There are two ways in which this can happen: * misunderstanding of intent * misexpression of (even perfectly-)understood intent. 1) What should tc39 do quickly, to unblock the DOM's need for promises and avoid a design fork? I apologize for being annoyingly insistent with this, but: tests. An unwanted de facto standard is a de facto design fork. I believe that to a large extent, the risk of both misunderstanding of intent and misexpression understood intent would be largely reduced by a test suite. The people who (will) implement this feature aren't necessarily the ones involved in this, sometimes very subtle, discussion. I believe fewer information would be lost if the latters formalized their agreement in tests for the formers to implement against. David [1] https://bugzilla.mozilla.org/show_bug.cgi?id=856410 ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
David Bruant wrote: I believe that to a large extent, the risk of both misunderstanding of intent and misexpression understood intent would be largely reduced by a test suite. +∞ /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises Consensus with /A+ terminology
From: Mark S. Miller [erig...@google.com] A good start would be to convert https://github.com/promises-aplus/promises-tests to test262 form, extending test262 in the process in order to accommodate async testing. Any volunteers? If someone does the latter (preferably with a simple Mocha-like `done()` facility), I will happily do the former. I imagine there might be licensing issues with non-Ecma members; would that still be the case for code licensed under the WTFPL? The only divergence between DOM promises and Promises/A+ so far are: 1. The handling of non-`undefined`, non-function arguments, which Promises/A+ mandates must be ignored while DOM Promises mandate must throw a synchronous `TypeError`. (This is a spec bug; it should result in an asynchronous `TypeError` rejection.) 2. DOM Promises requires `onFulfilled` and `onRejected` to be called as if they were methods of the promise itself, whereas Promises/A+ requires they be called as functions. 3. DOM Promises mandates an infinite loop for the code `const q = fulfilledPromise.then(() = fulfilledPromise)`, whereas Promises/A+ mandates that `q` be rejected with a `TypeError`. 4. DOM Promises mandates an infinite loop for the code `const q1 = fulfilledPromise.then(() = q2); const q2 = fulfilledPromise.then(() = q1)`, whereas Promises/A+ allows (but does not require) that implementations reject `q1` and `q2` with a `TypeError`. Of these, 1 I am ambivalent on, 2 I think was a very strange mistake, and 3 and 4 feel like oversights. But none of them are a big deal. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
On Fri, Aug 2, 2013 at 2:28 PM, Domenic Denicola dome...@domenicdenicola.com wrote: From: Mark S. Miller [erig...@google.com] A good start would be to convert https://github.com/promises-aplus/promises-tests to test262 form, extending test262 in the process in order to accommodate async testing. Any volunteers? If someone does the latter (preferably with a simple Mocha-like `done()` facility), I will happily do the former. I imagine there might be licensing issues with non-Ecma members; would that still be the case for code licensed under the WTFPL? Damn. I'm glad you raised this. Yes, there are issues. I don't know what they are. Would someone more knowledgeable of this minefield like to answer? Thanks. The only divergence between DOM promises and Promises/A+ so far are: 1. The handling of non-`undefined`, non-function arguments, which Promises/A+ mandates must be ignored while DOM Promises mandate must throw a synchronous `TypeError`. (This is a spec bug; it should result in an asynchronous `TypeError` rejection.) 2. DOM Promises requires `onFulfilled` and `onRejected` to be called as if they were methods of the promise itself, whereas Promises/A+ requires they be called as functions. 3. DOM Promises mandates an infinite loop for the code `const q = fulfilledPromise.then(() = fulfilledPromise)`, whereas Promises/A+ mandates that `q` be rejected with a `TypeError`. 4. DOM Promises mandates an infinite loop for the code `const q1 = fulfilledPromise.then(() = q2); const q2 = fulfilledPromise.then(() = q1)`, whereas Promises/A+ allows (but does not require) that implementations reject `q1` and `q2` with a `TypeError`. Of these, 1 I am ambivalent on, 2 I think was a very strange mistake, and 3 and 4 feel like oversights. But none of them are a big deal. I think tc39 quick consensus promises should not gratuitously differ from promises/A+, i.e., they should only differ when there's a well motivated reason, as with the addition of .flatMap and the shift of recursive flattening from the output side of .then to the input side. On #1, I like your parenthetical variant on DOM promise behavior (async rejection) better than either what promises/A+ does or what DOM currently mandates. On the others, tc39 consensus promises should follow promises/A+. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Text by me above is hereby placed in the public domain Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
On Fri, Aug 2, 2013 at 2:42 PM, Mark Miller erig...@gmail.com wrote: On Fri, Aug 2, 2013 at 2:28 PM, Domenic Denicola dome...@domenicdenicola.com wrote: From: Mark S. Miller [erig...@google.com] A good start would be to convert https://github.com/promises-aplus/promises-tests to test262 form, extending test262 in the process in order to accommodate async testing. Any volunteers? If someone does the latter (preferably with a simple Mocha-like `done()` facility), I will happily do the former. I imagine there might be licensing issues with non-Ecma members; would that still be the case for code licensed under the WTFPL? Damn. I'm glad you raised this. Yes, there are issues. I don't know what they are. Would someone more knowledgeable of this minefield like to answer? Thanks. The only divergence between DOM promises and Promises/A+ so far are: 1. The handling of non-`undefined`, non-function arguments, which Promises/A+ mandates must be ignored while DOM Promises mandate must throw a synchronous `TypeError`. (This is a spec bug; it should result in an asynchronous `TypeError` rejection.) 2. DOM Promises requires `onFulfilled` and `onRejected` to be called as if they were methods of the promise itself, whereas Promises/A+ requires they be called as functions. 3. DOM Promises mandates an infinite loop for the code `const q = fulfilledPromise.then(() = fulfilledPromise)`, whereas Promises/A+ mandates that `q` be rejected with a `TypeError`. 4. DOM Promises mandates an infinite loop for the code `const q1 = fulfilledPromise.then(() = q2); const q2 = fulfilledPromise.then(() = q1)`, whereas Promises/A+ allows (but does not require) that implementations reject `q1` and `q2` with a `TypeError`. Of these, 1 I am ambivalent on, 2 I think was a very strange mistake, and 3 and 4 feel like oversights. But none of them are a big deal. I think tc39 quick consensus promises should not gratuitously differ from promises/A+, i.e., they should only differ when there's a well motivated reason, as with the addition of .flatMap and the shift of recursive flattening from the output side of .then to the input side. On #1, I like your parenthetical variant on DOM promise behavior (async rejection) better than either what promises/A+ does or what DOM currently mandates. I retract this. Although I would like it better, it is not worth diverging from promises/A+ on this one. Let's stick to promises/A+ except where it really matters (.flatMap and recursive unwrapping on input side) On the others, tc39 consensus promises should follow promises/A+. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Text by me above is hereby placed in the public domain Cheers, --MarkM -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
On Thu, Aug 1, 2013 at 5:07 PM, Anne van Kesteren ann...@annevk.nl wrote: I basically took Tab's email and rewrote the terminology. I omitted the issues for brevity. Hopefully this helps. Having done that. I wonder if we could leave the monad part out for now. As Mark pointed out in the other thread it causes a bunch of headaches to get that correct, and since we already decided (I believe) to not break with existing practice we could ship the subset that is that and figure out the superset-promise-that-works-for-monads later. That might also give us some insight into how many people will want to wrap promises to make the monad-suitable. -- http://annevankesteren.nl/ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
On Thu, Aug 1, 2013 at 9:09 AM, Anne van Kesteren ann...@annevk.nl wrote: On Thu, Aug 1, 2013 at 5:07 PM, Anne van Kesteren ann...@annevk.nl wrote: I basically took Tab's email and rewrote the terminology. I omitted the issues for brevity. Hopefully this helps. Sorry, I was waiting until Mark and Domenic had finished up their terminology discussion before I did a third rewrite. Having done that. I wonder if we could leave the monad part out for now. As Mark pointed out in the other thread it causes a bunch of headaches to get that correct, and since we already decided (I believe) to not break with existing practice we could ship the subset that is that and figure out the superset-promise-that-works-for-monads later. That might also give us some insight into how many people will want to wrap promises to make the monad-suitable. Let's not reopen this, please. The way I've outlined things means that then()-based stuff works compatibly with the existing spec, so that's not a concern. We've already had long threads about why nested promises are useful (namely, that promise in practice isn't a single type - you have multiple types of promises from different promise sources, and don't always want to smash them together). ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
If then() deep flattens, flatMap() only flattens one level and promises assimilate thenables, is branding really necessary? Juan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
On the input side of .then and .flatMap, no. On the output side of both .then and .flatMap, depending on what you mean by branding, yes. If .flatMap's callback returns a non-promise the promise it already returned gets rejected. If .then's callback returns a non-promise, the promise it already returned accepts that non-promise. On Thu, Aug 1, 2013 at 10:33 AM, Juan Ignacio Dopazo dopazo.j...@gmail.comwrote: If then() deep flattens, flatMap() only flattens one level and promises assimilate thenables, is branding really necessary? Juan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
On Thu, Aug 1, 2013 at 10:33 AM, Juan Ignacio Dopazo dopazo.j...@gmail.com wrote: If then() deep flattens, flatMap() only flattens one level and promises assimilate thenables, is branding really necessary? The concept of thenable *is* branding. It's just branding with a short, simple string property (then), rather than branding with a hard-to-collide string (like __promiseBrand__ or something) or a symbol. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
Between Tab's answer and mine, we see the two issues one might mean by branding. Anne's clarifying summary at https://mail.mozilla.org/pipermail/es-discuss/2013-August/032465.htmlspeaks only in terms of promise-likes. One of the things we need to settle is whether there is one predicate that applies to all the places in this summary that says if ... promise-like. IMO we need to distinct tests -- * isPromise for whether p is a promise and * isThenable for whether p is thenable. I would have the output-side tests for both .flatMap and .then be isPromise. I would also have the input side test for .then use isThenable. .flatMap has no input-side test, which is its point. A somewhat separable question is what these two tests are testing. The first hard reason why we must at least have an isPromise test on the output side of .then is that the promise already returned by .then must adopt this output promise without calling its .then method. Otherwise we lose support for lazy evaluation and for promise pipelining. If the output side of .then is a non-promise thenable, it is a separate question whether it should be adopted by calling its .then method or whether it should be accepted. IMO it should be accepted. The isThenable test indicates something ugly is going on -- assimilation. With this AP2 based design, we can isolate this ugliness to the input side of .then. The second reason why the two tests need to be separate is that the output side of .flatMap cannot adopt simply by calling the output's .then method, because otherwise you'd often get exactly the recursive unwrapping that .flatMap is trying to avoid. In order to avoid this, it must test whether there is anything it can do to adopt other than calling .then. On Thu, Aug 1, 2013 at 10:43 AM, Tab Atkins Jr. jackalm...@gmail.comwrote: On Thu, Aug 1, 2013 at 10:33 AM, Juan Ignacio Dopazo dopazo.j...@gmail.com wrote: If then() deep flattens, flatMap() only flattens one level and promises assimilate thenables, is branding really necessary? The concept of thenable *is* branding. It's just branding with a short, simple string property (then), rather than branding with a hard-to-collide string (like __promiseBrand__ or something) or a symbol. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
On Thu, Aug 1, 2013 at 11:04 AM, Mark S. Miller erig...@google.com wrote: Between Tab's answer and mine, we see the two issues one might mean by branding. No, there's only the one concept. If you mean anything other than type detection via properties on the instance or its prototype chain, then I don't think you're using the right word. (If I'm wrong, please correct me.) Detecting thenables via the presence of a function-valued then property on the object is, explicitly, branding. Anne's clarifying summary at https://mail.mozilla.org/pipermail/es-discuss/2013-August/032465.html speaks only in terms of promise-likes. One of the things we need to settle is whether there is one predicate that applies to all the places in this summary that says if ... promise-like. IMO we need to distinct tests -- * isPromise for whether p is a promise and * isThenable for whether p is thenable. I would have the output-side tests for both .flatMap and .then be isPromise. I would also have the input side test for .then use isThenable. .flatMap has no input-side test, which is its point. A somewhat separable question is what these two tests are testing. The first hard reason why we must at least have an isPromise test on the output side of .then is that the promise already returned by .then must adopt this output promise without calling its .then method. Otherwise we lose support for lazy evaluation and for promise pipelining. Right. You can go as far as testing for the existence of a then property and verifying that its value is a Function, but you can't call it without breaking these qualities. (For example, you can't call it and verify that it doesn't immediately throw, perhaps because it's a false-positive and doesn't like being passed null/functions as arguments.) If we have a reliable brand unrelated to registering callbacks, that's even better. If the output side of .then is a non-promise thenable, it is a separate question whether it should be adopted by calling its .then method or whether it should be accepted. IMO it should be accepted. The isThenable test indicates something ugly is going on -- assimilation. With this AP2 based design, we can isolate this ugliness to the input side of .then. Hm, that works for me. It *is* undetectable whether you do adoption/assimilation on the output side or do wait-for-a-non-promise on the input side, except via measuring timeing/ordering of when .then() is called (which you shouldn't be doing, hopefully). This strategy also makes us somewhat more consistent in behavior when you do a .then().flatMap() chain, between returning from .then() a real promise and a thenable - if you do detection on the output side, the latter case will fully assimilate, while the former will only adopt (one level unwrapping). If you defer detection, then the latter case just accepts, which is closer to the former case's adoption. The second reason why the two tests need to be separate is that the output side of .flatMap cannot adopt simply by calling the output's .then method, because otherwise you'd often get exactly the recursive unwrapping that .flatMap is trying to avoid. In order to avoid this, it must test whether there is anything it can do to adopt other than calling .then. Yup, .flatMap() needs to do detection for a real/explicitly-branded promise. (It can't detect for flatMap-able, because that's meant to be the generic monad operation. Lots of different types of objects can be monads in different ways, so the methods are *not* compatible between types.) ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
On Thu, Aug 1, 2013 at 10:00 AM, Tab Atkins Jr. jackalm...@gmail.comwrote: On Thu, Aug 1, 2013 at 9:09 AM, Anne van Kesteren ann...@annevk.nl wrote: On Thu, Aug 1, 2013 at 5:07 PM, Anne van Kesteren ann...@annevk.nl wrote: I basically took Tab's email and rewrote the terminology. I omitted the issues for brevity. Hopefully this helps. Sorry, I was waiting until Mark and Domenic had finished up their terminology discussion before I did a third rewrite. Having done that. I wonder if we could leave the monad part out for now. As Mark pointed out in the other thread it causes a bunch of headaches to get that correct, and since we already decided (I believe) to not break with existing practice we could ship the subset that is that and figure out the superset-promise-that-works-for-monads later. That might also give us some insight into how many people will want to wrap promises to make the monad-suitable. Let's not reopen this, please. There are three questions here, which need to be settled in roughly this chronological order: 1) What should tc39 do quickly, to unblock the DOM's need for promises and avoid a design fork? 2) What should DOM do quickly the tc39's quick output? 3) Once #1 and #2 are settled, what should tc39 do for es7? For #1 I agree with Tab. We should not reopen this issue, but should rather proceed to settle the AP2ish design quickly. Otherwise we won't settle anything quickly and DOM will go their own way. For #2, since whatever DOM does quickly becomes a compat constraint on all future decisions, DOM should take the minimum subset of #1 which actually meets their short term needs. I leave it to those who understand those needs to argue about what these are. For #3, we will hopefully stay upwards compat with both #1 and #2. But compat with #2 will be more pressing, as that will have been massively deployed as part of the browser. The way I've outlined things means that then()-based stuff works compatibly with the existing spec, so that's not a concern. We've already had long threads about why nested promises are useful (namely, that promise in practice isn't a single type - you have multiple types of promises from different promise sources, and don't always want to smash them together). These have been long threads so I won't rehash them either. However, I will point out that these long threads also explain why nested promises won't be useful. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
On Thu, Aug 1, 2013 at 11:26 AM, Tab Atkins Jr. jackalm...@gmail.comwrote: On Thu, Aug 1, 2013 at 11:04 AM, Mark S. Miller erig...@google.com wrote: Between Tab's answer and mine, we see the two issues one might mean by branding. No, there's only the one concept. If you mean anything other than type detection via properties on the instance or its prototype chain, then I don't think you're using the right word. This is not a correct summary of the way we've been using branding. But it is one kind of branding if you wish. (If I'm wrong, please correct me.) Done ;) Detecting thenables via the presence of a function-valued then property on the object is, explicitly, branding. Anne's clarifying summary at https://mail.mozilla.org/pipermail/es-discuss/2013-August/032465.htmlspeaks only in terms of promise-likes. One of the things we need to settle is whether there is one predicate that applies to all the places in this summary that says if ... promise-like. IMO we need to distinct tests -- * isPromise for whether p is a promise and * isThenable for whether p is thenable. I would have the output-side tests for both .flatMap and .then be isPromise. I would also have the input side test for .then use isThenable. .flatMap has no input-side test, which is its point. A somewhat separable question is what these two tests are testing. The first hard reason why we must at least have an isPromise test on the output side of .then is that the promise already returned by .then must adopt this output promise without calling its .then method. Otherwise we lose support for lazy evaluation and for promise pipelining. Right. You can go as far as testing for the existence of a then property and verifying that its value is a Function, but you can't call it without breaking these qualities. (For example, you can't call it and verify that it doesn't immediately throw, perhaps because it's a false-positive and doesn't like being passed null/functions as arguments.) If we have a reliable brand unrelated to registering callbacks, that's even better. +1 If the output side of .then is a non-promise thenable, it is a separate question whether it should be adopted by calling its .then method or whether it should be accepted. IMO it should be accepted. The isThenable test indicates something ugly is going on -- assimilation. With this AP2 based design, we can isolate this ugliness to the input side of .then. Hm, that works for me. It *is* undetectable whether you do adoption/assimilation on the output side or do wait-for-a-non-promise on the input side, except via measuring timeing/ordering of when .then() is called (which you shouldn't be doing, hopefully). This strategy also makes us somewhat more consistent in behavior when you do a .then().flatMap() chain, between returning from .then() a real promise and a thenable - if you do detection on the output side, the latter case will fully assimilate, while the former will only adopt (one level unwrapping). If you defer detection, then the latter case just accepts, which is closer to the former case's adoption. Excellent! The second reason why the two tests need to be separate is that the output side of .flatMap cannot adopt simply by calling the output's .then method, because otherwise you'd often get exactly the recursive unwrapping that .flatMap is trying to avoid. In order to avoid this, it must test whether there is anything it can do to adopt other than calling .then. Yup, .flatMap() needs to do detection for a real/explicitly-branded promise. (It can't detect for flatMap-able, because that's meant to be the generic monad operation. Lots of different types of objects can be monads in different ways, so the methods are *not* compatible between types.) Great! I think we continue to converge very nicely. ~TJ -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus with /A+ terminology
On Thu, Aug 1, 2013 at 11:27 AM, Mark S. Miller erig...@google.com wrote: There are three questions here, which need to be settled in roughly this chronological order: 1) What should tc39 do quickly, to unblock the DOM's need for promises and avoid a design fork? 2) What should DOM do quickly the tc39's quick output? 3) Once #1 and #2 are settled, what should tc39 do for es7? For #1 I agree with Tab. We should not reopen this issue, but should rather proceed to settle the AP2ish design quickly. Otherwise we won't settle anything quickly and DOM will go their own way. And, importantly, except for the open question of thenable/branding, our proposal is behaviorally and API-compatible with current DOM promises. For #2, since whatever DOM does quickly becomes a compat constraint on all future decisions, DOM should take the minimum subset of #1 which actually meets their short term needs. I leave it to those who understand those needs to argue about what these are. Unfortunately, the thenable question still impinges on the minimum subset. :/ ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises Consensus
Just some terminology questions for this new proposal... From: Tab Atkins Jr. [jackalm...@gmail.com] whatever p resolves to, gets passed to the flatMap() callbacks. What does resolves mean in this context? I don't believe you are using it in the same way that it is used in Promises/A+ or DOM Promises. For example, using the existing definition, you could resolve `p` to a forever-pending promise; I doubt you'd want to pass that forever pending promise to either (both?) of the flatMap callbacks. if p accepts to a promise-like, the callbacks get moved down to that until it either accepts with a non-promise-like, or rejects. What does accepts mean? Promise.every() will eventually resolve to an array of non-promise-likes. Again, what are you meaning by resolve here? Promises don't resolve to anything, so this is confusing. We can't just use magic internal operations to detect when the returned value resolves, so the output promise will have to register callbacks on it. Same question. This may have performance implications - is it possible that we just do eager resolution now, but later have detection for lazy promises getting returned and switch to lazy behavior in just those cases? Here I believe you are using resolution in the same sense as Promises/A+ or DOM Promises, but differently from all the above uses. Should we reject with a TypeError, or fall back to using .then() resolution semantics? Again this seems correct, but in contradiction to earlier uses. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
[Gah, resending because I'm being *way* too loose with my terminology. Ignore previous email - this one has identical content, but uses terms correctly.] (Scratch that, I added a new point #3 at the end of the email.) [For the purposes of this email, a promise accepting or rejecting means that its resolver's accept() or reject() method was called, or the equivalent internal magic. fulfill means accept or reject. resolve means adopt or accept, depending on whether the value is a promise-like or not (in other words, what the resolver's resolve() method does). adopt means accepting or rejecting with the same value as the adopted promise. If I should be using better terms, let me know.] Heya! I, Mark, and others have been hashing out our remaining differences on Promises privately, and are all happy with each other now, with only two remaining issues to be decided in a larger audience. Anne says that we should be able to get DOM Promises on track with this consensus if we finish up the discussion in the next month or so, since the differences from the current spec are mostly internal/new API. Here's our current consensus: Promises have both a .then() and a .flatMap() method. 1. p.flatMap() does single-level resolution: * whatever p fulfills to, gets passed to the flatMap() callbacks. * The callback return value *must* be a promise-like, which is adopted by the output promise; otherwise, the output promise rejects with a TypeError. 2. p.then() does recursive resolution on the input side (per consensus following 2 TC39-meetings ago): * if p accepts to a promise-like, the .then() callbacks get moved down to that promise-like until it either accepts with a non-promise-like, or rejects. * Rejection calls the rejection callback without delay; no extra resolution mechanics happen here. * The callback return value can be a promise-like or not. If it is, the output promise adopts it; if not, the output promise accepts it. 3. The helper functions (Promise.every(), etc.) use .then() semantics. That is, Promise.every() will eventually accept to an array of non-promise-likes. The first issue still up for community discussion involves the definition of promise-like. We'd like the definition to be: (a) a Promise or subtype, or (b) a branded non-Promise (with the branding done via Symbol or similar). Promises/A+ wants the branding to be done via a method named then (the thenable concept). This, unfortunately, goes directly against TC39 practices in a number of other areas, such as iterators, where we don't want short string names as branding due to the possibility of collision. (In the case of then, collision isn't a possibility, it's a certainty - we *know* there are libraries out there today that put a then method on their objects without referring to Promises.) Thoughts? The second issue still up for community discussion is what adopts means, precisely. 1. Assume a .then() callback returns a non-native promise-like. We can't just use magic internal operations to detect when the returned promise fulfills, so the output promise will have to register callbacks on it. This appears to break our desire to have lazy promises in the future that don't compute a value until someone asks for it. Should we specify that adoption is done late? (That is, the output promise would hold onto the returned promise without touching it, until someone actually registers some callbacks on it.) This may have performance implications - is it possible that we just do eager resolution now, but later have detection for lazy promises getting returned and switch to lazy behavior in just those cases? 2. Assume a .flatMap() callback returns a non-native promise-like. Obviously, the output promise adopts it by registering .flatMap() callbacks on it. But what if the promise-like only has a .then() method? Should we reject with a TypeError, or fall back to using .then() resolution semantics? (I suspect we need to do the former to maintain monad laws.) 3. For that matter, what about adopting the returned promise value of a .then() callback? If you try and use .then() to listen for the returned promise to fulfill, you'll end up imposing full recursive semantics on the output promise, regardless of whether they're observed with .flatMap() or .then(). Looks like we should default to trying to adopt with .flatMap(), and then maybe fall back to .then() for .then()-returned promise-likes. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises Consensus
From: Tab Atkins Jr. [jackalm...@gmail.com] For the purposes of this email, a promise accepting or rejecting means that its resolver's accept() or reject() method was called, or the equivalent internal magic. fulfill means accept or reject. resolve means adopt or accept, depending on whether the value is a promise-like or not (in other words, what the resolver's resolve() method does). adopt means accepting or rejecting with the same value as the adopted promise. If I should be using better terms, let me know. Thanks for the clarifications :). I think this is a bit confusing because it is at odds with commonly-used terminology, from DOM Promises and Promises/A+, but at least now things are defined and used in a self-consistent way. Much appreciated. For the record, since you asked for better terms, the community consensus is: - Fulfill and reject are the two end states (as opposed to pending). - Settle means fulfill or reject. - Resolve means adopt or fulfill. Accept was just a strange neologism introduced by the linguistic fork that was DOM Futures, now thankfully dead. But for the purposes of this thread it may be best to stop worrying about these issues now that you've set out a set of self-consistent terminology, and simply go with the terms as you defined them. We can always re-kill the zombie accept at a later date, replacing it with the normal fulfill, once we understand its semantics. I'll step back and let everyone else comment now, as I believe my views on the proposed semantics are well-known. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
On Wed, Jul 31, 2013 at 11:38 AM, Domenic Denicola dome...@domenicdenicola.com wrote: From: Tab Atkins Jr. [jackalm...@gmail.com] For the purposes of this email, a promise accepting or rejecting means that its resolver's accept() or reject() method was called, or the equivalent internal magic. fulfill means accept or reject. resolve means adopt or accept, depending on whether the value is a promise-like or not (in other words, what the resolver's resolve() method does). adopt means accepting or rejecting with the same value as the adopted promise. If I should be using better terms, let me know. Thanks for the clarifications :). I think this is a bit confusing because it is at odds with commonly-used terminology, from DOM Promises and Promises/A+, but at least now things are defined and used in a self-consistent way. Much appreciated. For the record, since you asked for better terms, the community consensus is: - Fulfill and reject are the two end states (as opposed to pending). - Settle means fulfill or reject. - Resolve means adopt or fulfill. Ah, I'd never heard the term settle before, in any of the threads across the WGs here. Got it. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
Does this all mean that you're ok with having promises-for-promises? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
On Wed, Jul 31, 2013 at 12:48 PM, Juan Ignacio Dopazo dopazo.j...@gmail.com wrote: Does this all mean that you're ok with having promises-for-promises? I've always been okay with that. ^_^ This consensus details how to handle nested promises (use .flatMap()) *and* how to ignore that and just get values out of them (use .then()). Everyone's happy! ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Promises Consensus
From: Mark S. Miller [erig...@google.com] One thing I think Domenic is missing that I also missed at first: Once we introduce .flatMap, then we need a distinct accepted state that is neither fulfilled nor rejected. The issue is that p.then does not fire until the promise p is fulfilled or rejected. If q is pending, and p is accepted to q, then p.flatMap will fire but p.then will not yet fire. When q becomes fulfilled or rejected, then p becomes fulfilled or rejected and p.then fires. Thus, p is following q. So when p and q are both promises, p follows q when p is accepted to q or when p adopts q. This hair splitting goes beyond any previous conversations I've had with anyone, but becomes necessary to account for the behavior or both .flatMap and .then under AP2. Isn't this just what we've been calling resolved? As in p is resolved q, but still pending because q is pending? I suppose that is ambiguous because you could resolve p to a non-promise-like and the behavior is a bit different. Perhaps you're proposing that resolve p with q will make p resolved with q, and we will additionally say either that p is accepted with q, if q is a promise-like, or fulfilled with q, if q is non-promise-like. Does that sound accurate? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
Le 31 juil. 2013 à 20:23, Tab Atkins Jr. jackalm...@gmail.com a écrit : The first issue still up for community discussion involves the definition of promise-like. We'd like the definition to be: (a) a Promise or subtype, or (b) a branded non-Promise (with the branding done via Symbol or similar). Promises/A+ wants the branding to be done via a method named then (the thenable concept). This, unfortunately, goes directly against TC39 practices in a number of other areas, such as iterators, where we don't want short string names as branding due to the possibility of collision. (In the case of then, collision isn't a possibility, it's a certainty - we *know* there are libraries out there today that put a then method on their objects without referring to Promises.) Thoughts? I suggest an @@isPromise builtin symbol, which works the same way as @@isRegExp in the ES6 spec [1]: An object is reputed to be a promise if and only if it has a property (either own or inherited) named @@isPromise. And `Promise.prototype` has initially an @@isPromise own property, so that instances of subclasses of `Promise` are recognised as promises. (With this solution, you have not to choose between subclassing or branding, but you have the both. :-) ) —Claude [1] search the occurrences of @@isRegExp in: http://people.mozilla.org/~jorendorff/es6-draft.html ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Promises Consensus
On Wed, Jul 31, 2013 at 3:52 PM, Domenic Denicola dome...@domenicdenicola.com wrote: From: Mark S. Miller [erig...@google.com] One thing I think Domenic is missing that I also missed at first: Once we introduce .flatMap, then we need a distinct accepted state that is neither fulfilled nor rejected. The issue is that p.then does not fire until the promise p is fulfilled or rejected. If q is pending, and p is accepted to q, then p.flatMap will fire but p.then will not yet fire. When q becomes fulfilled or rejected, then p becomes fulfilled or rejected and p.then fires. Thus, p is following q. So when p and q are both promises, p follows q when p is accepted to q or when p adopts q. This hair splitting goes beyond any previous conversations I've had with anyone, but becomes necessary to account for the behavior or both .flatMap and .then under AP2. Isn't this just what we've been calling resolved? As in p is resolved q, but still pending because q is pending? I'm sorry Domenic, but since I'm hair splitting and stated several distinctions, I need to know which this you refer to. I suppose that is ambiguous because you could resolve p to a non-promise-like and the behavior is a bit different. Perhaps you're proposing that resolve p with q will make p resolved with q, and we will additionally say either that p is accepted with q, if q is a promise-like, or fulfilled with q, if q is non-promise-like. Does that sound accurate? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Text by me above is hereby placed in the public domain Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss