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&amp;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

Reply via email to