Promise capability support
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.
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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180719/c59032dc/attachment.html>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
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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180720/4190cd0f/attachment-0001.html>my use-case is the exact opposite. in integration-level javascript, its impractical to have independent rejectors/error-handlers for dozens of integrated-components. its much easier to debug/refactor integration-code with a single, universal rejector/error-handler (all it does is log the error-stack and return a 404/500 to the server-request, or crash the app if client-side).
on server-side, you can't do much except 404/500 if the db-request times-out, JSON.parse fails, etc.
on client-side, crashing the app (and notifying user an error occured, and retry the request) is pretty much the only thing you can do when the server 404/500's.
my use-case is the exact opposite. in integration-level javascript,
its impractical to have independent rejectors/error-handlers for
dozens of integrated-components. its much easier to debug/refactor
integration-code with a single, universal rejector/error-handler (all
it does is log the error-stack and return a 404/500 to the
server-request, or crash the app if client-side).
on server-side, you can't do much except 404/500 if the db-request
times-out, JSON.parse fails, etc.
on client-side, crashing the app (and notifying user an error occured,
and retry the request) is pretty much the only thing you can do when
the server 404/500's.
On 7/20/18, 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
>>
>First, I do get that not all uses of deferred-like objects really
merit the need for a deferred. For example, here, 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.
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:
- 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: isiahmeadows/invoke-parallel/blob/master/lib/api.js#L144-L147
- 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: 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/awaitPromise.prototype.finallyor some similar abstraction.Promise.prototype.then/Promise.prototype.catchPromise.resolve/Promise.rejectPromise.tryor 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
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-discussFor code that high level, I don't even usually deal with promises in the first place except when fetching data, saving data, and awaiting renders and transforms (sometimes). Raw event handlers are good enough for pretty much everything else, and when they aren't, you're almost certainly dealing with a stream or similar specialized data type that isn't a promise.
Either way, deferreds are the worst abstraction to use here, and you won't even likely see it much in Java code at this level (and deferreds is really only what Java has).
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
For code that high level, I don't even usually deal with promises in
the first place except when fetching data, saving data, and awaiting
renders and transforms (sometimes). Raw event handlers are good enough
for pretty much everything else, and when they aren't, you're almost
certainly dealing with a stream or similar specialized data type that
isn't a promise.
Either way, deferreds are the worst abstraction to use here, and you
won't even likely see it much in Java code at this level (and
deferreds is really only what Java *has*).
-----
Isiah Meadows
me at isiahmeadows.com
www.isiahmeadows.com
On Fri, Jul 20, 2018 at 12:27 AM, kai zhu <kaizhu256 at gmail.com> wrote:
> my use-case is the exact opposite. in integration-level javascript,
> its impractical to have independent rejectors/error-handlers for
> dozens of integrated-components. its much easier to debug/refactor
> integration-code with a single, universal rejector/error-handler (all
> it does is log the error-stack and return a 404/500 to the
> server-request, or crash the app if client-side).
>
> on server-side, you can't do much except 404/500 if the db-request
> times-out, JSON.parse fails, etc.
>
> on client-side, crashing the app (and notifying user an error occured,
> and retry the request) is pretty much the only thing you can do when
> the server 404/500's.
>
> On 7/20/18, 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-discussSo I run into this issue when waiting on multiple events I don't initiate. What I do is create a subclass of promise that exposes these calls.
Not saying that's the ideal way to do it, but solvable in userland and without modifying the base class.
So I run into this issue when waiting on multiple events I don't initiate.
What I do is create a subclass of promise that exposes these calls.
Not saying that's the ideal way to do it, but solvable in userland and
without modifying the base class.
On Thursday, July 19, 2018, 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180720/caaaf1b9/attachment.html>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:
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).
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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180720/27cb0076/attachment-0001.html>On Fri, Jul 20, 2018 at 12:15 PM Augusto Moura <augusto.borgesm at gmail.com>
wrote:
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:
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
thisbefore calling the super constructor (even when the using in the super call itself).
Isn't the solution the pattern we've already seen?
class Deferred extends Promise {
constructor(factory) {
const methods = {};
super((resolve, reject) => Object.assign(methods, { resolve, reject }));
Object.assign(this, methods);
factory(this.resolve, this.reject);
}
}
On Fri, Jul 20, 2018 at 12:15 PM Augusto Moura <augusto.borgesm at gmail.com>
wrote:
> 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).
>
Isn't the solution the pattern we've already seen?
```js
class Deferred extends Promise {
constructor(factory) {
const methods = {};
super((resolve, reject) => Object.assign(methods, { resolve, reject }));
Object.assign(this, methods);
factory(this.resolve, this.reject);
}
}
```
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180720/89f0c204/attachment.html>You are right, I didn't know you can use variables before calling the super constructor (a Java thing). So yeah, it's pretty easy to extend a Promise to externalize resolve and reject
PS: Sorry about my last email last paragraph grammar, I'm yet getting used to write long texts in English
You are right, I didn't know you can use variables before calling the super
constructor (a Java thing).
So yeah, it's pretty easy to extend a Promise to externalize resolve and
reject
---
PS: Sorry about my last email last paragraph grammar, I'm yet getting used
to write long texts in English
Em sex, 20 de jul de 2018 às 15:57, Richard Gibson <richard.gibson at gmail.com>
escreveu:
> On Fri, Jul 20, 2018 at 12:15 PM Augusto Moura <augusto.borgesm at gmail.com>
> wrote:
>
>> 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).
>>
>
> Isn't the solution the pattern we've already seen?
> ```js
> class Deferred extends Promise {
> constructor(factory) {
> const methods = {};
> super((resolve, reject) => Object.assign(methods, { resolve, reject })
> );
> Object.assign(this, methods);
> factory(this.resolve, this.reject);
> }
> }
> ```
>
--
Augusto Moura
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180720/cdf4a25d/attachment-0001.html>Just a heads up: that will throw, since the Promise constructor
executes its callback synchronously, and thus this isn't set up in
the callback until after it's called. You should probably rewrite it
the callback to store them locally and then set the properties and
call the factory after:
let resolve, reject
super((res, rej) => { resolve = res; reject = rej })
this.resolve = resolve
this.reject = reject
try {
if (factory != null) factory(resolve, reject)
} catch (e) {
reject(e)
}
It's subtle, but it does make a difference.
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
Just a heads up: that will throw, since the Promise constructor
executes its callback synchronously, and thus `this` isn't set up in
the callback until *after* it's called. You should probably rewrite it
the callback to store them locally and then set the properties and
call the factory after:
```js
let resolve, reject
super((res, rej) => { resolve = res; reject = rej })
this.resolve = resolve
this.reject = reject
try {
if (factory != null) factory(resolve, reject)
} catch (e) {
reject(e)
}
```
It's subtle, but it does make a difference.
-----
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 MouraI 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:
// 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
> 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 MouraThe 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.
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 <isiahmeadows 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.
>
> 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180721/89a601f6/attachment-0001.html>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.
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>Fair enough. I was just hoping that the internal mechanism itself could be exposed, since the spec already has to use it (as do implementations 99% of the time). In case you're concerned about the potential for abuse, I do have this:
- It's different than what you'd get with
new Promise(...)itself. This alone should push people away from it - you don't see a lot of repeatedmap.set("foo", foo).set("bar", bar)whennew Map([["foo", foo], ["bar", bar]])is sufficient. - The name is relatively long and it sticks out very well - the
factory's name is about twice as long to type than
new Promiseand it clearly doesn't return a Promise directly. - It's not a constructor, but a method. This makes it harder to contort it into the traditional OOP madness, since methods aren't subclassable.
- The name clearly states it's creating a new capability, a new
"controller" of sorts for a promise. This is way more explicit than
defer, which sounds like you're "deferring" something (when you're not), it'd would be much less prone to confusion and misuse. - Instead of the deferred type choosing how the promise is created, the relationship is inverted. And since promise subclassing is a bit annoying already, you're less likely to see the subclassing crap happen around this.
I specifically don't want to make it simple, idiomatic, and easy to use, because 1. it doesn't even properly capture errors that might occur, and 2. it's a relatively unsafe API most won't really need. However, it's easier to integrate into state-driven control flow, and that's why I would like to see it added.
In case you're curious about other legitimate use cases:
- Simplifying returning a promise fulfilled when an event emits next, or rejects if it's beat by an error event - if this is built-in, you could drop most of the memory and computational overhead by just dealing with the capability directly rather than invoking a closure.
- Returning a promise fulfilled when a socket completes, without the overhead of an event emitter.
- Returning a promise fulfilled on next stream flush, rejected if an error occurs - these are usually pooled, so the factory is a bit wasteful with memory.
- Sending a worker thread a message and returning a promise fulfilled with the response, rejected on error. When the message channel contains unrelated messages as well, you can't use normal promises with this.
Also, I created a quick prolyfill for my Promise.newCapability here:
gist.github.com/isiahmeadows/14ddaae1ae2e73df3115259a0aff1804
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
Fair enough. I was just hoping that the internal mechanism itself
could be exposed, since the spec already has to use it (as do
implementations 99% of the time). In case you're concerned about the
potential for abuse, I do have this:
1. It's different than what you'd get with `new Promise(...)` itself.
This alone should push people away from it - you don't see a lot of
repeated `map.set("foo", foo).set("bar", bar)` when `new Map([["foo",
foo], ["bar", bar]])` is sufficient.
2. The name is relatively long and it sticks out very well - the
factory's name is about twice as long to type than `new Promise` and
it clearly doesn't return a Promise directly.
3. It's not a constructor, but a method. This makes it harder to
contort it into the traditional OOP madness, since methods aren't
subclassable.
4. The name clearly states it's creating a new capability, a new
"controller" of sorts for a promise. This is way more explicit than
`defer`, which sounds like you're "deferring" something (when you're
not), it'd would be much less prone to confusion and misuse.
5. Instead of the deferred type choosing how the promise is created,
the relationship is inverted. And since promise subclassing is a bit
annoying already, you're less likely to see the subclassing crap
happen around this.
I specifically *don't* want to make it simple, idiomatic, and easy to
use, because 1. it doesn't even properly capture errors that might
occur, and 2. it's a relatively unsafe API most won't really need.
However, it's easier to integrate into state-driven control flow, and
that's why I would like to see it added.
In case you're curious about other legitimate use cases:
- Simplifying returning a promise fulfilled when an event emits next,
or rejects if it's beat by an error event - if this is built-in, you
could drop most of the memory and computational overhead by just
dealing with the capability directly rather than invoking a closure.
- Returning a promise fulfilled when a socket completes, without the
overhead of an event emitter.
- Returning a promise fulfilled on next stream flush, rejected if an
error occurs - these are usually pooled, so the factory is a bit
wasteful with memory.
- Sending a worker thread a message and returning a promise fulfilled
with the response, rejected on error. When the message channel
contains unrelated messages as well, you can't use normal promises
with this.
Also, I created a quick prolyfill for my `Promise.newCapability` here:
https://gist.github.com/isiahmeadows/14ddaae1ae2e73df3115259a0aff1804
-----
Isiah Meadows
me at isiahmeadows.com
www.isiahmeadows.com
On Sat, Jul 21, 2018 at 4:38 PM, Jordan Harband <ljharb at gmail.com> wrote:
> 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 <isiahmeadows 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.
>>
>> 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
>
>Isiah Meadows wrote on 20. 7. 2018 3:13:
Sometimes, it's very convenient to have those
resolve/rejectfunctions 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:
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.
Don't understand "revealing constructors". Can be done is userland in a few lines. lolg.it/herby/deferred
Isiah Meadows wrote on 20. 7. 2018 3:13:
> 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.
Don't understand "revealing constructors". Can be done is userland in a
few lines. https://lolg.it/herby/deferred-lite
> [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
>Here's a quick overview of the "revealing constructor" pattern, if it helps: blog.domenic.me/the-revealing-constructor-pattern
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
Here's a quick overview of the "revealing constructor" pattern, if it
helps: https://blog.domenic.me/the-revealing-constructor-pattern/
-----
Isiah Meadows
me at isiahmeadows.com
www.isiahmeadows.com
On Wed, Jul 25, 2018 at 7:05 AM, Herbert Vojčík <herby at mailbox.sk> wrote:
>
>
> Isiah Meadows wrote on 20. 7. 2018 3:13:
>>
>> 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.
>
>
> Don't understand "revealing constructors". Can be done is userland in a few
> lines. https://lolg.it/herby/deferred-lite
>
>> [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
>>
>Yes, I've encountered this "revealing constructor" terminology and find it confusing. I hope it doesn't catch on. A lot of people are likely to try to associate it with the "revealing module" pattern, with which it actually has nothing in common. It's a strange term because this pattern, if one wants to characterize it in terms of "revealing" things, is more about what is not being revealed (to the outside world), not what is being revealed.
It's a useful pattern seen also in the observable constructor, and probably deserves to have a good name, but I can't come up with anything myself, other than maybe the suboptimal "callback-based constructor".
Bob
Yes, I've encountered this "revealing constructor" terminology and find it
confusing. I hope it doesn't catch on.
A lot of people are likely to try to associate it with the "revealing
module" pattern, with which it actually has nothing in common.
It's a strange term because this pattern, if one wants to characterize it
in terms of "revealing" things,
is more about what is **not** being revealed (to the outside world), not
what is being revealed.
It's a useful pattern seen also in the observable constructor, and probably
deserves to have a good name,
but I can't come up with anything myself, other than maybe the suboptimal
"callback-based constructor".
Bob
On Wed, Jul 25, 2018 at 6:45 PM Isiah Meadows <isiahmeadows at gmail.com>
wrote:
> Here's a quick overview of the "revealing constructor" pattern, if it
> helps: https://blog.domenic.me/the-revealing-constructor-pattern/
> -----
>
> Isiah Meadows
> me at isiahmeadows.com
> www.isiahmeadows.com
>
>
> On Wed, Jul 25, 2018 at 7:05 AM, Herbert Vojčík <herby at mailbox.sk> wrote:
> >
> >
> > Isiah Meadows wrote on 20. 7. 2018 3:13:
> >>
> >> 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.
> >
> >
> > Don't understand "revealing constructors". Can be done is userland in a
> few
> > lines. https://lolg.it/herby/deferred-lite
> >
> >> [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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180725/1c7d28f3/attachment.html>It's already caught on - "revealing constructor pattern" is the pattern that describes the Promise executor.
The "revealing module" pattern is obsolete anyways, but it functions on the same principle - using closures to reveal only explicit things instead of everything.
It's already caught on - "revealing constructor pattern" is the pattern
that describes the Promise executor.
The "revealing module" pattern is obsolete anyways, but it functions on the
same principle - using closures to reveal only explicit things instead of
everything.
On Wed, Jul 25, 2018 at 10:01 AM, Bob Myers <rtm at gol.com> wrote:
> Yes, I've encountered this "revealing constructor" terminology and find it
> confusing. I hope it doesn't catch on.
> A lot of people are likely to try to associate it with the "revealing
> module" pattern, with which it actually has nothing in common.
> It's a strange term because this pattern, if one wants to characterize it
> in terms of "revealing" things,
> is more about what is **not** being revealed (to the outside world), not
> what is being revealed.
>
> It's a useful pattern seen also in the observable constructor, and
> probably deserves to have a good name,
> but I can't come up with anything myself, other than maybe the suboptimal
> "callback-based constructor".
>
> Bob
>
> On Wed, Jul 25, 2018 at 6:45 PM Isiah Meadows <isiahmeadows at gmail.com>
> wrote:
>
>> Here's a quick overview of the "revealing constructor" pattern, if it
>> helps: https://blog.domenic.me/the-revealing-constructor-pattern/
>> -----
>>
>> Isiah Meadows
>> me at isiahmeadows.com
>> www.isiahmeadows.com
>>
>>
>> On Wed, Jul 25, 2018 at 7:05 AM, Herbert Vojčík <herby at mailbox.sk> wrote:
>> >
>> >
>> > Isiah Meadows wrote on 20. 7. 2018 3:13:
>> >>
>> >> 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.
>> >
>> >
>> > Don't understand "revealing constructors". Can be done is userland in a
>> few
>> > lines. https://lolg.it/herby/deferred-lite
>> >
>> >> [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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180725/5d39c914/attachment-0001.html>Not to beat a dead horse but
- No, it hasn't caught on, as evidenced by the recent poster who had never heard of it.
- Yes, I know it's used to describe the Promise executor pattern.
- "Revealing module pattern" reveals. The so-called "revealing constructor pattern" does not reveal anything. It hides. So the term is semantically incorrect from the beginning.
Bob
Not to beat a dead horse but
* No, it hasn't caught on, as evidenced by the recent poster who had never
heard of it.
* Yes, I know it's used to describe the Promise executor pattern.
* "Revealing module pattern" reveals. The so-called "revealing constructor
pattern" does not reveal anything. It hides. So the term is semantically
incorrect from the beginning.
Bob
On Wed, Jul 25, 2018 at 10:55 PM Jordan Harband <ljharb at gmail.com> wrote:
> It's already caught on - "revealing constructor pattern" is the pattern
> that describes the Promise executor.
>
> The "revealing module" pattern is obsolete anyways, but it functions on
> the same principle - using closures to reveal only explicit things instead
> of everything.
>
> On Wed, Jul 25, 2018 at 10:01 AM, Bob Myers <rtm at gol.com> wrote:
>
>> Yes, I've encountered this "revealing constructor" terminology and find
>> it confusing. I hope it doesn't catch on.
>> A lot of people are likely to try to associate it with the "revealing
>> module" pattern, with which it actually has nothing in common.
>> It's a strange term because this pattern, if one wants to characterize it
>> in terms of "revealing" things,
>> is more about what is **not** being revealed (to the outside world), not
>> what is being revealed.
>>
>> It's a useful pattern seen also in the observable constructor, and
>> probably deserves to have a good name,
>> but I can't come up with anything myself, other than maybe the suboptimal
>> "callback-based constructor".
>>
>> Bob
>>
>> On Wed, Jul 25, 2018 at 6:45 PM Isiah Meadows <isiahmeadows at gmail.com>
>> wrote:
>>
>>> Here's a quick overview of the "revealing constructor" pattern, if it
>>> helps: https://blog.domenic.me/the-revealing-constructor-pattern/
>>> -----
>>>
>>> Isiah Meadows
>>> me at isiahmeadows.com
>>> www.isiahmeadows.com
>>>
>>>
>>> On Wed, Jul 25, 2018 at 7:05 AM, Herbert Vojčík <herby at mailbox.sk>
>>> wrote:
>>> >
>>> >
>>> > Isiah Meadows wrote on 20. 7. 2018 3:13:
>>> >>
>>> >> 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.
>>> >
>>> >
>>> > Don't understand "revealing constructors". Can be done is userland in
>>> a few
>>> > lines. https://lolg.it/herby/deferred-lite
>>> >
>>> >> [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
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180726/6eaa2fb5/attachment.html>It allows you to reveal things, selectively, just as the revealing module pattern does.
(separately, the measure of "caught on" is not "there does not exist someone who has not heard of it")
It allows you to reveal things, selectively, just as the revealing module
pattern does.
(separately, the measure of "caught on" is not "there does not exist
someone who has not heard of it")
On Wed, Jul 25, 2018 at 2:04 PM, Bob Myers <rtm at gol.com> wrote:
> Not to beat a dead horse but
>
> * No, it hasn't caught on, as evidenced by the recent poster who had never
> heard of it.
> * Yes, I know it's used to describe the Promise executor pattern.
> * "Revealing module pattern" reveals. The so-called "revealing constructor
> pattern" does not reveal anything. It hides. So the term is semantically
> incorrect from the beginning.
>
> Bob
>
> On Wed, Jul 25, 2018 at 10:55 PM Jordan Harband <ljharb at gmail.com> wrote:
>
>> It's already caught on - "revealing constructor pattern" is the pattern
>> that describes the Promise executor.
>>
>> The "revealing module" pattern is obsolete anyways, but it functions on
>> the same principle - using closures to reveal only explicit things instead
>> of everything.
>>
>> On Wed, Jul 25, 2018 at 10:01 AM, Bob Myers <rtm at gol.com> wrote:
>>
>>> Yes, I've encountered this "revealing constructor" terminology and find
>>> it confusing. I hope it doesn't catch on.
>>> A lot of people are likely to try to associate it with the "revealing
>>> module" pattern, with which it actually has nothing in common.
>>> It's a strange term because this pattern, if one wants to characterize
>>> it in terms of "revealing" things,
>>> is more about what is **not** being revealed (to the outside world), not
>>> what is being revealed.
>>>
>>> It's a useful pattern seen also in the observable constructor, and
>>> probably deserves to have a good name,
>>> but I can't come up with anything myself, other than maybe the
>>> suboptimal "callback-based constructor".
>>>
>>> Bob
>>>
>>> On Wed, Jul 25, 2018 at 6:45 PM Isiah Meadows <isiahmeadows at gmail.com>
>>> wrote:
>>>
>>>> Here's a quick overview of the "revealing constructor" pattern, if it
>>>> helps: https://blog.domenic.me/the-revealing-constructor-pattern/
>>>> -----
>>>>
>>>> Isiah Meadows
>>>> me at isiahmeadows.com
>>>> www.isiahmeadows.com
>>>>
>>>>
>>>> On Wed, Jul 25, 2018 at 7:05 AM, Herbert Vojčík <herby at mailbox.sk>
>>>> wrote:
>>>> >
>>>> >
>>>> > Isiah Meadows wrote on 20. 7. 2018 3:13:
>>>> >>
>>>> >> 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.
>>>> >
>>>> >
>>>> > Don't understand "revealing constructors". Can be done is userland in
>>>> a few
>>>> > lines. https://lolg.it/herby/deferred-lite
>>>> >
>>>> >> [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
>>>
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180725/0d97034a/attachment-0001.html>It allows you to reveal things, selectively, just as the revealing module pattern does.
The revealing module pattern is so named precisely because it reveals (to clients) certain aspects of the "module" in question. Yes, it hides other aspects, but only by virtue of not revealing them. If you want to support the terminology of "revealing constructor pattern", could you please state what is being revealed, exactly, to whom? When I call a promise constructor I get back one thing and one thing only, which is a promise. Is the notion that it is the promise which is being revealed? In that sense, all APIs "reveal" their results. If is the intent of using this term that the content of the executor is being revealed to the coder when he views the code? All code is revealed when it is viewed. It would seem to me that to qualify for the name "revealing constructor pattern" it ought to be revealing something.
(separately, the measure of "caught on" is not "there does not exist someone who has not heard of it")
This group is not a court of law with evidentiary rules. That someone with a degree of interest in JS to the extent they are a regular on the ML has never heard of it would seem to mean at least that it has not caught on widely. I myself had worked with and read about ES6 promises extensively before I heard the term. Perhaps in the future I can write things like "It might be argued by some that there is some possibility that the evidence could conceivably be interpreted as indicating that it may be the case that it has not caught on widely"?
In any case, "catching on", however you define it, would seem to require more than a grand total of two Google results for "revealing constructor pattern", one of which is by the person that invented it.
Bob
> It allows you to reveal things, selectively, just as the revealing module
pattern does. \
The revealing module pattern is so named precisely because it reveals (to
clients) certain aspects of the "module" in question.
Yes, it hides other aspects, but only by virtue of not revealing them.
If you want to support the terminology of "revealing constructor pattern",
could you please state *what* is being revealed, exactly, to *whom*?
When I call a promise constructor I get back one thing and one thing only,
which is a promise.
Is the notion that it is the promise which is being revealed?
In that sense, all APIs "reveal" their results.
If is the intent of using this term that the content of the executor is
being revealed to the coder when he views the code?
All code is revealed when it is viewed.
It would seem to me that to qualify for the name "revealing constructor
pattern" it ought to be revealing something.
> (separately, the measure of "caught on" is not "there does not exist
someone who has not heard of it")
This group is not a court of law with evidentiary rules.
That someone with a degree of interest in JS to the extent they are a
regular on the ML has never heard of it would seem to mean at least that it
has not caught on *widely*.
I myself had worked with and read about ES6 promises extensively before I
heard the term.
Perhaps in the future I can write things like "It might be argued by some
that there is some possibility that the evidence could conceivably be
interpreted as indicating that it may be the case that it has not caught on
widely"?
In any case, "catching on", however you define it, would seem to require
more than a grand total of two Google results for "revealing constructor
pattern", one of which is by the person that invented it.
Bob
On Thu, Jul 26, 2018 at 3:13 AM Jordan Harband <ljharb at gmail.com> wrote:
> It allows you to reveal things, selectively, just as the revealing module
> pattern does.
>
> (separately, the measure of "caught on" is not "there does not exist
> someone who has not heard of it")
>
> On Wed, Jul 25, 2018 at 2:04 PM, Bob Myers <rtm at gol.com> wrote:
>
>> Not to beat a dead horse but
>>
>> * No, it hasn't caught on, as evidenced by the recent poster who had
>> never heard of it.
>> * Yes, I know it's used to describe the Promise executor pattern.
>> * "Revealing module pattern" reveals. The so-called "revealing
>> constructor pattern" does not reveal anything. It hides. So the term is
>> semantically incorrect from the beginning.
>>
>> Bob
>>
>> On Wed, Jul 25, 2018 at 10:55 PM Jordan Harband <ljharb at gmail.com> wrote:
>>
>>> It's already caught on - "revealing constructor pattern" is the pattern
>>> that describes the Promise executor.
>>>
>>> The "revealing module" pattern is obsolete anyways, but it functions on
>>> the same principle - using closures to reveal only explicit things instead
>>> of everything.
>>>
>>> On Wed, Jul 25, 2018 at 10:01 AM, Bob Myers <rtm at gol.com> wrote:
>>>
>>>> Yes, I've encountered this "revealing constructor" terminology and find
>>>> it confusing. I hope it doesn't catch on.
>>>> A lot of people are likely to try to associate it with the "revealing
>>>> module" pattern, with which it actually has nothing in common.
>>>> It's a strange term because this pattern, if one wants to characterize
>>>> it in terms of "revealing" things,
>>>> is more about what is **not** being revealed (to the outside world),
>>>> not what is being revealed.
>>>>
>>>> It's a useful pattern seen also in the observable constructor, and
>>>> probably deserves to have a good name,
>>>> but I can't come up with anything myself, other than maybe the
>>>> suboptimal "callback-based constructor".
>>>>
>>>> Bob
>>>>
>>>> On Wed, Jul 25, 2018 at 6:45 PM Isiah Meadows <isiahmeadows at gmail.com>
>>>> wrote:
>>>>
>>>>> Here's a quick overview of the "revealing constructor" pattern, if it
>>>>> helps: https://blog.domenic.me/the-revealing-constructor-pattern/
>>>>> -----
>>>>>
>>>>> Isiah Meadows
>>>>> me at isiahmeadows.com
>>>>> www.isiahmeadows.com
>>>>>
>>>>>
>>>>> On Wed, Jul 25, 2018 at 7:05 AM, Herbert Vojčík <herby at mailbox.sk>
>>>>> wrote:
>>>>> >
>>>>> >
>>>>> > Isiah Meadows wrote on 20. 7. 2018 3:13:
>>>>> >>
>>>>> >> 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.
>>>>> >
>>>>> >
>>>>> > Don't understand "revealing constructors". Can be done is userland
>>>>> in a few
>>>>> > lines. https://lolg.it/herby/deferred-lite
>>>>> >
>>>>> >> [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
>>>>
>>>>
>>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180726/8a54305c/attachment.html>FWIW I have never heard of this terminology before either but in practice have used the pattern myself.
FWIW I have never heard of this terminology before either but in practice
have used the pattern myself.
On Thu, Jul 26, 2018 at 12:15 AM, Bob Myers <rtm at gol.com> wrote:
> > It allows you to reveal things, selectively, just as the revealing
> module pattern does. \
>
> The revealing module pattern is so named precisely because it reveals (to
> clients) certain aspects of the "module" in question.
> Yes, it hides other aspects, but only by virtue of not revealing them.
> If you want to support the terminology of "revealing constructor pattern",
> could you please state *what* is being revealed, exactly, to *whom*?
> When I call a promise constructor I get back one thing and one thing only,
> which is a promise.
> Is the notion that it is the promise which is being revealed?
> In that sense, all APIs "reveal" their results.
> If is the intent of using this term that the content of the executor is
> being revealed to the coder when he views the code?
> All code is revealed when it is viewed.
> It would seem to me that to qualify for the name "revealing constructor
> pattern" it ought to be revealing something.
>
> > (separately, the measure of "caught on" is not "there does not exist
> someone who has not heard of it")
>
> This group is not a court of law with evidentiary rules.
> That someone with a degree of interest in JS to the extent they are a
> regular on the ML has never heard of it would seem to mean at least that it
> has not caught on *widely*.
> I myself had worked with and read about ES6 promises extensively before I
> heard the term.
> Perhaps in the future I can write things like "It might be argued by some
> that there is some possibility that the evidence could conceivably be
> interpreted as indicating that it may be the case that it has not caught on
> widely"?
>
> In any case, "catching on", however you define it, would seem to require
> more than a grand total of two Google results for "revealing constructor
> pattern", one of which is by the person that invented it.
>
> Bob
>
> On Thu, Jul 26, 2018 at 3:13 AM Jordan Harband <ljharb at gmail.com> wrote:
>
>> It allows you to reveal things, selectively, just as the revealing module
>> pattern does.
>>
>> (separately, the measure of "caught on" is not "there does not exist
>> someone who has not heard of it")
>>
>> On Wed, Jul 25, 2018 at 2:04 PM, Bob Myers <rtm at gol.com> wrote:
>>
>>> Not to beat a dead horse but
>>>
>>> * No, it hasn't caught on, as evidenced by the recent poster who had
>>> never heard of it.
>>> * Yes, I know it's used to describe the Promise executor pattern.
>>> * "Revealing module pattern" reveals. The so-called "revealing
>>> constructor pattern" does not reveal anything. It hides. So the term is
>>> semantically incorrect from the beginning.
>>>
>>> Bob
>>>
>>> On Wed, Jul 25, 2018 at 10:55 PM Jordan Harband <ljharb at gmail.com>
>>> wrote:
>>>
>>>> It's already caught on - "revealing constructor pattern" is the pattern
>>>> that describes the Promise executor.
>>>>
>>>> The "revealing module" pattern is obsolete anyways, but it functions on
>>>> the same principle - using closures to reveal only explicit things instead
>>>> of everything.
>>>>
>>>> On Wed, Jul 25, 2018 at 10:01 AM, Bob Myers <rtm at gol.com> wrote:
>>>>
>>>>> Yes, I've encountered this "revealing constructor" terminology and
>>>>> find it confusing. I hope it doesn't catch on.
>>>>> A lot of people are likely to try to associate it with the "revealing
>>>>> module" pattern, with which it actually has nothing in common.
>>>>> It's a strange term because this pattern, if one wants to characterize
>>>>> it in terms of "revealing" things,
>>>>> is more about what is **not** being revealed (to the outside world),
>>>>> not what is being revealed.
>>>>>
>>>>> It's a useful pattern seen also in the observable constructor, and
>>>>> probably deserves to have a good name,
>>>>> but I can't come up with anything myself, other than maybe the
>>>>> suboptimal "callback-based constructor".
>>>>>
>>>>> Bob
>>>>>
>>>>> On Wed, Jul 25, 2018 at 6:45 PM Isiah Meadows <isiahmeadows at gmail.com>
>>>>> wrote:
>>>>>
>>>>>> Here's a quick overview of the "revealing constructor" pattern, if it
>>>>>> helps: https://blog.domenic.me/the-revealing-constructor-pattern/
>>>>>> -----
>>>>>>
>>>>>> Isiah Meadows
>>>>>> me at isiahmeadows.com
>>>>>> www.isiahmeadows.com
>>>>>>
>>>>>>
>>>>>> On Wed, Jul 25, 2018 at 7:05 AM, Herbert Vojčík <herby at mailbox.sk>
>>>>>> wrote:
>>>>>> >
>>>>>> >
>>>>>> > Isiah Meadows wrote on 20. 7. 2018 3:13:
>>>>>> >>
>>>>>> >> 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.
>>>>>> >
>>>>>> >
>>>>>> > Don't understand "revealing constructors". Can be done is userland
>>>>>> in a few
>>>>>> > lines. https://lolg.it/herby/deferred-lite
>>>>>> >
>>>>>> >> [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
>>>>>
>>>>>
>>>>
>>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180726/6c631062/attachment-0001.html>
Sometimes, it's very convenient to have those
resolve/rejectfunctions 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:
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, 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.
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
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