If this helps, `finally` is like `finally` in `try`/`finally`. On Fri, Sep 7, 2018 at 16:51 Jon Ronnenberg <[email protected]> wrote:
> You are probably right that it is too late. > > > inconsistent with expected behavior because it would have to run > multiple times. > > Is it inconsistent with expected behavior, if someone adds to the > Promise chain in another tick? Seems like a pretty conscious choice, > and the `finally` code would have to clean up that as well. > > I guess my main issue is that I don't really see a use-case for a > `finally` block that runs before a `then` block. It's just a > convenience for not writing the same function twice as a variable. > Yah, ok. I'll live with it and move on. A shame though. > > PS. but I just want to ask if you ran my example? ;) > > > On Fri, Sep 7, 2018 at 10:37 PM Peter Jaszkowiak <[email protected]> > wrote: > > > > Having finally run after all of them in just the tick is very > inconsistent. > > > > Having it run after the end of the chain is either impossible because it > would have to wait indefinitely, or inconsistent with expected behavior > because it would have to run multiple times. > > > > I think you should accept the specified behavior, it's very very > unlikely to change at this point, even if you did have a good argument. > > > > On Fri, Sep 7, 2018, 14:27 Jon Ronnenberg <[email protected]> > wrote: > >> > >> Hmm, so I misunderstand finally then. Running your code with `then` > >> gives NaN. x is undefined - I just ran it in node 8.11 > >> > >> let b = Promise.resolve(4).then(() => console.log('done')); > >> setTimeout(() => b.then(x => console.log(x + 3)), 1000); > >> > >> But running it in Firefox 63.0b4 gives `done` -> `7` > >> > >> let b = Promise.resolve(4).finally(() => console.log('done')); > >> setTimeout(() => b.then(x => console.log(x + 3)), 1000); > >> > >> I would still prefer that finally was run either after the last > >> then/catch in the same "tick" or after the last then/catch for the > >> duration of the promise lifetime. > >> > >> So that: > >> let b = Promise.resolve(4).finally(() => console.log('done')).then((x) > => { > >> console.log('another function added this later') > >> return x > >> }); > >> setTimeout(() => b.then(x => console.log(x + 3)), 1000); > >> > >> Would give "another function added this later" -> "done" -> 7 -> "done" > >> And not as it is currently: "done" -> "another function added this > later" -> 7 > >> > >> I made an lengthy example here just to see if it was possible (it > >> runs in node 8.11): > >> > >> function Promise(executor) { > >> if(executor == undefined) throw new TypeError('Not enough arguments > >> to Promise.'); > >> > >> let thenables = []; > >> let rejects = []; > >> let catches = []; > >> let finallies = []; > >> let states = { > >> pending: 1, > >> fulfilled: 2, > >> rejected: 4 > >> } > >> let state = states.pending; > >> let value; > >> let timer; > >> > >> function runFinallies () { > >> finallies.forEach(f => { > >> try { > >> f() > >> } catch (e) { /* we just skip throwing finallies since our catch > >> block has already executed */ } > >> }) > >> } > >> > >> this.then = (f, r) => { > >> if(timer) > >> timer = clearTimeout(timer); > >> thenables.push(f); > >> rejects.push(r); > >> > >> if(state === states.fulfilled) > >> timer = setTimeout(this.resolve.bind(this, value), 0); > >> else > >> if(state === states.rejected) > >> timer = setTimeout(this.reject.bind(this, value), 0); > >> > >> return this; > >> }; > >> this.resolve = v => { > >> state = states.fulfilled; > >> value = v; > >> > >> let chainableValue = value; > >> thenables.forEach(f => { > >> try { > >> chainableValue = f(chainableValue || value) > >> } catch(e) { > >> if(catches.length === 0) > >> this.reject(e); > >> else { > >> catches.forEach(f => f(e)); > >> catches = []; > >> } > >> } finally { > >> runFinallies() > >> } > >> }); > >> thenables = []; > >> }; > >> this.reject = v => { > >> state = states.rejected; > >> value = v; > >> > >> let chainableValue = value; > >> let error; > >> try { > >> rejects.forEach(f => chainableValue = f(chainableValue || value)); > >> } catch (e) { > >> error = e; > >> } finally { > >> runFinallies() > >> } > >> rejects = []; > >> if(error) throw error; > >> // console.log(this, state, value, rejects); > >> }; > >> this.catch = f => { > >> catches.push(f); > >> return this; > >> }; > >> this.finally = f => { > >> finallies.push(f); > >> return this; > >> }; > >> > >> try { > >> executor(this.resolve, this.reject); > >> } catch(e) { > >> if(state !== states.fulfilled) > >> this.reject(e); > >> } > >> } > >> Promise.resolve = function(v) { > >> let p; > >> if(v instanceof Promise) > >> p = v; > >> else if(v.then) > >> p = new Promise(v.then); > >> else > >> p = new Promise(resolve => resolve(v)); > >> return p; > >> } > >> Promise.reject = function(v) { > >> return new Promise((resolve, reject) => reject(v)); > >> } > >> Promise.race = function(iterable) { > >> return new Promise((resolve, reject) => { > >> let muteResolve = false, > >> muteReject = false; > >> > >> iterable.forEach(promise => { > >> promise.then( > >> value => { > >> muteReject = true; > >> if(!muteResolve) > >> resolve(value); > >> }, > >> value => { > >> muteResolve = true; > >> if(!muteReject) > >> reject(value) > >> } > >> ) > >> }) > >> }); > >> }; > >> Promise.all = function(iterable) { > >> // let allResolved = false; > >> let resolvedLength = iterable.length; > >> let promiseValues = []; > >> return new Promise((resolve, reject) => { > >> iterable.map( > >> promise => promise instanceof Promise ? > >> promise : Promise.resolve(promise) > >> ).forEach(promise => promise.then( > >> value => { > >> if(resolvedLength === 1) > >> resolve(promiseValues.concat(value)); > >> else { > >> resolvedLength--; > >> promiseValues.push(value); > >> } > >> }, > >> value => reject(value) > >> )); > >> }); > >> } > >> > >> let b = Promise.resolve(4).finally(() => console.log('done')).then((x) > => { > >> console.log('another function added this later') > >> return x > >> }); > >> setTimeout(() => b.then(x => console.log(x + 3)), 1000); > >> > >> > >> On Fri, Sep 7, 2018 at 9:49 PM Peter Jaszkowiak <[email protected]> > wrote: > >> > > >> > I think you're the one who's confused. Chains exist within a tick, > and persist beyond a single tick. > >> > > >> > ``` > >> > let b = Promise.resolve(4).finally(() => console log('done')); > >> > > >> > setTimeout(1000, () => b.then(x => console.log(x + 3))); > >> > ``` > >> > > >> > There is no way to know that the latter then exists until it is > executed. There's no way for the finally to execute after it without > waiting a second. > >> > > >> > The only thing you could possibly specify is that the finally must > execute after all thens added within the same tick. That will never happen, > though, because it would just be inconsistent behavior. > >> > > >> > On Fri, Sep 7, 2018, 13:40 Jon Ronnenberg <[email protected]> > wrote: > >> >> > >> >> Sorry but that is not how Promises work. You compose the entire > >> >> promise chain in one "tick" and then you start execution of the > entire > >> >> chain. > >> >> > >> >> I will try to write you an over simplification of the concept... > Hang on :) > >> >> On Fri, Sep 7, 2018 at 9:35 PM Peter Jaszkowiak <[email protected]> > wrote: > >> >> > > >> >> > No, it doesn't make any sense. > >> >> > > >> >> > Do you not see the impossibility of this? If you have a promise > `a`: > >> >> > > >> >> > You call `b = a.then(one).finally(done)` > >> >> > > >> >> > `b` is a promise. You can't have the thread waiting forever for > every possible descendent to be added before executing the `done` function. > There may even be none. > >> >> > > >> >> > On Fri, Sep 7, 2018, 13:27 Jon Ronnenberg < > [email protected]> wrote: > >> >> >> > >> >> >> I'm suggesting that finally is the last function to be executed. > So > >> >> >> the chain is, then().then().then().catch().finally(), regardless > of at > >> >> >> what time then or catch is added to the Promise chain. > >> >> >> Like try->catch->finally with a callback but without a callback. > Does > >> >> >> it make sense? > >> >> >> On Fri, Sep 7, 2018 at 9:24 PM Peter Jaszkowiak < > [email protected]> wrote: > >> >> >> > > >> >> >> > There are plenty of ways of doing what you need without > changing the behavior of finally. Are you suggesting that calling finally > make it so any other then or catch call just not execute? And are you also > suggesting that this should climb up the chain of promises? > >> >> >> > > >> >> >> > On Fri, Sep 7, 2018, 13:16 Jon Ronnenberg < > [email protected]> wrote: > >> >> >> >> > >> >> >> >> I know I am late to the game and that > Promise.prototype.finally is already in stage 4 but(!). > >> >> >> >> > >> >> >> >> It's just not very useful to have a final function when it's > not the final function to run. If it's suppose to be for cleanup, then the > current implementation is seriously lacking usefulness. > >> >> >> >> > >> >> >> >> Consider the following example: > >> >> >> >> > >> >> >> >> <audio > >> >> >> >> class="i-am-the-element" > >> >> >> >> autoplay="autoplay" > >> >> >> >> controls="controls"> > >> >> >> >> <source type="audio/mp3" src="http:\/\/play.dogmazic.net > \/play\/index.php?type=song&oid=22951&uid=-1&name=Black%20poetry%20-%20Aime-.mp3"> > >> >> >> >> </audio> > >> >> >> >> <script> > >> >> >> >> class Demo { > >> >> >> >> constructor (element) { > >> >> >> >> this.node = element > >> >> >> >> } > >> >> >> >> destroy () { > >> >> >> >> return new Promise(resolve => { > >> >> >> >> // do something or nothing > >> >> >> >> resolve() > >> >> >> >> }).finally(() => { > >> >> >> >> // schedule for DOM removal > >> >> >> >> this.node = null > >> >> >> >> }) > >> >> >> >> } > >> >> >> >> } > >> >> >> >> > >> >> >> >> const demo = new > Demo(document.querySelector('.i-am-the-element')) > >> >> >> >> > >> >> >> >> setTimeout(() => { > >> >> >> >> demo.destroy().then(() => { > >> >> >> >> // will throw an error because finally was run before > >> >> >> >> demo.node.pause() > >> >> >> >> }).catch(console.error) > >> >> >> >> }, 3000) > >> >> >> >> </script> > >> >> >> >> > >> >> >> >> One grand idea about promises is to delegate and compose > asynchronous functions, but the current implementation can not be used to > for code delegation. > >> >> >> >> > >> >> >> >> From the top of my head the only way to have consumer code, > tap into an execution process is to use callbacks which is what Promises > were suppose to help alleviate. > >> >> >> >> > >> >> >> >> <audio > >> >> >> >> class="i-am-the-element" > >> >> >> >> autoplay="autoplay" > >> >> >> >> controls="controls"> > >> >> >> >> <source type="audio/mp3" src="http:\/\/play.dogmazic.net > \/play\/index.php?type=song&oid=22951&uid=-1&name=Black%20poetry%20-%20Aime-.mp3"> > >> >> >> >> </audio> > >> >> >> >> <script> > >> >> >> >> class Demo { > >> >> >> >> constructor (element) { > >> >> >> >> this.node = element > >> >> >> >> } > >> >> >> >> destroy (callback) { > >> >> >> >> // do something or nothing > >> >> >> >> try { > >> >> >> >> callback() > >> >> >> >> } finally { > >> >> >> >> // schedule for DOM removal > >> >> >> >> this.node = null > >> >> >> >> } > >> >> >> >> } > >> >> >> >> } > >> >> >> >> > >> >> >> >> const demo = new > Demo(document.querySelector('.i-am-the-element')) > >> >> >> >> > >> >> >> >> setTimeout(() => { > >> >> >> >> demo.destroy(() => { > >> >> >> >> demo.node.pause() > >> >> >> >> }) > >> >> >> >> }, 3000) > >> >> >> >> </script> > >> >> >> >> > >> >> >> >> If at all possible, please amend to the spec before it's too > late! ... or just drop it. > >> >> >> >> > >> >> >> >> My current use-case is that I work with PSPDFKit and can not > get DOM access but rather schedule removal of DOM nodes via their API, but > I can pause audio/video - just not using Promise.prototype.finally as it is > currently envisioned. > >> >> >> >> > >> >> >> >> Regards, Jon > >> >> >> >> > >> >> >> >> PS. Tested in Firefox 63.0b3 and Safari 11.1.2 > >> >> >> >> Here is a polyfill if you need: > https://cdn.polyfill.io/v2/polyfill.minify.js?features=Promise.prototype.finally&flags=gated > >> >> >> >> > >> >> >> >> _______________________________________________ > >> >> >> >> es-discuss mailing list > >> >> >> >> [email protected] > >> >> >> >> https://mail.mozilla.org/listinfo/es-discuss > _______________________________________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/listinfo/es-discuss >
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

