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