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