> 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