> 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
m...@isiahmeadows.com
www.isiahmeadows.com


On Fri, Jul 20, 2018 at 12:15 PM, Augusto Moura
<augusto.borg...@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 <isiahmead...@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
>> m...@isiahmeadows.com
>> www.isiahmeadows.com
>>
>>
>> On Fri, Jul 20, 2018 at 12:04 AM, Bob Myers <r...@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 <ljh...@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 <isiahmead...@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
>> >>> m...@isiahmeadows.com
>> >>> www.isiahmeadows.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
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss@mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>
> --
> Augusto Moura
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to