Augusto Moura (2018-07-21T22:04:03.000Z)
Reject and resolve static methods are not introducing a new ~maybe
dangerous~ pattern into the language, they are just isolating a factory for
a common use case (creating a promise wrapping a well know value at the
time of execution), deferreds add a whole lot of indirection in the table,
that might lay some traps for non-experienced developers and promote some
bad designs or confusing code.

Em sáb, 21 de jul de 2018 às 15:20, Isiah Meadows <isiahmeadows at gmail.com>
escreveu:

> > I think what Jordan means, it's that the deferred has it use case, but
> > probably we don't want it in Javascript native library. There's a lot of
> > mature libraries implementing deferred wrappers and most of them are
> Promise
> > like compatible, and even if you cannot use libraries or don't want to,
> you
> > can easily implement a Promise extension and use it yourself.
>
> Jordan, is this accurate? It wasn't what I read of it, unless you sent
> something that mistakenly missed the list.
>
> If that *is* the case, I don't see much precedent:
> `Promise.resolve`/`Promise.reject` also hit that bar just as quickly,
> if not *quicker*:
>
> ```js
> // Exact polyfills, assuming `IsPromise` as per 25.6.1.6 is exposed
> globally.
> // https://tc39.github.io/ecma262/#sec-ispromise
> Promise.resolve = Promise.resolve || function (value) {
>     if (IsPromise(value) && value.constructor === Promise) return value
>     let resolve
>     let promise = new this((r, _) => { resolve = r })
>     resolve(value)
>     return promise
> }
>
> Promise.reject = Promise.reject || function (value) {
>     let reject
>     let promise = new this((_, r) => { reject = r })
>     reject(value)
>     return promise
> }
> ```
>
> -----
>
> Isiah Meadows
> me at isiahmeadows.com
> www.isiahmeadows.com
>
>
> On Fri, Jul 20, 2018 at 12:15 PM, Augusto Moura
> <augusto.borgesm at gmail.com> wrote:
> > I think what Jordan means, it's that the deferred has it use case, but
> > probably we don't want it in Javascript native library. There's a lot of
> > mature libraries implementing deferred wrappers and most of them are
> Promise
> > like compatible, and even if you cannot use libraries or don't want to,
> you
> > can easily implement a Promise extension and use it yourself.
> >
> > Interesting enough, I got a really weird case (reads contraintuitive, I'm
> > pretty sure the semantics of the error are right) extending the Promise
> > class to exemplify a simple Deferred implementation, the code:
> >
> > ``` js
> > class Deferred extends Promise {
> >   constructor(factory) {
> >     super((resolve, reject) => {
> >       Object.assign(this, { reject, resolve });
> >       factory(resolve, reject);
> >     });
> >   }
> > }
> >
> > const d = new Deferred(() => {});
> > ```
> > The problem is the usage of `this` before calling the super constructor
> > (even when the using in the super call itself). I wonder with it there
> are
> > any ways of capturing the super constructor arguments in a Base class
> using
> > class syntax. You probably can get the arguments in a old "function
> class"
> > syntax (can be done weakmaps too). We can probably start ~yet~ another
> > thread on Promises, about this problem (supposing there's no way of
> passing
> > `this` to the promise factory).
> >
> > Em sex, 20 de jul de 2018 às 03:03, Isiah Meadows <
> isiahmeadows at gmail.com>
> > escreveu:
> >>
> >> First, I do get that not all uses of deferred-like objects really
> >> merit the need for a deferred. For example, [here][1], I saved the
> >> resolver and pulled the state out from the main closure to make the
> >> state easier to follow. You could argue a deferred isn't really
> >> necessary since I only care about the `resolve` function, and nothing
> >> else. It's also pretty trivial to factor it back into a closure where
> >> it was originally.
> >>
> >> [1]:
> >>
> https://github.com/isiahmeadows/thallium/blob/master/lib/core/tests.js#L337-L428
> >>
> >> But it's when external forces control them indirectly through a state
> >> machine or similar, that's when it becomes necessary. I have some
> >> closed-source uses, but here's a couple concrete examples I have in
> >> OSS code:
> >>
> >> 1. Here, I have to treat it like a continuation because I have to wait
> >> for an IPC protocol sequence to complete before it resolves/rejects:
> >>
> >>
> https://github.com/isiahmeadows/invoke-parallel/blob/master/lib/api.js#L144-L147
> >> 2. Here, I have to treat it like a continuation because it's placed
> >> into a job queue driven by mainly the completion of child processes:
> >>
> >>
> https://github.com/isiahmeadows/website/blob/570db369cfca2b8a4a525be4e4621c854788b4d0/scripts/exec-limit.js#L71-L73
> >>
> >> There is literally no other way to handle these beyond using a fake
> >> deferred, thanks to the fact they aren't resolved directly in response
> >> to any external forces, but indirectly as the result of a state
> >> machine transition or similar. I can't even pass them around where I
> >> need them, because there's a giant process wall I have to cross each
> >> time. And it's this kind of use case that drove me to request this.
> >> The resolver functions get in my way, they take up more memory than
> >> necessary, and I've found myself occasionally adding separate arrays
> >> of resolver/rejector functions so I can also avoid the indirection of
> >> calling them.
> >>
> >> In general, I don't like using deferreds if I can help it - it's
> >> nothing but boilerplate for the common case. Here's what I usually
> >> prefer in order, provided I can help it:
> >>
> >> - The return value itself.
> >> - `async`/`await`
> >> - `Promise.prototype.finally` or some similar abstraction.
> >> - `Promise.prototype.then`/`Promise.prototype.catch`
> >> - `Promise.resolve`/`Promise.reject`
> >> - `Promise.try` or some similar abstraction.
> >> - `Promise.all([...])`/`Promise.race([...])
> >> - `new Promise(...)` using the callbacks directly.
> >> - `new Promise(...)`, converting the result to a pseudo-deferred.
> >>
> >> I'm not asking about this because I *enjoy* deferreds - they're
> >> nothing but useless boilerplate for the vast majority of use cases. In
> >> fact, I actively try to avoid it most of the time. I'm just asking for
> >> an escape hatch in case the *simple* stuff becomes boilerplate, one
> >> mirroring how the spec already deals with those complex scenarios.
> >> Very few things hit that breaking point when the callbacks become
> >> boilerplate, but low-level async code requiring a dedicated state
> >> machine driven by both calls and external effects has a habit of
> >> hitting that very quickly.
> >>
> >> -----
> >>
> >> Isiah Meadows
> >> me at isiahmeadows.com
> >> www.isiahmeadows.com
> >>
> >>
> >> On Fri, Jul 20, 2018 at 12:04 AM, Bob Myers <rtm at gol.com> wrote:
> >> > I've used this pattern exactly twice in the large-scale app I'm
> working
> >> > on
> >> > now.
> >> > One of those I was able to eliminate after I thought harder about the
> >> > problem.
> >> > The other I eventually replaced with the following kind of pattern:
> >> >
> >> > ```
> >> > function createPromise(resolver, rejector) {
> >> >   return new Promise((resolve, reject) {
> >> >     resolver.then(resolve);
> >> >     rejector.then(reject);
> >> >     });
> >> > }
> >> > ```
> >> >
> >> > Obviously the way this works it that to create a promise
> "controllable"
> >> > from
> >> > "the outside",
> >> > you create your own resolver and rejector promises to pass to
> >> > `createPromise`,
> >> > such that they trigger when you need them to.
> >> > To put it a different way, instead of getting back and passing around
> >> > deferred-like objects,
> >> > which seems to be a massive anti-pattern to me,
> >> > the client creates their own promise-controlling promises designed to
> >> > trigger at the right time.
> >> >
> >> > Bob
> >> >
> >> > On Fri, Jul 20, 2018 at 9:07 AM Jordan Harband <ljharb at gmail.com>
> wrote:
> >> >>
> >> >> I don't think the Deferred pattern is a good primitive to have in the
> >> >> language, and it's a pretty trivial primitive to write yourself if
> you
> >> >> need
> >> >> it.
> >> >>
> >> >> On Thu, Jul 19, 2018 at 6:13 PM, Isiah Meadows <
> isiahmeadows at gmail.com>
> >> >> wrote:
> >> >>>
> >> >>> Sometimes, it's *very* convenient to have those `resolve`/`reject`
> >> >>> functions as separate functions. However, when logic gets complex
> >> >>> enough and you need to send them elsewhere, save a continuation,
> etc.,
> >> >>> it'd be much more convenient to just have a capability object
> exposed
> >> >>> more directly rather than go through the overhead and boilerplate of
> >> >>> going through the constructor with all its callback stuff and
> >> >>> everything.
> >> >>>
> >> >>> It's surprisingly not as uncommon as you'd expect for me to do this:
> >> >>>
> >> >>> ```js
> >> >>> let resolve, reject
> >> >>> let promise = new Promise((res, rej) => {
> >> >>>     resolve = res
> >> >>>     reject = rej
> >> >>> })
> >> >>> ```
> >> >>>
> >> >>> But doing this repeatedly gets *old*, especially when you've had to
> >> >>> write it several dozen times already. And it comes up frequently
> when
> >> >>> you're writing lower-level async utilities that require saving
> promise
> >> >>> state and resolving it in a way that's decoupled from the promise
> >> >>> itself.
> >> >>>
> >> >>> -----
> >> >>>
> >> >>> So here's what I propose:
> >> >>>
> >> >>> - `Promise.newCapability()` - This basically returns the result of
> >> >>> [this][1], just wrapped in a suitable object whose prototype is
> >> >>> %PromiseCapabilityPrototype% (internal, no direct constructor). It's
> >> >>> subclass-safe, so you can do it with subclasses as appropriate, too.
> >> >>> - `capability.resolve(value)` - This invokes the implicit resolver
> >> >>> created for it, spec'd as [[Resolve]].
> >> >>> - `capability.reject(value)` - This invokes the implicit rejector
> >> >>> created for it, spec'd as [[Reject]].
> >> >>> - `capability.promise` - This returns the newly created promise.
> >> >>>
> >> >>> Yes, this is effectively a deferred API, but revealing constructors
> >> >>> are a bit too rigid and wasteful for some use cases.
> >> >>>
> >> >>> [1]: https://tc39.github.io/ecma262/#sec-newpromisecapability
> >> >>>
> >> >>> -----
> >> >>>
> >> >>> Isiah Meadows
> >> >>> me at isiahmeadows.com
> >> >>> www.isiahmeadows.com
> >> >>> _______________________________________________
> >> >>> es-discuss mailing list
> >> >>> es-discuss at mozilla.org
> >> >>> https://mail.mozilla.org/listinfo/es-discuss
> >> >>
> >> >>
> >> >> _______________________________________________
> >> >> es-discuss mailing list
> >> >> es-discuss at mozilla.org
> >> >> https://mail.mozilla.org/listinfo/es-discuss
> >> _______________________________________________
> >> es-discuss mailing list
> >> es-discuss at mozilla.org
> >> https://mail.mozilla.org/listinfo/es-discuss
> >
> > --
> > Augusto Moura
>
-- 
Augusto Moura
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180721/0198ba6e/attachment-0001.html>
augusto.borgesm at gmail.com (2018-07-23T16:57:40.100Z)
Reject and resolve static methods are not introducing a new ~maybe
dangerous~ pattern into the language, they are just isolating a factory for
a common use case (creating a promise wrapping a well know value at the
time of execution), deferreds add a whole lot of indirection in the table,
that might lay some traps for non-experienced developers and promote some
bad designs or confusing code.