The use cases for "i want a resolved or rejected promise for a value/reason" are much more numerous than for "i want a deferred", so yes, I don't think a Deferred belongs in the language, and I think Promise.resolve/reject absolutely clear that bar.
On Sat, Jul 21, 2018 at 11:19 AM, Isiah Meadows <isiahmead...@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. > > 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